diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a8df94cc6..936f5f2615 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,14 +1,17 @@ version: 2 jobs: test-node10-0: - working_directory: ~/ark-core + working_directory: ~/core + environment: + CORE_DB_DATABASE: core_development + CORE_DB_USERNAME: core docker: - image: 'circleci/node:10-browsers' - image: 'postgres:alpine' environment: POSTGRES_PASSWORD: password - POSTGRES_DB: ark_development - POSTGRES_USER: ark + POSTGRES_DB: core_development + POSTGRES_USER: core steps: - checkout - run: @@ -17,8 +20,8 @@ jobs: sudo sh -c 'echo "deb http://ftp.debian.org/debian stable main contrib non-free" >> /etc/apt/sources.list' && sudo apt-get update - run: - name: Install xsel - command: sudo apt-get install -q xsel + name: Install xsel & postgresql-client + command: sudo apt-get install -q xsel postgresql-client - run: name: Generate cache key command: >- @@ -27,20 +30,18 @@ jobs: - restore_cache: key: 'core-node10-{{ checksum "checksum.txt" }}-1' - run: - name: Install packages - command: yarn + name: Install and build packages + command: yarn setup - save_cache: key: 'core-node10-{{ checksum "checksum.txt" }}-1' paths: - ./packages/core/node_modules - ./packages/core-api/node_modules - ./packages/core-blockchain/node_modules - - ./packages/core-config/node_modules - ./packages/core-container/node_modules - ./packages/core-database/node_modules - ./packages/core-database-postgres/node_modules - ./packages/core-debugger-cli/node_modules - - ./packages/core-deployer/node_modules - ./packages/core-elasticsearch/node_modules - ./packages/core-error-tracker-bugsnag/node_modules - ./packages/core-error-tracker-sentry/node_modules @@ -59,26 +60,159 @@ jobs: - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules - - ./packages/core-transaction-pool-mem/node_modules - ./packages/core-utils/node_modules - ./packages/core-vote-report/node_modules - ./packages/core-webhooks/node_modules - ./packages/crypto/node_modules - ./node_modules - run: - name: Create .ark/database directory - command: mkdir -p $HOME/.ark/database + name: Create .core/database directory + command: mkdir -p $HOME/.core/database - run: - name: Test - command: > - ./node_modules/.bin/cross-env ARK_ENV=test ./node_modules/.bin/jest - ./packages/core-webhooks/ ./packages/core-transaction-pool-mem/ - ./packages/core-test-utils/ ./packages/core-p2p/ - ./packages/core-json-rpc/ ./packages/core-http-utils/ - ./packages/core-event-emitter/ ./packages/core-elasticsearch/ - ./packages/core-database-postgres/ ./packages/core-config/ - ./packages/core/ --detectOpenHandles --runInBand --forceExit --ci - --coverage | tee test_output.txt + name: core + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core && + yarn test:coverage + - run: + name: core-api + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-api && yarn test:coverage + - run: + name: core-blockchain + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-blockchain && yarn test:coverage + - run: + name: core-container + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-container && yarn test:coverage + - run: + name: core-database + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-database && yarn test:coverage + - run: + name: core-database-postgres + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-database-postgres && yarn test:coverage + - run: + name: core-debugger-cli + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-debugger-cli && yarn test:coverage + - run: + name: Last 1000 lines of test output + when: on_fail + command: tail -n 1000 test_output.txt + - run: + name: Codecov + command: ./node_modules/.bin/codecov + test-node11-0: + working_directory: ~/core + environment: + CORE_DB_DATABASE: core_development + CORE_DB_USERNAME: core + docker: + - image: 'circleci/node:11-browsers' + - image: 'postgres:alpine' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_development + POSTGRES_USER: core + steps: + - checkout + - run: + name: Apt update + command: >- + sudo sh -c 'echo "deb http://ftp.debian.org/debian stable main + contrib non-free" >> /etc/apt/sources.list' && sudo apt-get update + - run: + name: Install xsel & postgresql-client + command: sudo apt-get install -q xsel postgresql-client + - run: + name: Generate cache key + command: >- + find ./packages/ -name package.json -print0 | sort -z | xargs -r0 + echo ./package.json | xargs md5sum | md5sum - > checksum.txt + - restore_cache: + key: 'core-node11-{{ checksum "checksum.txt" }}-1' + - run: + name: Install and build packages + command: yarn setup + - save_cache: + key: 'core-node11-{{ checksum "checksum.txt" }}-1' + paths: + - ./packages/core/node_modules + - ./packages/core-api/node_modules + - ./packages/core-blockchain/node_modules + - ./packages/core-container/node_modules + - ./packages/core-database/node_modules + - ./packages/core-database-postgres/node_modules + - ./packages/core-debugger-cli/node_modules + - ./packages/core-elasticsearch/node_modules + - ./packages/core-error-tracker-bugsnag/node_modules + - ./packages/core-error-tracker-sentry/node_modules + - ./packages/core-event-emitter/node_modules + - ./packages/core-forger/node_modules + - ./packages/core-graphql/node_modules + - ./packages/core-http-utils/node_modules + - ./packages/core-interfaces/node_modules + - ./packages/core-jest-matchers/node_modules + - ./packages/core-json-rpc/node_modules + - ./packages/core-logger/node_modules + - ./packages/core-logger-winston/node_modules + - ./packages/core-p2p/node_modules + - ./packages/core-snapshots/node_modules + - ./packages/core-snapshots-cli/node_modules + - ./packages/core-test-utils/node_modules + - ./packages/core-tester-cli/node_modules + - ./packages/core-transaction-pool/node_modules + - ./packages/core-utils/node_modules + - ./packages/core-vote-report/node_modules + - ./packages/core-webhooks/node_modules + - ./packages/crypto/node_modules + - ./node_modules + - run: + name: Create .core/database directory + command: mkdir -p $HOME/.core/database + - run: + name: core + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core && + yarn test:coverage + - run: + name: core-api + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-api && yarn test:coverage + - run: + name: core-blockchain + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-blockchain && yarn test:coverage + - run: + name: core-container + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-container && yarn test:coverage + - run: + name: core-database + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-database && yarn test:coverage + - run: + name: core-database-postgres + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-database-postgres && yarn test:coverage + - run: + name: core-debugger-cli + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-debugger-cli && yarn test:coverage - run: name: Last 1000 lines of test output when: on_fail @@ -87,14 +221,17 @@ jobs: name: Codecov command: ./node_modules/.bin/codecov test-node10-1: - working_directory: ~/ark-core + working_directory: ~/core + environment: + CORE_DB_DATABASE: core_development + CORE_DB_USERNAME: core docker: - image: 'circleci/node:10-browsers' - image: 'postgres:alpine' environment: POSTGRES_PASSWORD: password - POSTGRES_DB: ark_development - POSTGRES_USER: ark + POSTGRES_DB: core_development + POSTGRES_USER: core steps: - checkout - run: @@ -103,8 +240,8 @@ jobs: sudo sh -c 'echo "deb http://ftp.debian.org/debian stable main contrib non-free" >> /etc/apt/sources.list' && sudo apt-get update - run: - name: Install xsel - command: sudo apt-get install -q xsel + name: Install xsel & postgresql-client + command: sudo apt-get install -q xsel postgresql-client - run: name: Generate cache key command: >- @@ -113,20 +250,18 @@ jobs: - restore_cache: key: 'core-node10-{{ checksum "checksum.txt" }}-1' - run: - name: Install packages - command: yarn + name: Install and build packages + command: yarn setup - save_cache: key: 'core-node10-{{ checksum "checksum.txt" }}-1' paths: - ./packages/core/node_modules - ./packages/core-api/node_modules - ./packages/core-blockchain/node_modules - - ./packages/core-config/node_modules - ./packages/core-container/node_modules - ./packages/core-database/node_modules - ./packages/core-database-postgres/node_modules - ./packages/core-debugger-cli/node_modules - - ./packages/core-deployer/node_modules - ./packages/core-elasticsearch/node_modules - ./packages/core-error-tracker-bugsnag/node_modules - ./packages/core-error-tracker-sentry/node_modules @@ -145,26 +280,59 @@ jobs: - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules - - ./packages/core-transaction-pool-mem/node_modules - ./packages/core-utils/node_modules - ./packages/core-vote-report/node_modules - ./packages/core-webhooks/node_modules - ./packages/crypto/node_modules - ./node_modules - run: - name: Create .ark/database directory - command: mkdir -p $HOME/.ark/database + name: Create .core/database directory + command: mkdir -p $HOME/.core/database - run: - name: Test - command: > - ./node_modules/.bin/cross-env ARK_ENV=test ./node_modules/.bin/jest - ./packages/crypto/ ./packages/core-utils/ - ./packages/core-tester-cli/ ./packages/core-snapshots/ - ./packages/core-logger/ ./packages/core-interfaces/ - ./packages/core-forger/ ./packages/core-error-tracker-bugsnag/ - ./packages/core-debugger-cli/ ./packages/core-container/ - ./packages/core-api/ --detectOpenHandles --runInBand --forceExit - --ci --coverage | tee test_output.txt + name: core-event-emitter + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-event-emitter && yarn test:coverage + - run: + name: core-forger + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-forger && yarn test:coverage + - run: + name: core-graphql + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-graphql && yarn test:coverage + - run: + name: core-http-utils + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-http-utils && yarn test:coverage + - run: + name: core-jest-matchers + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-jest-matchers && yarn test:coverage + - run: + name: core-json-rpc + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-json-rpc && yarn test:coverage + - run: + name: core-logger + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-logger && yarn test:coverage + - run: + name: core-logger-winston + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-logger-winston && yarn test:coverage + - run: + name: core-p2p + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-p2p && yarn test:coverage - run: name: Last 1000 lines of test output when: on_fail @@ -173,14 +341,17 @@ jobs: name: Codecov command: ./node_modules/.bin/codecov test-node10-2: - working_directory: ~/ark-core + working_directory: ~/core + environment: + CORE_DB_DATABASE: core_development + CORE_DB_USERNAME: core docker: - image: 'circleci/node:10-browsers' - image: 'postgres:alpine' environment: POSTGRES_PASSWORD: password - POSTGRES_DB: ark_development - POSTGRES_USER: ark + POSTGRES_DB: core_development + POSTGRES_USER: core steps: - checkout - run: @@ -189,8 +360,8 @@ jobs: sudo sh -c 'echo "deb http://ftp.debian.org/debian stable main contrib non-free" >> /etc/apt/sources.list' && sudo apt-get update - run: - name: Install xsel - command: sudo apt-get install -q xsel + name: Install xsel & postgresql-client + command: sudo apt-get install -q xsel postgresql-client - run: name: Generate cache key command: >- @@ -199,20 +370,18 @@ jobs: - restore_cache: key: 'core-node10-{{ checksum "checksum.txt" }}-1' - run: - name: Install packages - command: yarn + name: Install and build packages + command: yarn setup - save_cache: key: 'core-node10-{{ checksum "checksum.txt" }}-1' paths: - ./packages/core/node_modules - ./packages/core-api/node_modules - ./packages/core-blockchain/node_modules - - ./packages/core-config/node_modules - ./packages/core-container/node_modules - ./packages/core-database/node_modules - ./packages/core-database-postgres/node_modules - ./packages/core-debugger-cli/node_modules - - ./packages/core-deployer/node_modules - ./packages/core-elasticsearch/node_modules - ./packages/core-error-tracker-bugsnag/node_modules - ./packages/core-error-tracker-sentry/node_modules @@ -231,26 +400,289 @@ jobs: - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules - - ./packages/core-transaction-pool-mem/node_modules - ./packages/core-utils/node_modules - ./packages/core-vote-report/node_modules - ./packages/core-webhooks/node_modules - ./packages/crypto/node_modules - ./node_modules - run: - name: Create .ark/database directory - command: mkdir -p $HOME/.ark/database + name: Create .core/database directory + command: mkdir -p $HOME/.core/database + - run: + name: core-snapshots + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-snapshots && yarn test:coverage + - run: + name: core-test-utils + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-test-utils && yarn test:coverage + - run: + name: core-tester-cli + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-tester-cli && yarn test:coverage + - run: + name: core-transaction-pool + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-transaction-pool && yarn test:coverage + - run: + name: core-utils + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-utils && yarn test:coverage + - run: + name: core-vote-report + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-vote-report && yarn test:coverage + - run: + name: core-webhooks + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-webhooks && yarn test:coverage + - run: + name: crypto + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/crypto + && yarn test:coverage + - run: + name: Last 1000 lines of test output + when: on_fail + command: tail -n 1000 test_output.txt + - run: + name: Codecov + command: ./node_modules/.bin/codecov + test-node11-1: + working_directory: ~/core + environment: + CORE_DB_DATABASE: core_development + CORE_DB_USERNAME: core + docker: + - image: 'circleci/node:11-browsers' + - image: 'postgres:alpine' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_development + POSTGRES_USER: core + steps: + - checkout + - run: + name: Apt update + command: >- + sudo sh -c 'echo "deb http://ftp.debian.org/debian stable main + contrib non-free" >> /etc/apt/sources.list' && sudo apt-get update + - run: + name: Install xsel & postgresql-client + command: sudo apt-get install -q xsel postgresql-client + - run: + name: Generate cache key + command: >- + find ./packages/ -name package.json -print0 | sort -z | xargs -r0 + echo ./package.json | xargs md5sum | md5sum - > checksum.txt + - restore_cache: + key: 'core-node11-{{ checksum "checksum.txt" }}-1' + - run: + name: Install and build packages + command: yarn setup + - save_cache: + key: 'core-node11-{{ checksum "checksum.txt" }}-1' + paths: + - ./packages/core/node_modules + - ./packages/core-api/node_modules + - ./packages/core-blockchain/node_modules + - ./packages/core-container/node_modules + - ./packages/core-database/node_modules + - ./packages/core-database-postgres/node_modules + - ./packages/core-debugger-cli/node_modules + - ./packages/core-elasticsearch/node_modules + - ./packages/core-error-tracker-bugsnag/node_modules + - ./packages/core-error-tracker-sentry/node_modules + - ./packages/core-event-emitter/node_modules + - ./packages/core-forger/node_modules + - ./packages/core-graphql/node_modules + - ./packages/core-http-utils/node_modules + - ./packages/core-interfaces/node_modules + - ./packages/core-jest-matchers/node_modules + - ./packages/core-json-rpc/node_modules + - ./packages/core-logger/node_modules + - ./packages/core-logger-winston/node_modules + - ./packages/core-p2p/node_modules + - ./packages/core-snapshots/node_modules + - ./packages/core-snapshots-cli/node_modules + - ./packages/core-test-utils/node_modules + - ./packages/core-tester-cli/node_modules + - ./packages/core-transaction-pool/node_modules + - ./packages/core-utils/node_modules + - ./packages/core-vote-report/node_modules + - ./packages/core-webhooks/node_modules + - ./packages/crypto/node_modules + - ./node_modules + - run: + name: Create .core/database directory + command: mkdir -p $HOME/.core/database + - run: + name: core-event-emitter + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-event-emitter && yarn test:coverage + - run: + name: core-forger + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-forger && yarn test:coverage + - run: + name: core-graphql + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-graphql && yarn test:coverage + - run: + name: core-http-utils + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-http-utils && yarn test:coverage + - run: + name: core-jest-matchers + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-jest-matchers && yarn test:coverage - run: - name: Test - command: > - ./node_modules/.bin/cross-env ARK_ENV=test ./node_modules/.bin/jest - ./packages/core-vote-report/ ./packages/core-transaction-pool/ - ./packages/core-snapshots-cli/ ./packages/core-logger-winston/ - ./packages/core-jest-matchers/ ./packages/core-graphql/ - ./packages/core-error-tracker-sentry/ ./packages/core-deployer/ - ./packages/core-database/ ./packages/core-blockchain/ - --detectOpenHandles --runInBand --forceExit --ci --coverage | tee - test_output.txt + name: core-json-rpc + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-json-rpc && yarn test:coverage + - run: + name: core-logger + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-logger && yarn test:coverage + - run: + name: core-logger-winston + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-logger-winston && yarn test:coverage + - run: + name: core-p2p + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-p2p && yarn test:coverage + - run: + name: Last 1000 lines of test output + when: on_fail + command: tail -n 1000 test_output.txt + - run: + name: Codecov + command: ./node_modules/.bin/codecov + test-node11-2: + working_directory: ~/core + environment: + CORE_DB_DATABASE: core_development + CORE_DB_USERNAME: core + docker: + - image: 'circleci/node:11-browsers' + - image: 'postgres:alpine' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_development + POSTGRES_USER: core + steps: + - checkout + - run: + name: Apt update + command: >- + sudo sh -c 'echo "deb http://ftp.debian.org/debian stable main + contrib non-free" >> /etc/apt/sources.list' && sudo apt-get update + - run: + name: Install xsel & postgresql-client + command: sudo apt-get install -q xsel postgresql-client + - run: + name: Generate cache key + command: >- + find ./packages/ -name package.json -print0 | sort -z | xargs -r0 + echo ./package.json | xargs md5sum | md5sum - > checksum.txt + - restore_cache: + key: 'core-node11-{{ checksum "checksum.txt" }}-1' + - run: + name: Install and build packages + command: yarn setup + - save_cache: + key: 'core-node11-{{ checksum "checksum.txt" }}-1' + paths: + - ./packages/core/node_modules + - ./packages/core-api/node_modules + - ./packages/core-blockchain/node_modules + - ./packages/core-container/node_modules + - ./packages/core-database/node_modules + - ./packages/core-database-postgres/node_modules + - ./packages/core-debugger-cli/node_modules + - ./packages/core-elasticsearch/node_modules + - ./packages/core-error-tracker-bugsnag/node_modules + - ./packages/core-error-tracker-sentry/node_modules + - ./packages/core-event-emitter/node_modules + - ./packages/core-forger/node_modules + - ./packages/core-graphql/node_modules + - ./packages/core-http-utils/node_modules + - ./packages/core-interfaces/node_modules + - ./packages/core-jest-matchers/node_modules + - ./packages/core-json-rpc/node_modules + - ./packages/core-logger/node_modules + - ./packages/core-logger-winston/node_modules + - ./packages/core-p2p/node_modules + - ./packages/core-snapshots/node_modules + - ./packages/core-snapshots-cli/node_modules + - ./packages/core-test-utils/node_modules + - ./packages/core-tester-cli/node_modules + - ./packages/core-transaction-pool/node_modules + - ./packages/core-utils/node_modules + - ./packages/core-vote-report/node_modules + - ./packages/core-webhooks/node_modules + - ./packages/crypto/node_modules + - ./node_modules + - run: + name: Create .core/database directory + command: mkdir -p $HOME/.core/database + - run: + name: core-snapshots + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-snapshots && yarn test:coverage + - run: + name: core-test-utils + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-test-utils && yarn test:coverage + - run: + name: core-tester-cli + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-tester-cli && yarn test:coverage + - run: + name: core-transaction-pool + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-transaction-pool && yarn test:coverage + - run: + name: core-utils + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-utils && yarn test:coverage + - run: + name: core-vote-report + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-vote-report && yarn test:coverage + - run: + name: core-webhooks + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-webhooks && yarn test:coverage + - run: + name: crypto + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/crypto + && yarn test:coverage - run: name: Last 1000 lines of test output when: on_fail @@ -265,3 +697,6 @@ workflows: - test-node10-0 - test-node10-1 - test-node10-2 + - test-node11-0 + - test-node11-1 + - test-node11-2 diff --git a/.circleci/configTemplate.json b/.circleci/configTemplate.json index cc32aa212d..6d249c90b9 100644 --- a/.circleci/configTemplate.json +++ b/.circleci/configTemplate.json @@ -1,90 +1,177 @@ { - "version": 2, - "jobs": { - "test-node10-0": { - "working_directory": "~/ark-core", - "docker": [ - { - "image": "circleci/node:10-browsers" + "version": 2, + "jobs": { + "test-node10-0": { + "working_directory": "~/core", + "environment": { + "CORE_DB_DATABASE": "core_development", + "CORE_DB_USERNAME": "core" + }, + "docker": [ + { + "image": "circleci/node:10-browsers" + }, + { + "image": "postgres:alpine", + "environment": { + "POSTGRES_PASSWORD": "password", + "POSTGRES_DB": "core_development", + "POSTGRES_USER": "core" + } + } + ], + "steps": [ + "checkout", + { + "run": { + "name": "Apt update", + "command": "sudo sh -c 'echo \"deb http://ftp.debian.org/debian stable main contrib non-free\" >> /etc/apt/sources.list' && sudo apt-get update" + } + }, + { + "run": { + "name": "Install xsel & postgresql-client", + "command": "sudo apt-get install -q xsel postgresql-client" + } + }, + { + "run": { + "name": "Generate cache key", + "command": "find ./packages/ -name package.json -print0 | sort -z | xargs -r0 echo ./package.json | xargs md5sum | md5sum - > checksum.txt" + } + }, + { + "restore_cache": { + "key": "core-node10-{{ checksum \"checksum.txt\" }}-1" + } + }, + { + "run": { + "name": "Install and build packages", + "command": "yarn setup" + } + }, + { + "save_cache": { + "key": "core-node10-{{ checksum \"checksum.txt\" }}-1", + "paths": [] + } + }, + { + "run": { + "name": "Create .core/database directory", + "command": "mkdir -p $HOME/.core/database" + } + }, + { + "run": { + "name": "Test", + "command": "" + } + }, + { + "run": { + "name": "Last 1000 lines of test output", + "when": "on_fail", + "command": "tail -n 1000 test_output.txt" + } + }, + { + "run": { + "name": "Codecov", + "command": "./node_modules/.bin/codecov" + } + } + ] }, - { - "image": "postgres:alpine", - "environment": { - "POSTGRES_PASSWORD": "password", - "POSTGRES_DB": "ark_development", - "POSTGRES_USER": "ark" - } + "test-node11-0": { + "working_directory": "~/core", + "environment": { + "CORE_DB_DATABASE": "core_development", + "CORE_DB_USERNAME": "core" + }, + "docker": [ + { + "image": "circleci/node:11-browsers" + }, + { + "image": "postgres:alpine", + "environment": { + "POSTGRES_PASSWORD": "password", + "POSTGRES_DB": "core_development", + "POSTGRES_USER": "core" + } + } + ], + "steps": [ + "checkout", + { + "run": { + "name": "Apt update", + "command": "sudo sh -c 'echo \"deb http://ftp.debian.org/debian stable main contrib non-free\" >> /etc/apt/sources.list' && sudo apt-get update" + } + }, + { + "run": { + "name": "Install xsel & postgresql-client", + "command": "sudo apt-get install -q xsel postgresql-client" + } + }, + { + "run": { + "name": "Generate cache key", + "command": "find ./packages/ -name package.json -print0 | sort -z | xargs -r0 echo ./package.json | xargs md5sum | md5sum - > checksum.txt" + } + }, + { + "restore_cache": { + "key": "core-node11-{{ checksum \"checksum.txt\" }}-1" + } + }, + { + "run": { + "name": "Install and build packages", + "command": "yarn setup" + } + }, + { + "save_cache": { + "key": "core-node11-{{ checksum \"checksum.txt\" }}-1", + "paths": [] + } + }, + { + "run": { + "name": "Create .core/database directory", + "command": "mkdir -p $HOME/.core/database" + } + }, + { + "run": { + "name": "Test", + "command": "" + } + }, + { + "run": { + "name": "Last 1000 lines of test output", + "when": "on_fail", + "command": "tail -n 1000 test_output.txt" + } + }, + { + "run": { + "name": "Codecov", + "command": "./node_modules/.bin/codecov" + } + } + ] } - ], - "steps": [ - "checkout", - { - "run": { - "name": "Apt update", - "command": "sudo sh -c 'echo \"deb http://ftp.debian.org/debian stable main contrib non-free\" >> /etc/apt/sources.list' && sudo apt-get update" - } - }, - { - "run": { - "name": "Install xsel", - "command": "sudo apt-get install -q xsel" - } - }, - { - "run": { - "name": "Generate cache key", - "command": "find ./packages/ -name package.json -print0 | sort -z | xargs -r0 echo ./package.json | xargs md5sum | md5sum - > checksum.txt" - } - }, - { - "restore_cache": { - "key": "core-node10-{{ checksum \"checksum.txt\" }}-1" - } - }, - { - "run": { - "name": "Install packages", - "command": "yarn" - } - }, - { - "save_cache": { - "key": "core-node10-{{ checksum \"checksum.txt\" }}-1", - "paths": [] - } - }, - { - "run": { - "name": "Create .ark/database directory", - "command": "mkdir -p $HOME/.ark/database" - } - }, - { - "run": { - "name": "Test", - "command": "./node_modules/.bin/cross-env ARK_ENV=test ./node_modules/.bin/jest {{TESTPATHS}} --detectOpenHandles --runInBand --forceExit --ci --coverage | tee test_output.txt\n" - } - }, - { - "run": { - "name": "Last 1000 lines of test output", - "when": "on_fail", - "command": "tail -n 1000 test_output.txt" - } - }, - { - "run": { - "name": "Codecov", - "command": "./node_modules/.bin/codecov" - } + }, + "workflows": { + "version": 2, + "build_and_test": { + "jobs": [] } - ] - } - }, - "workflows": { - "version": 2, - "build_and_test": { - "jobs": [] } - } } diff --git a/.circleci/generateConfig.js b/.circleci/generateConfig.js index b729ba31e8..73cb30527d 100644 --- a/.circleci/generateConfig.js +++ b/.circleci/generateConfig.js @@ -1,90 +1,73 @@ -const yaml = require('js-yaml') -const fs = require('fs') -const path = require('path') +const yaml = require("js-yaml"); +const fs = require("fs"); +const path = require("path"); +const chunk = require("lodash.chunk"); -const config = require('./configTemplate.json') +const config = require("./configTemplate.json"); -generateConfig() - -function generateConfig() { - fs.readdir('./packages', (err, packages) => genYaml({ packages })) +function jason(value) { + return JSON.parse(JSON.stringify(value)); } -function genYaml(options) { - // save cache - const saveCacheStep = config.jobs['test-node10-0'].steps.find( - step => typeof step === 'object' && step.save_cache, - ) - saveCacheStep.save_cache.paths = options.packages - .map(package => `./packages/${package}/node_modules`) - .concat('./node_modules') - - // test split - const packagesSplit = splitPackagesByTestFiles(options.packages, 3) - - const jobs = [ - config.jobs['test-node10-0'], - JSON.parse(JSON.stringify(config.jobs['test-node10-0'])), - JSON.parse(JSON.stringify(config.jobs['test-node10-0'])), - ] - - jobs.forEach((job, index) => { - const testStep = job.steps.find( - step => typeof step === 'object' && step.run && step.run.name === 'Test', - ) - testStep.run.command = testStep.run.command.replace( - '{{TESTPATHS}}', - packagesSplit[index].map(package => `./packages/${package}/`).join(' '), - ) - - config.jobs[`test-node10-${index}`] = job - config.workflows.build_and_test.jobs.push(`test-node10-${index}`) - }) - - fs.writeFile('.circleci/config.yml', yaml.safeDump(config), 'utf8', err => { - if (err) console.error(err) - }) -} +fs.readdir("./packages", (_, packages) => { + // test split + const packagesSplit = chunk(packages.sort(), 10); -function splitPackagesByTestFiles(packages, splitNumber) { - /* distribute test packages by test files count : start by most files package, - and distribute package by package in each _packagesSplit_ (not the most effective - distribution but simple and enough for now) */ - const packagesWithCount = packages.map(package => ({ - package, - count: countFiles(`packages/${package}/__tests__`, '.test.js'), - })) - const packagesSortedByCount = packagesWithCount.sort( - (pkgA, pkgB) => pkgA.count > pkgB.count, - ) - - const packagesSplit = new Array(splitNumber) - packagesSortedByCount.forEach( - (pkg, index) => - (packagesSplit[index % splitNumber] = [pkg.package].concat( - packagesSplit[index % splitNumber] || [], - )), - ) - - return packagesSplit -} + const resetSqlCommand = "cd ~/core/.circleci && ./rebuild-db.sh" + + for (const [name, job] of Object.entries(config.jobs)) { + // save cache + const saveCacheStep = config.jobs[name].steps.find(step => typeof step === "object" && step.save_cache); + saveCacheStep.save_cache.paths = packages + .map(package => `./packages/${package}/node_modules`) + .concat("./node_modules"); + + const jobs = [ + config.jobs[name], + jason(config.jobs[name]), + jason(config.jobs[name]), + ]; + + jobs.forEach((job, index) => { + const testStepIndex = job.steps.findIndex( + step => typeof step === "object" && step.run && step.run.name === "Test", + ); + + const pkgs = packagesSplit[index].map(package => `./packages/${package}/`); -function countFiles(startPath, filter) { - let count = 0 - if (!fs.existsSync(startPath)) { - return - } - - var files = fs.readdirSync(startPath) - for (let i = 0; i < files.length; i++) { - const filename = path.join(startPath, files[i]) - const stat = fs.lstatSync(filename) - if (stat.isDirectory()) { - count += countFiles(filename, filter) - } else if (filename.indexOf(filter) >= 0) { - count++ + const steps = pkgs + .map(pkg => { + const name = path.basename(pkg); + + return { + run: { + name, + command: `${resetSqlCommand} && cd ~/core/packages/${name} && yarn test:coverage`, + }, + }; + }) + .filter(pkg => { + const { + scripts + } = require(path.resolve(__dirname, `../packages/${pkg.run.name}/package.json`)); + + return Object.keys(scripts).includes("test:coverage"); + }); + + const stepLog = job.steps[9]; + const stepCoverage = job.steps[10]; + + for (i = 0; i < steps.length; i++) { + job.steps[testStepIndex + i] = steps[i]; + } + + job.steps.push(stepLog); + job.steps.push(stepCoverage); + + config.jobs[name.slice(0, -1) + index] = job; + config.workflows.build_and_test.jobs.push(name.slice(0, -1) + index); + }); } - } - return count -} + fs.writeFileSync(".circleci/config.yml", yaml.safeDump(config)); +}); diff --git a/.circleci/rebuild-db.sh b/.circleci/rebuild-db.sh new file mode 100755 index 0000000000..3798325e18 --- /dev/null +++ b/.circleci/rebuild-db.sh @@ -0,0 +1,15 @@ +tables='rounds blocks transactions wallets' + +for table in $tables +do + dropcmd=$(echo "drop table if exists ${table};") + psql -h localhost -U core -d core_development -c "${dropcmd}" +done + +cd ../packages/core-database-postgres/src/migrations/ + +for sqlFile in ./*.sql +do + sqlcmd=$(cat $sqlFile | sed 's/${schema~}\.//g') + psql -h localhost -U core -d core_development -c "${sqlcmd}" +done \ No newline at end of file diff --git a/.codecov.yml b/.codecov.yml index f0d614a77b..3e621d21c5 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,3 +1,14 @@ ignore: + - "packages/**/src/defaults.ts" + - "packages/**/src/index.ts" + - "packages/**/src/plugin.ts" + - "packages/core-error-tracker-bugsnag/**/*" + - "packages/core-error-tracker-sentry/**/*" + - "packages/core-graphql/**/*" + - "packages/core-http-utils/**/*" + - "packages/core-logger-winston/src/formatter.ts" + - "packages/core-snapshots-cli/**/*" + - "packages/core-test-utils/**/*" + - "packages/core-test-utils/src/fixtures/**/*" - "packages/core-tester-cli/**/*" - - "packages/**/lib/index" + - "packages/core-webhooks/src/database/migrations/**/*" diff --git a/.editorconfig b/.editorconfig index 99580d06fe..cf80b4e9c2 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,5 +5,5 @@ charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = space -indent_size = 2 +indent_size = 4 trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index a04bc1cdb1..0000000000 --- a/.eslintignore +++ /dev/null @@ -1,9 +0,0 @@ -/build/** -/dist/** -/packages/dist/** -/.coverage/** -/docs/** -/tmp/** -/fixtures/** -/__fixtures__/** -!.eslintrc.js diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index ab219a187a..0000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "env": { - "commonjs": true, - "node": true, - "jest": true - }, - "extends": ["@arkecosystem/eslint-config-base", "prettier"], - "rules": { - "class-methods-use-this": "off", - "complexity": "off", - "global-require": "off", - "import/no-dynamic-require": "off", - "no-restricted-syntax": "off", - "no-console": [ - "error", - { "allow": ["error", "info", "warn", "time", "timeEnd"] } - ], - "no-plusplus": "off", - "no-continue": "off", - "no-param-reassign": "off", - "max-len": [ - "warn", - { - "code": 120, - "ignoreTemplateLiterals": true, - "ignoreRegExpLiterals": true - } - ], - "import/no-extraneous-dependencies": "off", - // TODO: fix later as they require a lot of changes - "consistent-return": "off", - "no-unused-expressions": "off", - "no-underscore-dangle": "off", - "no-unused-vars": "off", - "prefer-destructuring": "off", - "radix": "off" - } -} diff --git a/.github/ISSUE_TEMPLATE/Documentation_issue.md b/.github/ISSUE_TEMPLATE/Documentation_issue.md new file mode 100644 index 0000000000..089a660657 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Documentation_issue.md @@ -0,0 +1,8 @@ +--- +name: "Documentation Issue" +about: "For documentation issues, see: https://github.com/ArkEcosystem/docs/issues" +--- + +The Ark Core documentation has its own dedicated repository. Please open your documentation-related issue at https://github.com/ArkEcosystem/docs/issues. **However, it's best to simply make a pull request to correct the issue you have found!** + +Thanks! diff --git a/.github/ISSUE_TEMPLATE/Security_vulnerabilities.md b/.github/ISSUE_TEMPLATE/Security_vulnerabilities.md new file mode 100644 index 0000000000..1f0c3c599b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Security_vulnerabilities.md @@ -0,0 +1,8 @@ +--- +name: "Security Vulnerabilities" +about: "For reporting security-related issues, see: https://docs.ark.io/security/" +--- + +PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW. + +If you discover a security vulnerability within Core, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. diff --git a/.github/ISSUE_TEMPLATE/Support_question.md b/.github/ISSUE_TEMPLATE/Support_question.md new file mode 100644 index 0000000000..3fb7a681ac --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Support_question.md @@ -0,0 +1,8 @@ +--- +name: "Support Question" +about: "This repository is only for reporting bugs or problems." +--- + +This repository is only for reporting bugs or issues. If you need support, please send an e-mail to support@ark.io. + +Thanks! diff --git a/.gitignore b/.gitignore index da756344b3..a470a2cc41 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ docs # Optional npm cache directory .npm +package-lock.json # Mac OS X local settings .DS_Store @@ -60,4 +61,7 @@ packages/**/dist/ *.sqlite # Random -peers_backup.json +docker/development + +#Webstorm/Intellij +.idea diff --git a/.lintstagedrc.json b/.lintstagedrc.json index a8cd9eebff..9e4311289b 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -1,4 +1,4 @@ { - "*.js": ["eslint --fix", "prettier --write", "git add"], - "*.{json,md}": ["prettier --write", "git add"] + "*.ts": ["yarn lint", "prettier --write", "git add"], + "*.{json,md}": ["prettier --write", "git add"] } diff --git a/.prettierignore b/.prettierignore index 6350e98682..ccd28dd23c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,3 @@ .coverage +dist +docs diff --git a/.prettierrc.json b/.prettierrc.json index 89a0a2b7f0..3149fccdfb 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,7 @@ { - "singleQuote": true, - "trailingComma": "all", - "semi": false + "printWidth": 120, + "singleQuote": false, + "tabWidth": 4, + "trailingComma": "all", + "useTabs": false } diff --git a/.snyk b/.snyk index 384ff6609d..d57e0639a7 100644 --- a/.snyk +++ b/.snyk @@ -215,7 +215,7 @@ ignore: reason: None given expires: '2018-12-11T05:03:38.314Z' 'npm:chownr:20180731': - - '@arkecosystem/core > @arkecosystem/core-transaction-pool-mem > better-sqlite3 > tar > chownr': + - '@arkecosystem/core > @arkecosystem/core-transaction-pool > better-sqlite3 > tar > chownr': reason: None given expires: '2018-12-11T05:03:38.314Z' - '@arkecosystem/core > @arkecosystem/core-webhooks > sqlite3 > node-pre-gyp > tar > chownr': diff --git a/.yarnrc b/.yarnrc deleted file mode 100644 index 4f14322dc8..0000000000 --- a/.yarnrc +++ /dev/null @@ -1 +0,0 @@ ---ignore-engines true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..e0f975c7b6 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,272 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) +and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [2.1.0] - 2019-02-11 + +### Added + +- Added a `milestoneHash` identifier to use for peer banning ([#1837]) +- Added TypeScript declarations for `core-logger` ([#1833]) +- Added TypeScript declarations for `core-logger-winston` ([#1887]) +- Added TypeScript declarations for `core-container` ([#1891]) +- Added TypeScript declarations for `core-database` ([#1901], [#1905]) +- Added TypeScript declarations for `core-transaction-pool` ([#1906]) +- Added TypeScript declarations for `core-blockchain` ([#1943]) +- Added TypeScript declarations for `core-snapshots` ([#1947]) +- Added TypeScript declarations for `core-api` ([#1948]) +- Added TypeScript declarations for `crypto` ([#1917]) +- Added the `core-jest-matchers` package ([#1926]) +- Added the `core-interfaces` package ([#1924]) +- Return the transaction expiration time via API ([#1927]) +- Added the ability to disable the public API cache ([#1930]) +- Return the vote of a wallet via public API ([#2009]) +- Upgrade script for 2.1 ([#1999]) +- Installation script for deb/rpm distros ([#2016]) +- Case specific errors for `crypto` ([#2038]) + +### Changed + +- Migrated from JavaScript to TypeScript ([#1625]) +- Moved the `peers.json` configuration into `core-p2p` ([#1625]) +- Merged `core-transaction-pool-mem` into `core-transaction-pool` ([#1625]) +- Use a faster alternative to derive an estimate ([#1655]) +- Reworked crypto configuration to make it simpler ([#1733]) +- Moved the dynamic fees configuration into `core-transaction-pool` ([#1733]) +- Periodically check for new peers instead of retrying until finding some ([#1738]) +- Adjusted some banning times for peers to make network recovery smoother ([#1730]) +- Simplified configuration by further separating network and core ([#1733]) +- Take the `minFeeBroadcast` value into account for fee statistics ([#1873]) +- Only allow vendor fields for type 0 and 6 transactions ([#1931]) +- Improved the network quorum details and feedback ([#1898]) +- Only return errors when broadcast and pool fees are too low ([#1940]) +- Improved performance of BIP38 ([#1941]) +- Cleaned up the logic of block processing ([#1953]) +- Cleaned up the logic of serialise/deserialise in crypto ([#1969]) +- Replaced all ARK naming with CORE ([#1970]) +- Use system paths for data and configuration ([#1987]) +- Increased the maximum transaction age to 6 hours ([#1996]) +- Replaced progress bars with logging to reduce noise ([#2044]) +- Replaced commander.js with @oclif in `core-debugger-cli` ([#2049]) +- Replaced commander.js with @oclif in `core-snapshots-cli` ([#2050]) +- Replaced commander.js with @oclif in `core-tester-cli` ([#2051]) +- Moved docker files from `docker/*` to `docker/development/*` ([#2053]) +- Moved the genesis blocks from the `core` configuration to the network configuration in `crypto` ([#2052]) +- Separate business-logic from data-layer logic ([#2055]) + +### Fixed + +- Resolved an issue with the `resolveOptions` method that would result in options being resolved for plugins that are not registered in the container ([#1625]) +- Malformed condition for filtering peers ([#1689]) +- Use the correct pagination schema for the v2 public API ([#1717]) +- Ensure that delegate searches can handle undefined values ([#1831]) +- Mark semantically invalid versions as invalid overall ([#1836]) +- Ordering of delegates via public API ([#1731]) +- Handle webhooks that have no conditions ([#1869]) +- Validate the network byte on transactions ([#1853]) +- Use correct schemas for address, public key and username validation in the public API ([#1954]) +- Populate the last block of all delegates ([#1919]) +- Return the transaction forging timestamp instead of signing timestamp ([#1957]) +- Mark cold wallets as not found in the legacy API ([#1955]) +- A malformed condition that resulted in wrong peer lists ([#1939]) +- Properly verify block slot timestamps ([#1985]) +- Return fixed peer states for v1 and v2 API responses ([#2027]) +- Validate IP ranges to detect loopbacks ([#2045]) +- https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-010.md ([#2046]) +- Check if the blockchain state storage is available before performing fork checks ([#2047]) +- Gracefully handle a corrupted cached `peers.json` file ([#2061]) +- Always sort transactions by sequence and the requested field to make API sorting deterministic ([#2058]) +- Disallow multiple registrations for same delegate ([#2080]) + +### Removed + +- Removed the `transactionsFromIds` P2P endpoint ([#1911]) +- Removed the `validator` and `rules` fron `@arkecosystem/crypto` ([#2021]) +- Ended support for the legacy multisignatures from the previous LISK fork ([#2057]) + +## [2.0.19] - 2019-01-31 + +### Fixed + +- https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-009.md +- https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-010.md + +## [2.0.18] - 2019-01-28 + +### Fixed + +- https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-011.md + +## [2.0.17] - 2019-01-15 + +### Fixed + +- https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-008.md +- https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-007.md + +## [2.0.16] - 2018-12-17 + +### Fixed + +- Prevent the list of peers to become too short. This is related to the nodes running behind a firewall. + +Closed security vulnerabilities: + +- [CORE-SV-004](https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-004.md) +- [CORE-SV-003](https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-003.md) +- [CORE-SV-002](https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-002.md) +- [CORE-SV-001](https://github.com/ArkEcosystem/security-vulnerabilities/blob/master/core/core-sv-001.md) + +## [2.0.15] - 2018-12-11 + +### Fixed + +- Ensure no local peers are enlisted and that the IP of the TCP connection is used ([#1695]) + +## [2.0.14] - 2018-12-10 + +### Fixed + +- Reset last downloaded block when block is discarded ([#1692]) + +## [2.0.13] - 2018-12-07 + +### Fixed + +- Ensure safe integer range for block height lookups via API ([#1673]) + +## [2.0.12] - 2018-12-06 + +### Fixed + +- Perform second-signature checks in the `canApply` logic of multi-signatures ([#1658]) +- return the encoded WIF for BIP38 wallets instead of the encrypted WIF ([#1653]) + +## [2.0.11] - 2018-12-05 + +### Added + +- Store executed migrations in the database ([#1648]) + +### Changed + +- Increase cache generation timeout and make it configurable ([#1645], [#1646]) + +## [2.0.1] - 2018-12-05 + +### Added + +- Retrieve blocks via height or ID per public API ([#1626]) + +### Changed + +- Improved performance for block and transaction queries by adding more indices on critical columns ([#1636], [#1638], [#1634]) + +### Fixed + +- Take milestones into account for supply calculations ([#1640]) +- Use the raw transaction data in `acceptChainedBlock` to avoid timestamp mismatches and second signature double spend errors ([#1564]) +- Return the correct peer count for the v2 public API ([#1563]) + +## [2.0.0] - 2018-12-03 + +- Initial Release + +[unreleased]: https://github.com/ArkEcosystem/core/compare/2.0.19...develop +[2.1.0]: https://github.com/ArkEcosystem/core/compare/2.0.19...2.1.0 +[2.0.19]: https://github.com/ArkEcosystem/core/compare/2.0.18...2.0.19 +[2.0.18]: https://github.com/ArkEcosystem/core/compare/2.0.17...2.0.18 +[2.0.17]: https://github.com/ArkEcosystem/core/compare/2.0.16...2.0.17 +[2.0.16]: https://github.com/ArkEcosystem/core/compare/2.0.15...2.0.16 +[2.0.15]: https://github.com/ArkEcosystem/core/compare/2.0.14...2.0.15 +[2.0.14]: https://github.com/ArkEcosystem/core/compare/2.0.13...2.0.14 +[2.0.13]: https://github.com/ArkEcosystem/core/compare/2.0.12...2.0.13 +[2.0.12]: https://github.com/ArkEcosystem/core/compare/2.0.11...2.0.12 +[2.0.11]: https://github.com/ArkEcosystem/core/compare/2.0.1...2.0.11 +[2.0.1]: https://github.com/ArkEcosystem/core/compare/2.0.0...2.0.1 +[2.0.0]: https://github.com/ArkEcosystem/core/compare/0.1.1...2.0.0 +[#1563]: https://github.com/ArkEcosystem/core/pull/1563 +[#1564]: https://github.com/ArkEcosystem/core/pull/1564 +[#1625]: https://github.com/ArkEcosystem/core/pull/1625 +[#1626]: https://github.com/ArkEcosystem/core/pull/1626 +[#1634]: https://github.com/ArkEcosystem/core/pull/1634 +[#1636]: https://github.com/ArkEcosystem/core/pull/1636 +[#1638]: https://github.com/ArkEcosystem/core/pull/1638 +[#1638]: https://github.com/ArkEcosystem/core/pull/1638 +[#1640]: https://github.com/ArkEcosystem/core/pull/1640 +[#1645]: https://github.com/ArkEcosystem/core/pull/1645 +[#1646]: https://github.com/ArkEcosystem/core/pull/1646 +[#1648]: https://github.com/ArkEcosystem/core/pull/1648 +[#1653]: https://github.com/ArkEcosystem/core/pull/1653 +[#1655]: https://github.com/ArkEcosystem/core/pull/1655 +[#1658]: https://github.com/ArkEcosystem/core/pull/1658 +[#1673]: https://github.com/ArkEcosystem/core/pull/1673 +[#1689]: https://github.com/ArkEcosystem/core/pull/1689 +[#1692]: https://github.com/ArkEcosystem/core/pull/1692 +[#1695]: https://github.com/ArkEcosystem/core/pull/1695 +[#1717]: https://github.com/ArkEcosystem/core/pull/1717 +[#1730]: https://github.com/ArkEcosystem/core/pull/1730 +[#1731]: https://github.com/ArkEcosystem/core/pull/1731 +[#1732]: https://github.com/ArkEcosystem/core/pull/1732 +[#1733]: https://github.com/ArkEcosystem/core/pull/1733 +[#1738]: https://github.com/ArkEcosystem/core/pull/1738 +[#1831]: https://github.com/ArkEcosystem/core/pull/1831 +[#1833]: https://github.com/ArkEcosystem/core/pull/1833 +[#1836]: https://github.com/ArkEcosystem/core/pull/1836 +[#1837]: https://github.com/ArkEcosystem/core/pull/1837 +[#1853]: https://github.com/ArkEcosystem/core/pull/1853 +[#1869]: https://github.com/ArkEcosystem/core/pull/1869 +[#1873]: https://github.com/ArkEcosystem/core/pull/1873 +[#1887]: https://github.com/ArkEcosystem/core/pull/1887 +[#1891]: https://github.com/ArkEcosystem/core/pull/1891 +[#1898]: https://github.com/ArkEcosystem/core/pull/1898 +[#1901]: https://github.com/ArkEcosystem/core/pull/1901 +[#1905]: https://github.com/ArkEcosystem/core/pull/1905 +[#1906]: https://github.com/ArkEcosystem/core/pull/1906 +[#1911]: https://github.com/ArkEcosystem/core/pull/1911 +[#1917]: https://github.com/ArkEcosystem/core/pull/1917 +[#1919]: https://github.com/ArkEcosystem/core/pull/1919 +[#1924]: https://github.com/ArkEcosystem/core/pull/1924 +[#1926]: https://github.com/ArkEcosystem/core/pull/1926 +[#1927]: https://github.com/ArkEcosystem/core/pull/1927 +[#1930]: https://github.com/ArkEcosystem/core/pull/1930 +[#1931]: https://github.com/ArkEcosystem/core/pull/1931 +[#1939]: https://github.com/ArkEcosystem/core/pull/1939 +[#1940]: https://github.com/ArkEcosystem/core/pull/1940 +[#1941]: https://github.com/ArkEcosystem/core/pull/1941 +[#1943]: https://github.com/ArkEcosystem/core/pull/1943 +[#1947]: https://github.com/ArkEcosystem/core/pull/1947 +[#1948]: https://github.com/ArkEcosystem/core/pull/1948 +[#1953]: https://github.com/ArkEcosystem/core/pull/1953 +[#1954]: https://github.com/ArkEcosystem/core/pull/1954 +[#1955]: https://github.com/ArkEcosystem/core/pull/1955 +[#1957]: https://github.com/ArkEcosystem/core/pull/1957 +[#1969]: https://github.com/ArkEcosystem/core/pull/1969 +[#1970]: https://github.com/ArkEcosystem/core/pull/1970 +[#1985]: https://github.com/ArkEcosystem/core/pull/1985 +[#1987]: https://github.com/ArkEcosystem/core/pull/1987 +[#1996]: https://github.com/ArkEcosystem/core/pull/1996 +[#1999]: https://github.com/ArkEcosystem/core/pull/1999 +[#2009]: https://github.com/ArkEcosystem/core/pull/2009 +[#2016]: https://github.com/ArkEcosystem/core/pull/2016 +[#2021]: https://github.com/ArkEcosystem/core/pull/2021 +[#2038]: https://github.com/ArkEcosystem/core/pull/2038 +[#2044]: https://github.com/ArkEcosystem/core/pull/2044 +[#2045]: https://github.com/ArkEcosystem/core/pull/2045 +[#2046]: https://github.com/ArkEcosystem/core/pull/2046 +[#2047]: https://github.com/ArkEcosystem/core/pull/2047 +[#2049]: https://github.com/ArkEcosystem/core/pull/2049 +[#2050]: https://github.com/ArkEcosystem/core/pull/2050 +[#2051]: https://github.com/ArkEcosystem/core/pull/2051 +[#2052]: https://github.com/ArkEcosystem/core/pull/2052 +[#2053]: https://github.com/ArkEcosystem/core/pull/2053 +[#2055]: https://github.com/ArkEcosystem/core/pull/2055 +[#2057]: https://github.com/ArkEcosystem/core/pull/2057 +[#2058]: https://github.com/ArkEcosystem/core/pull/2058 +[#2061]: https://github.com/ArkEcosystem/core/pull/2061 +[#2080]: https://github.com/ArkEcosystem/core/pull/2080 diff --git a/README.md b/README.md index 5ba201e18b..2865acf7e2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@

-[![Greenkeeper badge](https://badges.greenkeeper.io/ArkEcosystem/core.svg)](https://greenkeeper.io/) [![Build Status](https://badgen.now.sh/circleci/github/ArkEcosystem/core)](https://circleci.com/gh/ArkEcosystem/core) [![Codecov](https://badgen.now.sh/codecov/c/github/arkecosystem/core)](https://codecov.io/gh/arkecosystem/core) [![License: MIT](https://badgen.now.sh/badge/license/MIT/green)](https://opensource.org/licenses/MIT) @@ -15,41 +14,50 @@ This repository contains all plugins that make up the Ark Core. ## Documentation -- Development : https://docs.ark.io/guidebook/core/development.html -- Docker : https://docs.ark.io/guidebook/core/docker.html +- Development : https://docs.ark.io/guidebook/core/development.html +- Docker : https://docs.ark.io/guidebook/core/docker.html ## API Documentation -- API v1 : https://docs.ark.io/api/public/v1/ -- API v2 : https://docs.ark.io/api/public/v2/ +- API v1 : https://docs.ark.io/api/public/v1/ +- API v2 : https://docs.ark.io/api/public/v2/ ## GitHub Development Bounty -- Get involved with Ark development and start earning ARK : https://bounty.ark.io +- Get involved with Ark development and start earning ARK : https://bounty.ark.io ## Core Packages -| Package | Version | Description | -| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -| **[core](/packages/core)** | [![npm](https://img.shields.io/npm/v/@arkecosystem/core.svg)](https://www.npmjs.com/package/@arkecosystem/core) | **Includes all packages** | -| [core-api](/packages/core-api) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-api.svg)](https://www.npmjs.com/package/@arkecosystem/core-api) | Public API | -| [core-blockchain](/packages/core-blockchain) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-blockchain.svg)](https://www.npmjs.com/package/@arkecosystem/core-blockchain) | Blockchain Management | -| [core-config](/packages/core-config) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-config.svg)](https://www.npmjs.com/package/@arkecosystem/core-config) | Configuration Loader | -| [core-container](/packages/core-container) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-container.svg)](https://www.npmjs.com/package/@arkecosystem/core-container) | Container Management | -| [core-database](/packages/core-database) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-database.svg)](https://www.npmjs.com/package/@arkecosystem/core-database) | Database Interface | -| [core-deployer](/packages/core-deployer) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-deployer.svg)](https://www.npmjs.com/package/@arkecosystem/core-deployer) | Deployer CLI | -| [core-event-emitter](/packages/core-event-emitter) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-event-emitter.svg)](https://www.npmjs.com/package/@arkecosystem/core-event-emitter) | Event Manager | -| [core-forger](/packages/core-forger) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-forger.svg)](https://www.npmjs.com/package/@arkecosystem/core-forger) | Forger Manager | -| [core-graphql](/packages/core-graphql) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-graphql.svg)](https://www.npmjs.com/package/@arkecosystem/core-graphql) | GraphQL Provider | -| [core-json-rpc](/packages/core-json-rpc) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-json-rpc.svg)](https://www.npmjs.com/package/@arkecosystem/core-json-rpc) | JSON-RPC Server | -| [core-logger](/packages/core-logger) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-logger.svg)](https://www.npmjs.com/package/@arkecosystem/core-logger) | Logger Manager | -| [core-logger-winston](/packages/core-logger-winston) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-logger-winston.svg)](https://www.npmjs.com/package/@arkecosystem/core-logger-winston) | Winston Logger Provider | -| [core-p2p](/packages/core-p2p) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-p2p.svg)](https://www.npmjs.com/package/@arkecosystem/core-p2p) | P2P API | -| [test-utils](/packages/core-test-utils) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-test-utils.svg)](https://www.npmjs.com/package/@arkecosystem/core-test-utils) | Test Utilities | -| [tester-cli](/packages/core-tester-cli) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-tester-cli.svg)](https://www.npmjs.com/package/@arkecosystem/core-tester-cli) | Tester CLI | -| [core-transaction-pool](/packages/core-transaction-pool) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-transaction-pool.svg)](https://www.npmjs.com/package/@arkecosystem/core-transaction-pool) | Transaction Pool Interface | -| [core-webhooks](/packages/core-webhooks) | [![npm](https://img.shields.io/npm/v/@arkecosystem/core-webhooks.svg)](https://www.npmjs.com/package/@arkecosystem/core-webhooks) | Webhooks Manager | -| [crypto](/packages/crypto) | [![npm](https://img.shields.io/npm/v/@arkecosystem/crypto.svg)](https://www.npmjs.com/package/@arkecosystem/crypto) | Crypto Utilities | +| Package | Version | Description | +| ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------ | +| **[core](/packages/core)** | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core)](https://www.npmjs.com/package/@arkecosystem/core) | **Includes all packages** | +| [core-api](/packages/core-api) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-api)](https://www.npmjs.com/package/@arkecosystem/core-api) | Public REST API | +| [core-blockchain](/packages/core-blockchain) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-blockchain)](https://www.npmjs.com/package/@arkecosystem/core-blockchain) | Blockchain Managment | +| [core-container](/packages/core-container) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-container)](https://www.npmjs.com/package/@arkecosystem/core-container) | Container Managment | +| [core-database](/packages/core-database) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-database)](https://www.npmjs.com/package/@arkecosystem/core-database) | Database Interface | +| [core-database-postgres](/packages/core-database-postgres) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-database-postgres)](https://www.npmjs.com/package/@arkecosystem/core-database-postgres) | Database Implementation - PostgreSQL | +| [core-debugger-cli](/packages/core-debugger-cli) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-debugger-cli)](https://www.npmjs.com/package/@arkecosystem/core-debugger-cli) | Debugger CLI _(development only)_ | +| [core-deployer](/packages/core-deployer) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-deployer)](https://www.npmjs.com/package/@arkecosystem/core-deployer) | Deployer CLI | +| [core-elasticsearch](/packages/core-elasticsearch) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-elasticsearch)](https://www.npmjs.com/package/@arkecosystem/core-elasticsearch) | Elasticsearch Server | +| [core-error-tracker-bugsnag](/packages/core-error-tracker-bugsnag) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-error-tracker-bugsnag)](https://www.npmjs.com/package/@arkecosystem/core-error-tracker-bugsnag) | Error Tracking - Bugsnag | +| [core-error-tracker-sentry](/packages/core-error-tracker-sentry) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-error-tracker-sentry)](https://www.npmjs.com/package/@arkecosystem/core-error-tracker-sentry) | Error Tracking - Sentry | +| [core-event-emitter](/packages/core-event-emitter) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-event-emitter)](https://www.npmjs.com/package/@arkecosystem/core-event-emitter) | Event Emitter | +| [core-forger](/packages/core-forger) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-forger)](https://www.npmjs.com/package/@arkecosystem/core-forger) | Forger Manager | +| [core-graphql](/packages/core-graphql) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-graphql)](https://www.npmjs.com/package/@arkecosystem/core-graphql) | GraphQL Server | +| [core-http-utils](/packages/core-http-utils) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-http-utils)](https://www.npmjs.com/package/@arkecosystem/core-http-utils) | HTTP Utilities | +| [core-json-rpc](/packages/core-json-rpc) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-json-rpc)](https://www.npmjs.com/package/@arkecosystem/core-json-rpc) | JSON-RPC Server | +| [core-logger](/packages/core-logger) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-logger)](https://www.npmjs.com/package/@arkecosystem/core-logger) | Logger Interface | +| [core-logger-winston](/packages/core-logger-winston) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-logger-winston)](https://www.npmjs.com/package/@arkecosystem/core-logger-winston) | Logger Implementation - Winston | +| [core-p2p](/packages/core-p2p) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-p2p)](https://www.npmjs.com/package/@arkecosystem/core-p2p) | P2P Communication | +| [core-snapshots](/packages/core-snapshots) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-snapshots)](https://www.npmjs.com/package/@arkecosystem/core-snapshots) | Snapshot Manager | +| [core-snapshots-cli](/packages/core-snapshots-cli) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-snapshots-cli)](https://www.npmjs.com/package/@arkecosystem/core-snapshots-cli) | Snapshot CLI | +| [core-test-utils](/packages/core-test-utils) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-test-utils)](https://www.npmjs.com/package/@arkecosystem/core-test-utils) | Test Utilities _(development only)_ | +| [core-tester-cli](/packages/core-tester-cli) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-tester-cli)](https://www.npmjs.com/package/@arkecosystem/core-tester-cli) | Tester CLi _(development only)_ | +| [core-transaction-pool](/packages/core-transaction-pool) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-transaction-pool)](https://www.npmjs.com/package/@arkecosystem/core-transaction-pool) | Transaction Pool | +| [core-utils](/packages/core-utils) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-utils)](https://www.npmjs.com/package/@arkecosystem/core-utils) | Utilities | +| [core-vote-report](/packages/core-vote-report) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-vote-report)](https://www.npmjs.com/package/@arkecosystem/core-vote-report) | Vote Report | +| [core-webhooks](/packages/core-webhooks) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/core-webhooks)](https://www.npmjs.com/package/@arkecosystem/core-webhooks) | Webhook Server | +| [crypto](/packages/crypto) | [![npm](https://badgen.now.sh/npm/v/@arkecosystem/crypto)](https://www.npmjs.com/package/@arkecosystem/crypto) | Cryptography | ## Security @@ -57,11 +65,13 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [All Contributors](../../contributors) +- [All Contributors](../../contributors) +- [Alex Barnsley](https://github.com/alexbarnsley) +- [Brian Faust](https://github.com/faustbrian) +- [François-Xavier Thoorens](https://github.com/fix) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [Vasil Dimov](https://github.com/vasild) ## License diff --git a/benchmark/block/deserialize/0.js b/benchmark/block/deserialize/0.js new file mode 100644 index 0000000000..8f9a9c7f50 --- /dev/null +++ b/benchmark/block/deserialize/0.js @@ -0,0 +1,9 @@ +const { + deserialize +} = require('./methods') + +const data = require('../../helpers').getFixture('block/serialized/no-transactions.txt'); + +exports['core'] = () => { + return deserialize(data); +}; diff --git a/benchmark/block/deserialize/150.js b/benchmark/block/deserialize/150.js new file mode 100644 index 0000000000..30a3017ff9 --- /dev/null +++ b/benchmark/block/deserialize/150.js @@ -0,0 +1,9 @@ +const { + deserialize +} = require('./methods') + +const data = require('../../helpers').getFixture('block/serialized/transactions.txt'); + +exports['core'] = () => { + return deserialize(data); +}; diff --git a/benchmark/block/deserialize/methods.js b/benchmark/block/deserialize/methods.js new file mode 100644 index 0000000000..07326048e0 --- /dev/null +++ b/benchmark/block/deserialize/methods.js @@ -0,0 +1,5 @@ +const { models } = require('@arkecosystem/crypto') + +exports.deserialize = data => { + return models.Block.deserialize(data) +} diff --git a/benchmark/block/serialize.js b/benchmark/block/serialize.js new file mode 100644 index 0000000000..7360bfeb4b --- /dev/null +++ b/benchmark/block/serialize.js @@ -0,0 +1,9 @@ +const { + models +} = require('@arkecosystem/crypto') + +const data = require('../helpers').getJSONFixture('block/deserialized/no-transactions'); + +exports['core'] = () => { + return models.Block.serialize(data); +}; diff --git a/benchmark/block/serializeFull.js b/benchmark/block/serializeFull.js new file mode 100644 index 0000000000..d8e5eba5cf --- /dev/null +++ b/benchmark/block/serializeFull.js @@ -0,0 +1,9 @@ +const { + models +} = require('@arkecosystem/crypto') + +const data = require('../helpers').getJSONFixture('block/deserialized/transactions'); + +exports['core'] = () => { + return models.Block.serializeFull(data); +}; diff --git a/benchmark/fixtures/block/deserialized/no-transactions.json b/benchmark/fixtures/block/deserialized/no-transactions.json new file mode 100644 index 0000000000..792a633096 --- /dev/null +++ b/benchmark/fixtures/block/deserialized/no-transactions.json @@ -0,0 +1,17 @@ +{ + "version": 0, + "timestamp": 58126418, + "height": 19, + "previousBlockHex": "a099b7651f0e5eb8", + "previousBlock": "11572482362445815480", + "numberOfTransactions": 150, + "totalAmount": "30000000000", + "totalFee": "1500000000", + "reward": "0", + "payloadLength": 4800, + "payloadHash": "e5a7e9b5a8a8e2f47f7d8a532e0e9c43d44052dc6c6339ad57246e9a339665e3", + "generatorPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "blockSignature": "304402204e31f1ae02cbcf2bb936e225f9f9db332ac275577b777a389b2d713e48b78c9002203f11c4ee0d30d2e10b2cb4a7fb59569e761571971ffe1be5abaa32fdc42a056b", + "idHex": "d9401ad36a03b8b4", + "id": "15654541800058894516" +} diff --git a/benchmark/fixtures/block/deserialized/transactions.json b/benchmark/fixtures/block/deserialized/transactions.json new file mode 100644 index 0000000000..fb2c7b9e87 --- /dev/null +++ b/benchmark/fixtures/block/deserialized/transactions.json @@ -0,0 +1,4669 @@ +{ + "version": 0, + "timestamp": 58126418, + "height": 19, + "previousBlockHex": "a099b7651f0e5eb8", + "previousBlock": "11572482362445815480", + "numberOfTransactions": 150, + "totalAmount": "30000000000", + "totalFee": "1500000000", + "reward": "0", + "payloadLength": 4800, + "payloadHash": "e5a7e9b5a8a8e2f47f7d8a532e0e9c43d44052dc6c6339ad57246e9a339665e3", + "generatorPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "blockSignature": "304402204e31f1ae02cbcf2bb936e225f9f9db332ac275577b777a389b2d713e48b78c9002203f11c4ee0d30d2e10b2cb4a7fb59569e761571971ffe1be5abaa32fdc42a056b", + "transactions": [ + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b00000000000000001759e7dc56557733804418f0ea6fd3b2573a9aabdd3045022100bac5b7699748a891b39ff5439e16ea1a694e93954b248be6b8082da01e5386310220129eb06a58b9f80d36ea3cdc903e6cc0240bbe1d371339ffe15c87742af1427d", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2037", + "expiration": 0, + "recipientId": "APyFYXxXtUrvZFnEuwLopfst94GMY5Zkeq", + "signature": "3045022100bac5b7699748a891b39ff5439e16ea1a694e93954b248be6b8082da01e5386310220129eb06a58b9f80d36ea3cdc903e6cc0240bbe1d371339ffe15c87742af1427d", + "vendorField": "Transaction 7", + "id": "a4b0eed1247ae168a8e425e020edd26e7bb05260cd0c63e3c3df5d1ce02f63f8" + }, + "verified": false, + "id": "a4b0eed1247ae168a8e425e020edd26e7bb05260cd0c63e3c3df5d1ce02f63f8", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APyFYXxXtUrvZFnEuwLopfst94GMY5Zkeq", + "type": 0, + "vendorField": "Transaction 7", + "vendorFieldHex": "5472616e73616374696f6e2037", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100bac5b7699748a891b39ff5439e16ea1a694e93954b248be6b8082da01e5386310220129eb06a58b9f80d36ea3cdc903e6cc0240bbe1d371339ffe15c87742af1427d", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20343000c2eb0b00000000000000001779265225b00860251a567d176a8927c8416d27f7304502210090d88b95320b5e0d51eec36a007bfe1f0a95c7b2c4f9ad00e833bc9f2dd4160102203c4f24c4cb1f8faa60ab139a2245c977eb39b4b6e09ea1d575beede498cf0908", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203430", + "expiration": 0, + "recipientId": "ASpTKHsiVWoqSPqBnkde54c32kNqqETyxk", + "signature": "304502210090d88b95320b5e0d51eec36a007bfe1f0a95c7b2c4f9ad00e833bc9f2dd4160102203c4f24c4cb1f8faa60ab139a2245c977eb39b4b6e09ea1d575beede498cf0908", + "vendorField": "Transaction 40", + "id": "585ed6b426b5359ad219101223aaafdc0ef23695a71ab624ee1db1acd7ba9fd6" + }, + "verified": false, + "id": "585ed6b426b5359ad219101223aaafdc0ef23695a71ab624ee1db1acd7ba9fd6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ASpTKHsiVWoqSPqBnkde54c32kNqqETyxk", + "type": 0, + "vendorField": "Transaction 40", + "vendorFieldHex": "5472616e73616374696f6e203430", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210090d88b95320b5e0d51eec36a007bfe1f0a95c7b2c4f9ad00e833bc9f2dd4160102203c4f24c4cb1f8faa60ab139a2245c977eb39b4b6e09ea1d575beede498cf0908", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333600c2eb0b00000000000000001740d8fa2bdb69bd24ff0b861241632fa355be348a3044022014e308901e42c8881964cb4abc5af1122e1be51cf39548fb0461f44cf26394410220484234dd58a91ffffd0439c158c47f08652f1c180148fc41caa62421df962dce", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203336", + "expiration": 0, + "recipientId": "AMgkpcndp2JHrqQEm3ftnBHrCaN59N4WLh", + "signature": "3044022014e308901e42c8881964cb4abc5af1122e1be51cf39548fb0461f44cf26394410220484234dd58a91ffffd0439c158c47f08652f1c180148fc41caa62421df962dce", + "vendorField": "Transaction 36", + "id": "d1b83f1abdf43adae989c06ff22f6ef0fc96f4a8477ec9c951ddf3d60be6bc69" + }, + "verified": false, + "id": "d1b83f1abdf43adae989c06ff22f6ef0fc96f4a8477ec9c951ddf3d60be6bc69", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AMgkpcndp2JHrqQEm3ftnBHrCaN59N4WLh", + "type": 0, + "vendorField": "Transaction 36", + "vendorFieldHex": "5472616e73616374696f6e203336", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022014e308901e42c8881964cb4abc5af1122e1be51cf39548fb0461f44cf26394410220484234dd58a91ffffd0439c158c47f08652f1c180148fc41caa62421df962dce", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b000000000000000017cce58b0a597c0eb2b10b68178e6bddd32e1e15193045022100c0cd610e0230d66200c15b6b4e1e34806ee17db7ec593b395f0b33f1544c3b8c0220211ce028811af38210c3f4768110df1173c373ed89574e3d0eee7275c9b660ca", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203138", + "expiration": 0, + "recipientId": "AaTGUZtFw5fkK9yndiSHoJaHYHLRP9NUdS", + "signature": "3045022100c0cd610e0230d66200c15b6b4e1e34806ee17db7ec593b395f0b33f1544c3b8c0220211ce028811af38210c3f4768110df1173c373ed89574e3d0eee7275c9b660ca", + "vendorField": "Transaction 18", + "id": "f393e06b672e11510086cf6a1f889d331cd84b400317652a28c6d98d572f32aa" + }, + "verified": false, + "id": "f393e06b672e11510086cf6a1f889d331cd84b400317652a28c6d98d572f32aa", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AaTGUZtFw5fkK9yndiSHoJaHYHLRP9NUdS", + "type": 0, + "vendorField": "Transaction 18", + "vendorFieldHex": "5472616e73616374696f6e203138", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c0cd610e0230d66200c15b6b4e1e34806ee17db7ec593b395f0b33f1544c3b8c0220211ce028811af38210c3f4768110df1173c373ed89574e3d0eee7275c9b660ca", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b00000000000000001737cb57f4bf6e9f4e64fe975a2b26d8ad37b5859b304502210083bd4d5ac41a8072672573c2fe008d5a626306b7d5bb3b775e429bd50a3a51a20220422ee1674f478bcca69527ccd5347c711aa04026702eb258658935aeeb451621", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203135", + "expiration": 0, + "recipientId": "ALrtQcFHYk8SjXNqoQy5vq4CQn9dDZ32HV", + "signature": "304502210083bd4d5ac41a8072672573c2fe008d5a626306b7d5bb3b775e429bd50a3a51a20220422ee1674f478bcca69527ccd5347c711aa04026702eb258658935aeeb451621", + "vendorField": "Transaction 15", + "id": "0d7a15c9805853d25ea37362c03c18fb675fa0f24ec713c0066c477f29b37823" + }, + "verified": false, + "id": "0d7a15c9805853d25ea37362c03c18fb675fa0f24ec713c0066c477f29b37823", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ALrtQcFHYk8SjXNqoQy5vq4CQn9dDZ32HV", + "type": 0, + "vendorField": "Transaction 15", + "vendorFieldHex": "5472616e73616374696f6e203135", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210083bd4d5ac41a8072672573c2fe008d5a626306b7d5bb3b775e429bd50a3a51a20220422ee1674f478bcca69527ccd5347c711aa04026702eb258658935aeeb451621", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b0000000000000000174736fe7ebfdf155ab1eaa5da635391ec140ceff43045022100c0f7ddd725c781c57efcec81aa42872b1791320889b820d8db2da9172df6cc150220560872a2e522fe8851f9642e42551e72ebc8e0bfa0428cb535dc4285a62337d5", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2031", + "expiration": 0, + "recipientId": "ANGRWBD9E58V4pz8XZPhYc6XMQ5TCpWxWv", + "signature": "3045022100c0f7ddd725c781c57efcec81aa42872b1791320889b820d8db2da9172df6cc150220560872a2e522fe8851f9642e42551e72ebc8e0bfa0428cb535dc4285a62337d5", + "vendorField": "Transaction 1", + "id": "67d71cc75ff85938faee0b14a653b060305dcc0f5113fe735f59fcf7bd86a0f5" + }, + "verified": false, + "id": "67d71cc75ff85938faee0b14a653b060305dcc0f5113fe735f59fcf7bd86a0f5", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ANGRWBD9E58V4pz8XZPhYc6XMQ5TCpWxWv", + "type": 0, + "vendorField": "Transaction 1", + "vendorFieldHex": "5472616e73616374696f6e2031", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c0f7ddd725c781c57efcec81aa42872b1791320889b820d8db2da9172df6cc150220560872a2e522fe8851f9642e42551e72ebc8e0bfa0428cb535dc4285a62337d5", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b0000000000000000172f5a665ddf88e839f2be0898b0a00b30606988233045022100e768703636589e566a237a52b7ffc47f33a90b43378b72ed8c4d8a253c3beff102205cbd729b8b00e0d27c342e174b136f11152b52b8c243b0f0d16cbfe9408eb700", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203137", + "expiration": 0, + "recipientId": "AL6FhcBeyDW7dpX4PdRTQvjgLzPKBZa6zf", + "signature": "3045022100e768703636589e566a237a52b7ffc47f33a90b43378b72ed8c4d8a253c3beff102205cbd729b8b00e0d27c342e174b136f11152b52b8c243b0f0d16cbfe9408eb700", + "vendorField": "Transaction 17", + "id": "e5d725d3432d64b69bcb4d54e36939735165833864fc7559ee43387b09610cb9" + }, + "verified": false, + "id": "e5d725d3432d64b69bcb4d54e36939735165833864fc7559ee43387b09610cb9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AL6FhcBeyDW7dpX4PdRTQvjgLzPKBZa6zf", + "type": 0, + "vendorField": "Transaction 17", + "vendorFieldHex": "5472616e73616374696f6e203137", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e768703636589e566a237a52b7ffc47f33a90b43378b72ed8c4d8a253c3beff102205cbd729b8b00e0d27c342e174b136f11152b52b8c243b0f0d16cbfe9408eb700", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017b384f7340916f6343bb04f6839faaa3743c2f28f304402206f203df1a332df9029eb362d75af589f391f301330ac4f9ac9311f4c46e26ac602201fc0ac2e97b2ed60737644f1906fb7265815db6a2013c3df674e8274a8352f0e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203133", + "expiration": 0, + "recipientId": "AY95tT5ZJruhxS2nozU3tJG2YgJTqHSDiC", + "signature": "304402206f203df1a332df9029eb362d75af589f391f301330ac4f9ac9311f4c46e26ac602201fc0ac2e97b2ed60737644f1906fb7265815db6a2013c3df674e8274a8352f0e", + "vendorField": "Transaction 13", + "id": "903e3476ce07fa27d0d2499c85d162f61d396b2c02413d97ee7a0c1ef0b95bb6" + }, + "verified": false, + "id": "903e3476ce07fa27d0d2499c85d162f61d396b2c02413d97ee7a0c1ef0b95bb6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AY95tT5ZJruhxS2nozU3tJG2YgJTqHSDiC", + "type": 0, + "vendorField": "Transaction 13", + "vendorFieldHex": "5472616e73616374696f6e203133", + "amount": "200000000", + "fee": "10000000", + "signature": "304402206f203df1a332df9029eb362d75af589f391f301330ac4f9ac9311f4c46e26ac602201fc0ac2e97b2ed60737644f1906fb7265815db6a2013c3df674e8274a8352f0e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b0000000000000000178c86c220a027fb8e55429eaa91c92fbe9fc978aa3044022061dd2066ec8fed52d6992b91f724fc4b10d5ee772d2bd2e7d980501d773c0c1a0220189932d1ea738fd54227ec0019ddd7446b216cb0d839f716a7899521d47ca15c", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2039", + "expiration": 0, + "recipientId": "AUaugXUAx2hskhLAQ8kFkYxKTr3r8Gk1yu", + "signature": "3044022061dd2066ec8fed52d6992b91f724fc4b10d5ee772d2bd2e7d980501d773c0c1a0220189932d1ea738fd54227ec0019ddd7446b216cb0d839f716a7899521d47ca15c", + "vendorField": "Transaction 9", + "id": "f3aa47c5af1f5346f48579981b4264b920415c2b7671bf930167fc2f29aeb9c6" + }, + "verified": false, + "id": "f3aa47c5af1f5346f48579981b4264b920415c2b7671bf930167fc2f29aeb9c6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AUaugXUAx2hskhLAQ8kFkYxKTr3r8Gk1yu", + "type": 0, + "vendorField": "Transaction 9", + "vendorFieldHex": "5472616e73616374696f6e2039", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022061dd2066ec8fed52d6992b91f724fc4b10d5ee772d2bd2e7d980501d773c0c1a0220189932d1ea738fd54227ec0019ddd7446b216cb0d839f716a7899521d47ca15c", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b0000000000000000170e52656c664a3401457644d379eccf63219051ee304502210090364cc31769fe896d265797f1048147f374ce55d5b4544403459e95f66567cb0220246454cdf2961c4246b00a70f0c9e5f853f5e3ba34ada1f45ba5b40ad86a5c8e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203236", + "expiration": 0, + "recipientId": "AH5bpAtRB7yKYG31biiDCJVAQF5qwRyQfB", + "signature": "304502210090364cc31769fe896d265797f1048147f374ce55d5b4544403459e95f66567cb0220246454cdf2961c4246b00a70f0c9e5f853f5e3ba34ada1f45ba5b40ad86a5c8e", + "vendorField": "Transaction 26", + "id": "c32e5c1337ffde88d46b9e2403f3b2a647c40a4027610b3670525c54dc5e1497" + }, + "verified": false, + "id": "c32e5c1337ffde88d46b9e2403f3b2a647c40a4027610b3670525c54dc5e1497", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AH5bpAtRB7yKYG31biiDCJVAQF5qwRyQfB", + "type": 0, + "vendorField": "Transaction 26", + "vendorFieldHex": "5472616e73616374696f6e203236", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210090364cc31769fe896d265797f1048147f374ce55d5b4544403459e95f66567cb0220246454cdf2961c4246b00a70f0c9e5f853f5e3ba34ada1f45ba5b40ad86a5c8e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b000000000000000017c277b97583e4e3709292528d50a988631d73bb27304402201955362c3285d9452ff7a212fdb963cc80346e1d1ec883cffba07e7fa53929d7022013cc3982a6c3fa482e1545b5a856b4589f2212782c7aa4efde6863d092971187", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2032", + "expiration": 0, + "recipientId": "AZW8AKAaGESzJCLVcVLaExR1ZXBNfsyJcK", + "signature": "304402201955362c3285d9452ff7a212fdb963cc80346e1d1ec883cffba07e7fa53929d7022013cc3982a6c3fa482e1545b5a856b4589f2212782c7aa4efde6863d092971187", + "vendorField": "Transaction 2", + "id": "9f4c4b1b020917cfe97bca0936d15f09c189a955c4072d4265a8cd7559c46000" + }, + "verified": false, + "id": "9f4c4b1b020917cfe97bca0936d15f09c189a955c4072d4265a8cd7559c46000", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AZW8AKAaGESzJCLVcVLaExR1ZXBNfsyJcK", + "type": 0, + "vendorField": "Transaction 2", + "vendorFieldHex": "5472616e73616374696f6e2032", + "amount": "200000000", + "fee": "10000000", + "signature": "304402201955362c3285d9452ff7a212fdb963cc80346e1d1ec883cffba07e7fa53929d7022013cc3982a6c3fa482e1545b5a856b4589f2212782c7aa4efde6863d092971187", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b0000000000000000170a953805b93337eac6e84e7546ce431ffa11e071304502210089b1238ad440434ddadd661bbe1c9545990f9d7e18625ea6d1501608cb408168022002bd4e6022f29d967fa28b3030ad6f5a50d3d82b8a2d0f851b6c1d266745deef", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203139", + "expiration": 0, + "recipientId": "AGjqAKhY362t93gYQH5MxM7PQjyhEFxHzE", + "signature": "304502210089b1238ad440434ddadd661bbe1c9545990f9d7e18625ea6d1501608cb408168022002bd4e6022f29d967fa28b3030ad6f5a50d3d82b8a2d0f851b6c1d266745deef", + "vendorField": "Transaction 19", + "id": "0511ec2b530191df929f71f27b46d21463d3b3df9027acc384f440d8d82da4a6" + }, + "verified": false, + "id": "0511ec2b530191df929f71f27b46d21463d3b3df9027acc384f440d8d82da4a6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AGjqAKhY362t93gYQH5MxM7PQjyhEFxHzE", + "type": 0, + "vendorField": "Transaction 19", + "vendorFieldHex": "5472616e73616374696f6e203139", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210089b1238ad440434ddadd661bbe1c9545990f9d7e18625ea6d1501608cb408168022002bd4e6022f29d967fa28b3030ad6f5a50d3d82b8a2d0f851b6c1d266745deef", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b0000000000000000176b7f10a49c1e137a728a6952f0b5236b841d677d30440220779694b27ec4fc1861b071708caaa0f57975f1335e9cacd71d4e7880cd4b1fcb022006badd77518de6eca6d7149c240ca7d028063552f4653db46794e818b21e047a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203238", + "expiration": 0, + "recipientId": "ARaGAezxE446zDfh5HnXpViRXHd3kpzy1L", + "signature": "30440220779694b27ec4fc1861b071708caaa0f57975f1335e9cacd71d4e7880cd4b1fcb022006badd77518de6eca6d7149c240ca7d028063552f4653db46794e818b21e047a", + "vendorField": "Transaction 28", + "id": "49cd6e3eb00b7780710d58d6d653f3fabec38f8e91e1240c81e56110cb1bdd4d" + }, + "verified": false, + "id": "49cd6e3eb00b7780710d58d6d653f3fabec38f8e91e1240c81e56110cb1bdd4d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ARaGAezxE446zDfh5HnXpViRXHd3kpzy1L", + "type": 0, + "vendorField": "Transaction 28", + "vendorFieldHex": "5472616e73616374696f6e203238", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220779694b27ec4fc1861b071708caaa0f57975f1335e9cacd71d4e7880cd4b1fcb022006badd77518de6eca6d7149c240ca7d028063552f4653db46794e818b21e047a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b000000000000000017fdebc4b239afc2e6903ed93105125aa256416a3130440220405044882c636f9cd266ba6dab3fb1044b7225e6c8c31823b1c07f643dc2246c02202bc752c7c077310a1a9b07431b32f6333492b6cd18f0eabdd6250fe827e8da74", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203231", + "expiration": 0, + "recipientId": "AevV3W8yKoC5ajqRG9PvPc6Ugg82Ne9kmV", + "signature": "30440220405044882c636f9cd266ba6dab3fb1044b7225e6c8c31823b1c07f643dc2246c02202bc752c7c077310a1a9b07431b32f6333492b6cd18f0eabdd6250fe827e8da74", + "vendorField": "Transaction 21", + "id": "6d1488d7c5439e10553cf48e1a3cb2be849cd2ab9ba7b482bb792ed6cf5618c5" + }, + "verified": false, + "id": "6d1488d7c5439e10553cf48e1a3cb2be849cd2ab9ba7b482bb792ed6cf5618c5", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AevV3W8yKoC5ajqRG9PvPc6Ugg82Ne9kmV", + "type": 0, + "vendorField": "Transaction 21", + "vendorFieldHex": "5472616e73616374696f6e203231", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220405044882c636f9cd266ba6dab3fb1044b7225e6c8c31823b1c07f643dc2246c02202bc752c7c077310a1a9b07431b32f6333492b6cd18f0eabdd6250fe827e8da74", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b000000000000000017d5da08ba4319905bc4d2052d603e0ca776b73fb73045022100aceb5f53684a8c9c8339132f30369a82677cee0ed4485421a70a34016ac61a8d0220499ddca0d7a6180452c25f109f737a8d982674f0dd3f2d42a191c5790dfbf34e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2033", + "expiration": 0, + "recipientId": "AbGcmde2Xgar2sK9hJNNytmWRn4f7CixxX", + "signature": "3045022100aceb5f53684a8c9c8339132f30369a82677cee0ed4485421a70a34016ac61a8d0220499ddca0d7a6180452c25f109f737a8d982674f0dd3f2d42a191c5790dfbf34e", + "vendorField": "Transaction 3", + "id": "79154b4d3b3b64fb9506dda7eb5010b96c0ad531efaaf18f208abeeb70a9179e" + }, + "verified": false, + "id": "79154b4d3b3b64fb9506dda7eb5010b96c0ad531efaaf18f208abeeb70a9179e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AbGcmde2Xgar2sK9hJNNytmWRn4f7CixxX", + "type": 0, + "vendorField": "Transaction 3", + "vendorFieldHex": "5472616e73616374696f6e2033", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100aceb5f53684a8c9c8339132f30369a82677cee0ed4485421a70a34016ac61a8d0220499ddca0d7a6180452c25f109f737a8d982674f0dd3f2d42a191c5790dfbf34e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b000000000000000017594547725060beb91e991aceb0cceeec84d4b7953045022100f966cc6d0d00932284e8088557cc113378847236cb22151b8bbd10bd9acfaa7602203bd4e93dcd72343a9dfe60b36d7e0deaaaffe7676ef6083e839cd2451aac7f36", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203233", + "expiration": 0, + "recipientId": "APutnAcu2uWwrTxR5JhY8FKYwFJvU14hLs", + "signature": "3045022100f966cc6d0d00932284e8088557cc113378847236cb22151b8bbd10bd9acfaa7602203bd4e93dcd72343a9dfe60b36d7e0deaaaffe7676ef6083e839cd2451aac7f36", + "vendorField": "Transaction 23", + "id": "52a06db107d480e222273d1c8430e711e4fbeec55524d463a22b41dd5b61dac4" + }, + "verified": false, + "id": "52a06db107d480e222273d1c8430e711e4fbeec55524d463a22b41dd5b61dac4", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APutnAcu2uWwrTxR5JhY8FKYwFJvU14hLs", + "type": 0, + "vendorField": "Transaction 23", + "vendorFieldHex": "5472616e73616374696f6e203233", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f966cc6d0d00932284e8088557cc113378847236cb22151b8bbd10bd9acfaa7602203bd4e93dcd72343a9dfe60b36d7e0deaaaffe7676ef6083e839cd2451aac7f36", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333200c2eb0b00000000000000001762ca3bd2c81b0ee0730b2edfb63dafc7510ffd703045022100cfdefde6923cd0c64d2048d6859dde6ec75b335883043cd547153df64d83e80c0220341247ce11b798fb90cc0a3f49e2413e826d8b020a0dbabfbf7104b8d4829b16", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203332", + "expiration": 0, + "recipientId": "AQnE8juEecusbwxDEqo1f7rdJo8bSsCTuU", + "signature": "3045022100cfdefde6923cd0c64d2048d6859dde6ec75b335883043cd547153df64d83e80c0220341247ce11b798fb90cc0a3f49e2413e826d8b020a0dbabfbf7104b8d4829b16", + "vendorField": "Transaction 32", + "id": "7426f4de247c99f1c86b50b562e81ebebe428df61a6be3082930a7dae741e6cf" + }, + "verified": false, + "id": "7426f4de247c99f1c86b50b562e81ebebe428df61a6be3082930a7dae741e6cf", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AQnE8juEecusbwxDEqo1f7rdJo8bSsCTuU", + "type": 0, + "vendorField": "Transaction 32", + "vendorFieldHex": "5472616e73616374696f6e203332", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100cfdefde6923cd0c64d2048d6859dde6ec75b335883043cd547153df64d83e80c0220341247ce11b798fb90cc0a3f49e2413e826d8b020a0dbabfbf7104b8d4829b16", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b0000000000000000175099ae927c19d292911fcdc5427e5ffbe3fb104030440220177594e9b7d1966081acb422076bf4a585ff2e715667b048d74f63ca7c7ff8e502203b2505cec5776e092bcf6a56bab9fa8198d607f554f7794da9d51aa75282bdf2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2039", + "expiration": 0, + "recipientId": "AP83orNCj2iB7RnDFWqmcB5MtURZR1NgDQ", + "signature": "30440220177594e9b7d1966081acb422076bf4a585ff2e715667b048d74f63ca7c7ff8e502203b2505cec5776e092bcf6a56bab9fa8198d607f554f7794da9d51aa75282bdf2", + "vendorField": "Transaction 9", + "id": "8f6a9db646930fcd84ce680c0b557c981e69c9b3b130316a4d689e69c17cd19e" + }, + "verified": false, + "id": "8f6a9db646930fcd84ce680c0b557c981e69c9b3b130316a4d689e69c17cd19e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AP83orNCj2iB7RnDFWqmcB5MtURZR1NgDQ", + "type": 0, + "vendorField": "Transaction 9", + "vendorFieldHex": "5472616e73616374696f6e2039", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220177594e9b7d1966081acb422076bf4a585ff2e715667b048d74f63ca7c7ff8e502203b2505cec5776e092bcf6a56bab9fa8198d607f554f7794da9d51aa75282bdf2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b0000000000000000174a0005e4612043b8a2f0b8d6a4fddf19a4c5a4103044022051cd54f7d947ef4f7f673ae2af5b6410c109a9fe788cd99c0273d0effdf6109702206705b4362c7fdbc57b10afac5e98ad274686de1d30ac3859d6cd73241e3c1851", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203230", + "expiration": 0, + "recipientId": "ANX9gHwmuvnvx8n8HTor42bB498xyiW6iY", + "signature": "3044022051cd54f7d947ef4f7f673ae2af5b6410c109a9fe788cd99c0273d0effdf6109702206705b4362c7fdbc57b10afac5e98ad274686de1d30ac3859d6cd73241e3c1851", + "vendorField": "Transaction 20", + "id": "3c6bfe5f3129853c71a8c3d4783c57962ee0e4a5dd89a5373d66d24dae54ac51" + }, + "verified": false, + "id": "3c6bfe5f3129853c71a8c3d4783c57962ee0e4a5dd89a5373d66d24dae54ac51", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ANX9gHwmuvnvx8n8HTor42bB498xyiW6iY", + "type": 0, + "vendorField": "Transaction 20", + "vendorFieldHex": "5472616e73616374696f6e203230", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022051cd54f7d947ef4f7f673ae2af5b6410c109a9fe788cd99c0273d0effdf6109702206705b4362c7fdbc57b10afac5e98ad274686de1d30ac3859d6cd73241e3c1851", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b0000000000000000177c6239437aa46aa5ef2075666ca4297eb9f8410f30440220185d5b31db25b2feb61564f36d57262e2c86826dd71d0100f8fe7e208aecfa2002200c38836a96b603cef11d506ae4fd6604b364197a342dd3ba6bf4944d1b1a7985", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203131", + "expiration": 0, + "recipientId": "AT7Z6zBK89EBgdy1YbokYP9L7qwrUSbKsa", + "signature": "30440220185d5b31db25b2feb61564f36d57262e2c86826dd71d0100f8fe7e208aecfa2002200c38836a96b603cef11d506ae4fd6604b364197a342dd3ba6bf4944d1b1a7985", + "vendorField": "Transaction 11", + "id": "d18814a5a84ae0a71f41f23178bba5945ccec1f0a9f5962b2367984d3e112d5f" + }, + "verified": false, + "id": "d18814a5a84ae0a71f41f23178bba5945ccec1f0a9f5962b2367984d3e112d5f", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AT7Z6zBK89EBgdy1YbokYP9L7qwrUSbKsa", + "type": 0, + "vendorField": "Transaction 11", + "vendorFieldHex": "5472616e73616374696f6e203131", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220185d5b31db25b2feb61564f36d57262e2c86826dd71d0100f8fe7e208aecfa2002200c38836a96b603cef11d506ae4fd6604b364197a342dd3ba6bf4944d1b1a7985", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b0000000000000000177fe700020debbafcaf8f8c1a862289916a11fb693045022100b47e7694ac15badd2f31e1cdac3be26dc454902a0855d8c989246c31126631c5022049691f87c1c178db989a6d8c1f42e24ba4ae1e767163378afb9a028112c37500", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203232", + "expiration": 0, + "recipientId": "ATSAC2NA4ZHGrUjsg5swPZzKYPifeAuJK8", + "signature": "3045022100b47e7694ac15badd2f31e1cdac3be26dc454902a0855d8c989246c31126631c5022049691f87c1c178db989a6d8c1f42e24ba4ae1e767163378afb9a028112c37500", + "vendorField": "Transaction 22", + "id": "81b6af8f3051eb134c99f77eb53ce2df26d6af1de6b3153ab9ae6dab1443dc84" + }, + "verified": false, + "id": "81b6af8f3051eb134c99f77eb53ce2df26d6af1de6b3153ab9ae6dab1443dc84", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ATSAC2NA4ZHGrUjsg5swPZzKYPifeAuJK8", + "type": 0, + "vendorField": "Transaction 22", + "vendorFieldHex": "5472616e73616374696f6e203232", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b47e7694ac15badd2f31e1cdac3be26dc454902a0855d8c989246c31126631c5022049691f87c1c178db989a6d8c1f42e24ba4ae1e767163378afb9a028112c37500", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b0000000000000000174c2241fe6d8f9d5d11fad9462c878784095518df304402205f405a4cd637263fe0a168b9ceaa848c96db344ecd7b16ef0263e2db7d9c3020022063978a6a0b77c796a279e404d8d736e99810205517f797e5787f10db7ed469d9", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203136", + "expiration": 0, + "recipientId": "ANiS3MGiAka88wftSzEWpwqEaKd3u7EPm8", + "signature": "304402205f405a4cd637263fe0a168b9ceaa848c96db344ecd7b16ef0263e2db7d9c3020022063978a6a0b77c796a279e404d8d736e99810205517f797e5787f10db7ed469d9", + "vendorField": "Transaction 16", + "id": "75f000a3d5e31b1450da46631f8d6007fe30c93c8d2333ceec06003f7141aa4e" + }, + "verified": false, + "id": "75f000a3d5e31b1450da46631f8d6007fe30c93c8d2333ceec06003f7141aa4e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ANiS3MGiAka88wftSzEWpwqEaKd3u7EPm8", + "type": 0, + "vendorField": "Transaction 16", + "vendorFieldHex": "5472616e73616374696f6e203136", + "amount": "200000000", + "fee": "10000000", + "signature": "304402205f405a4cd637263fe0a168b9ceaa848c96db344ecd7b16ef0263e2db7d9c3020022063978a6a0b77c796a279e404d8d736e99810205517f797e5787f10db7ed469d9", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333500c2eb0b00000000000000001711beac93f240795e4b480aebc0103bee86580ba33045022100b9c5d38b2eb6a7cd625ca8d4d9f9856a95516c9eb0971bb77ce71e6f2c3955b5022029d762f117a27b4ec61abd6b9f929e4b95bff88b687a8fbcd424d51a958bb7aa", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203335", + "expiration": 0, + "recipientId": "AHPhZ2icNbffxDy2WMSPA4iM3TK19ZWcn3", + "signature": "3045022100b9c5d38b2eb6a7cd625ca8d4d9f9856a95516c9eb0971bb77ce71e6f2c3955b5022029d762f117a27b4ec61abd6b9f929e4b95bff88b687a8fbcd424d51a958bb7aa", + "vendorField": "Transaction 35", + "id": "5562cee9bffef6452f6e1a610ce772ed3f2fff0315bc56874eb63c635e45fed2" + }, + "verified": false, + "id": "5562cee9bffef6452f6e1a610ce772ed3f2fff0315bc56874eb63c635e45fed2", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AHPhZ2icNbffxDy2WMSPA4iM3TK19ZWcn3", + "type": 0, + "vendorField": "Transaction 35", + "vendorFieldHex": "5472616e73616374696f6e203335", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b9c5d38b2eb6a7cd625ca8d4d9f9856a95516c9eb0971bb77ce71e6f2c3955b5022029d762f117a27b4ec61abd6b9f929e4b95bff88b687a8fbcd424d51a958bb7aa", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b000000000000000017a4fda16aad9000bd4fadbecca4d3a0a2deb0103d3044022070700d8de3ecac8750cf3eb624972cb1b9395eb9cc06871c59392ffad2b879b80220161eaa96bb92ce95c345981866ca67dedd1df04a137e27b7cbbed5b236293539", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203330", + "expiration": 0, + "recipientId": "AWpGJWwKfyyEFL2mNNvSM8QiayjEvF6vrh", + "signature": "3044022070700d8de3ecac8750cf3eb624972cb1b9395eb9cc06871c59392ffad2b879b80220161eaa96bb92ce95c345981866ca67dedd1df04a137e27b7cbbed5b236293539", + "vendorField": "Transaction 30", + "id": "f5c2d1ad717255da737818b91dfb4c72096b8680948b2a1494afd7812e4dfe36" + }, + "verified": false, + "id": "f5c2d1ad717255da737818b91dfb4c72096b8680948b2a1494afd7812e4dfe36", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AWpGJWwKfyyEFL2mNNvSM8QiayjEvF6vrh", + "type": 0, + "vendorField": "Transaction 30", + "vendorFieldHex": "5472616e73616374696f6e203330", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022070700d8de3ecac8750cf3eb624972cb1b9395eb9cc06871c59392ffad2b879b80220161eaa96bb92ce95c345981866ca67dedd1df04a137e27b7cbbed5b236293539", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b000000000000000017840121097caceef4c5a46fbe679ba6ecbc3bc05e30450221009b1ac5b9b02b87a1cec17e945e70ec1c1940b6d48d09679029dbef496f8d031c0220567914580441466a1339d92d99cf0893234ed6b5e7b9f00407e6e3bd972051b7", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2031", + "expiration": 0, + "recipientId": "ATorCGV5D9XdjDLLrgaNiJeTTnZ8mQ5da4", + "signature": "30450221009b1ac5b9b02b87a1cec17e945e70ec1c1940b6d48d09679029dbef496f8d031c0220567914580441466a1339d92d99cf0893234ed6b5e7b9f00407e6e3bd972051b7", + "vendorField": "Transaction 1", + "id": "a1bc69dd7a02161360d038d1f3ce6de02cabcb883d5d453251f35ee9c68e7542" + }, + "verified": false, + "id": "a1bc69dd7a02161360d038d1f3ce6de02cabcb883d5d453251f35ee9c68e7542", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ATorCGV5D9XdjDLLrgaNiJeTTnZ8mQ5da4", + "type": 0, + "vendorField": "Transaction 1", + "vendorFieldHex": "5472616e73616374696f6e2031", + "amount": "200000000", + "fee": "10000000", + "signature": "30450221009b1ac5b9b02b87a1cec17e945e70ec1c1940b6d48d09679029dbef496f8d031c0220567914580441466a1339d92d99cf0893234ed6b5e7b9f00407e6e3bd972051b7", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b000000000000000017822df13e7b00e0f671a7f120a8f78bd78eb31adf3044022059f6d5aa1a5e59b9d5b378ed40ab3d109f175d97c8c2cc1e0fa98ff45ca93ef50220056f0a2fe0c3b6fafdb3eae2c49ae05ac6cb62a3b6b2571dd45d1774b90bf384", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203139", + "expiration": 0, + "recipientId": "ATeCXZrxPo1YzX7vexsDH86wHvuyRngphs", + "signature": "3044022059f6d5aa1a5e59b9d5b378ed40ab3d109f175d97c8c2cc1e0fa98ff45ca93ef50220056f0a2fe0c3b6fafdb3eae2c49ae05ac6cb62a3b6b2571dd45d1774b90bf384", + "vendorField": "Transaction 19", + "id": "c26bd597e820d7abfb5d8218f1738b82ec71f05f1eb50a3b66fc7cac8c90514f" + }, + "verified": false, + "id": "c26bd597e820d7abfb5d8218f1738b82ec71f05f1eb50a3b66fc7cac8c90514f", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ATeCXZrxPo1YzX7vexsDH86wHvuyRngphs", + "type": 0, + "vendorField": "Transaction 19", + "vendorFieldHex": "5472616e73616374696f6e203139", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022059f6d5aa1a5e59b9d5b378ed40ab3d109f175d97c8c2cc1e0fa98ff45ca93ef50220056f0a2fe0c3b6fafdb3eae2c49ae05ac6cb62a3b6b2571dd45d1774b90bf384", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b000000000000000017efad13da9347cbbc96683505e808c8ed14d46dc230440220538781f8c1a5587feda6b1333b86d979360a9f53cf93b29230c13416dad81634022019daed2374f9c6d767635413bba689ee3ca28b308c600f86d1a5fde4080164c7", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203232", + "expiration": 0, + "recipientId": "AddAUz62XrVRBUztnE5Z4cka99Azkdhz9u", + "signature": "30440220538781f8c1a5587feda6b1333b86d979360a9f53cf93b29230c13416dad81634022019daed2374f9c6d767635413bba689ee3ca28b308c600f86d1a5fde4080164c7", + "vendorField": "Transaction 22", + "id": "56f375bcd1d8f7c28574c571dce9604ddd18e34ff2b84eaefb61a84a972565d6" + }, + "verified": false, + "id": "56f375bcd1d8f7c28574c571dce9604ddd18e34ff2b84eaefb61a84a972565d6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AddAUz62XrVRBUztnE5Z4cka99Azkdhz9u", + "type": 0, + "vendorField": "Transaction 22", + "vendorFieldHex": "5472616e73616374696f6e203232", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220538781f8c1a5587feda6b1333b86d979360a9f53cf93b29230c13416dad81634022019daed2374f9c6d767635413bba689ee3ca28b308c600f86d1a5fde4080164c7", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b0000000000000000171217f283de4bcf1b28e1e05c5320373f9cdc195630450221008fe76370d9968412848282580eb9a8c253d027fc4e610673857c15271246774b022003c249b93d51d344c2c92435c6adf782dff83da7a38b37445c7e0f007fdea786", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2036", + "expiration": 0, + "recipientId": "AHRYVqhuLo2SwR6YymAMAjQVEBDYDapRKV", + "signature": "30450221008fe76370d9968412848282580eb9a8c253d027fc4e610673857c15271246774b022003c249b93d51d344c2c92435c6adf782dff83da7a38b37445c7e0f007fdea786", + "vendorField": "Transaction 6", + "id": "09dc531495fcc3fc6cbcb2bebeb1e683026a030b6a74bb33f12edd84b78b8dbd" + }, + "verified": false, + "id": "09dc531495fcc3fc6cbcb2bebeb1e683026a030b6a74bb33f12edd84b78b8dbd", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AHRYVqhuLo2SwR6YymAMAjQVEBDYDapRKV", + "type": 0, + "vendorField": "Transaction 6", + "vendorFieldHex": "5472616e73616374696f6e2036", + "amount": "200000000", + "fee": "10000000", + "signature": "30450221008fe76370d9968412848282580eb9a8c253d027fc4e610673857c15271246774b022003c249b93d51d344c2c92435c6adf782dff83da7a38b37445c7e0f007fdea786", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333100c2eb0b0000000000000000179a9e5009b0145145d954df555acf50b36b0469443045022100b3861e0c3333c7bef777d9b3342749cabeb006d898c028c39cc331f17d8e379702207eebde356b9f3f5dc5c2f9db0f46fb1fd2e350ec796d023a065c8dd23faf4bb1", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203331", + "expiration": 0, + "recipientId": "AVsRMo7Q1CH3ni1miN8bcxNEfD78PDqsaA", + "signature": "3045022100b3861e0c3333c7bef777d9b3342749cabeb006d898c028c39cc331f17d8e379702207eebde356b9f3f5dc5c2f9db0f46fb1fd2e350ec796d023a065c8dd23faf4bb1", + "vendorField": "Transaction 31", + "id": "9baff7e39223d0b95ae87569398eb4237ba6d8132ac29d2ae610358945dd74ec" + }, + "verified": false, + "id": "9baff7e39223d0b95ae87569398eb4237ba6d8132ac29d2ae610358945dd74ec", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AVsRMo7Q1CH3ni1miN8bcxNEfD78PDqsaA", + "type": 0, + "vendorField": "Transaction 31", + "vendorFieldHex": "5472616e73616374696f6e203331", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b3861e0c3333c7bef777d9b3342749cabeb006d898c028c39cc331f17d8e379702207eebde356b9f3f5dc5c2f9db0f46fb1fd2e350ec796d023a065c8dd23faf4bb1", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b000000000000000017305bcd10e47d011cc8a30c8680a09bb192253ff23045022100cf711a506ab8e2abf5bf7323c5e5ac51776fc7778845dc7f9686286d7044f20302200bc0fc09b71567a83447983623f45266a68d8a6428dd3d18acc82a042945251f", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203130", + "expiration": 0, + "recipientId": "ALBa49tysfKQQuxUTY7CYGMGSqSznWJxE8", + "signature": "3045022100cf711a506ab8e2abf5bf7323c5e5ac51776fc7778845dc7f9686286d7044f20302200bc0fc09b71567a83447983623f45266a68d8a6428dd3d18acc82a042945251f", + "vendorField": "Transaction 10", + "id": "46e2f9f86f01ebeb373fd1b73a14f58ef2a067e279fe9886c4b2702b5bdb61eb" + }, + "verified": false, + "id": "46e2f9f86f01ebeb373fd1b73a14f58ef2a067e279fe9886c4b2702b5bdb61eb", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ALBa49tysfKQQuxUTY7CYGMGSqSznWJxE8", + "type": 0, + "vendorField": "Transaction 10", + "vendorFieldHex": "5472616e73616374696f6e203130", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100cf711a506ab8e2abf5bf7323c5e5ac51776fc7778845dc7f9686286d7044f20302200bc0fc09b71567a83447983623f45266a68d8a6428dd3d18acc82a042945251f", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b00000000000000001747ba8f15f9b7d8f3a4f7d450d5544b985692110c3045022100c1657829e9bf119301eb33228787b438aed6f3f33b61e3c174080dacfb9990ad022022759aaa39ee30254c272d8015b4fb33fd466e830f9c484e530f4169acc17b85", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203239", + "expiration": 0, + "recipientId": "ANK97TdSNRZ1jypr8s3NWZs4JsnqWTpiPi", + "signature": "3045022100c1657829e9bf119301eb33228787b438aed6f3f33b61e3c174080dacfb9990ad022022759aaa39ee30254c272d8015b4fb33fd466e830f9c484e530f4169acc17b85", + "vendorField": "Transaction 29", + "id": "c76840b93e2628d8660d39eeb512c19d814730b540bd8d94bda82c71d5c7703a" + }, + "verified": false, + "id": "c76840b93e2628d8660d39eeb512c19d814730b540bd8d94bda82c71d5c7703a", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ANK97TdSNRZ1jypr8s3NWZs4JsnqWTpiPi", + "type": 0, + "vendorField": "Transaction 29", + "vendorFieldHex": "5472616e73616374696f6e203239", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c1657829e9bf119301eb33228787b438aed6f3f33b61e3c174080dacfb9990ad022022759aaa39ee30254c272d8015b4fb33fd466e830f9c484e530f4169acc17b85", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b00000000000000001779e55b60c0e72d3d785bebbd33df99d6bea7af843044022052a46f491850b93b1520071b6f7b6525b97b604fa7e93f8172eeaf30baad247802203ceb22389721211041ccaaff83c9d0279c81e25afe632bb2e254f35e521cb94e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2033", + "expiration": 0, + "recipientId": "AStQAi7nWidJxZRUUGtLqUPJMu6KsmDVXk", + "signature": "3044022052a46f491850b93b1520071b6f7b6525b97b604fa7e93f8172eeaf30baad247802203ceb22389721211041ccaaff83c9d0279c81e25afe632bb2e254f35e521cb94e", + "vendorField": "Transaction 3", + "id": "e21215785d7334a5943fbfb18515dbf84968a69c8788d62eb4f50441faddcfef" + }, + "verified": false, + "id": "e21215785d7334a5943fbfb18515dbf84968a69c8788d62eb4f50441faddcfef", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AStQAi7nWidJxZRUUGtLqUPJMu6KsmDVXk", + "type": 0, + "vendorField": "Transaction 3", + "vendorFieldHex": "5472616e73616374696f6e2033", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022052a46f491850b93b1520071b6f7b6525b97b604fa7e93f8172eeaf30baad247802203ceb22389721211041ccaaff83c9d0279c81e25afe632bb2e254f35e521cb94e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b000000000000000017cf09b439324369cd49096fd2746367081689895c3045022100b7e1e1bc99c5d3144bce62785d82189232bd332afb98732b51f80747dcd2701302207caf6782b25e97c47eb6ea1d436f84b528b3d8f3af7998625b74e68845de7f6a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203231", + "expiration": 0, + "recipientId": "Aaeb9TDVxUkQKfKVkSkDZqUfUxbXp4oakc", + "signature": "3045022100b7e1e1bc99c5d3144bce62785d82189232bd332afb98732b51f80747dcd2701302207caf6782b25e97c47eb6ea1d436f84b528b3d8f3af7998625b74e68845de7f6a", + "vendorField": "Transaction 21", + "id": "f1d7f48249c928d16aff4e23b16737004e9dfb5c5d53f7fa42a3bb6a3f97b78d" + }, + "verified": false, + "id": "f1d7f48249c928d16aff4e23b16737004e9dfb5c5d53f7fa42a3bb6a3f97b78d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Aaeb9TDVxUkQKfKVkSkDZqUfUxbXp4oakc", + "type": 0, + "vendorField": "Transaction 21", + "vendorFieldHex": "5472616e73616374696f6e203231", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b7e1e1bc99c5d3144bce62785d82189232bd332afb98732b51f80747dcd2701302207caf6782b25e97c47eb6ea1d436f84b528b3d8f3af7998625b74e68845de7f6a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b000000000000000017ebb33c4df59859924201cbb64ced6bef3adbc5a43045022100f7a8046fd9560543185d153cc9ee7785a295b2b32357d635131cc242dc42749d02204fb0b2238ffca4fc4af42607f435ece3fa35ade4d6bfeff6ffeee817f5498412", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203130", + "expiration": 0, + "recipientId": "AdG9A5pFFrLhyZnwaJ7zNZpKHCwhVySGnx", + "signature": "3045022100f7a8046fd9560543185d153cc9ee7785a295b2b32357d635131cc242dc42749d02204fb0b2238ffca4fc4af42607f435ece3fa35ade4d6bfeff6ffeee817f5498412", + "vendorField": "Transaction 10", + "id": "bb2d7328394c1ecaee73984a73e726dc2803a2de06809f1a74fd2948354fe062" + }, + "verified": false, + "id": "bb2d7328394c1ecaee73984a73e726dc2803a2de06809f1a74fd2948354fe062", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AdG9A5pFFrLhyZnwaJ7zNZpKHCwhVySGnx", + "type": 0, + "vendorField": "Transaction 10", + "vendorFieldHex": "5472616e73616374696f6e203130", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f7a8046fd9560543185d153cc9ee7785a295b2b32357d635131cc242dc42749d02204fb0b2238ffca4fc4af42607f435ece3fa35ade4d6bfeff6ffeee817f5498412", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b000000000000000017ec4d31936cd27e442479e0b3fcb0085c66e150173045022100ec3d0752a914671cb143dc3c3020c47ccb72876b268c0ef1d4d82fc2ee3f000c02207e7e8bcee0a10e659e350af5ff00e5c937f9ed1f016004d9841d988fc162e237", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2033", + "expiration": 0, + "recipientId": "AdKKbHAczw24UU9VTeNByKo6eipUfbVvW7", + "signature": "3045022100ec3d0752a914671cb143dc3c3020c47ccb72876b268c0ef1d4d82fc2ee3f000c02207e7e8bcee0a10e659e350af5ff00e5c937f9ed1f016004d9841d988fc162e237", + "vendorField": "Transaction 3", + "id": "f49b25cb08cc808db7e93dcbb517fec13a96e98307255147d88fd594a2ddc022" + }, + "verified": false, + "id": "f49b25cb08cc808db7e93dcbb517fec13a96e98307255147d88fd594a2ddc022", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AdKKbHAczw24UU9VTeNByKo6eipUfbVvW7", + "type": 0, + "vendorField": "Transaction 3", + "vendorFieldHex": "5472616e73616374696f6e2033", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ec3d0752a914671cb143dc3c3020c47ccb72876b268c0ef1d4d82fc2ee3f000c02207e7e8bcee0a10e659e350af5ff00e5c937f9ed1f016004d9841d988fc162e237", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b00000000000000001784de56a2ae261edbf5713002f4a606030a5488a7304402206c8ffd3d3c28c0e2873e526b774705ae81fff2ee4db765966b6c7865ea223d590220225a29f3fa0f2adc23a6e1e16ee3cd5a48b5d8bd6c6baa49321b720d8646d35e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2035", + "expiration": 0, + "recipientId": "ATtRCAUYrimPzhXvwNHfpQErC3G9rv6HSE", + "signature": "304402206c8ffd3d3c28c0e2873e526b774705ae81fff2ee4db765966b6c7865ea223d590220225a29f3fa0f2adc23a6e1e16ee3cd5a48b5d8bd6c6baa49321b720d8646d35e", + "vendorField": "Transaction 5", + "id": "f8ec27684c01792d0ae427cc8590b996e7f6a7f8445e6e70bec7f63ac0e4b5d0" + }, + "verified": false, + "id": "f8ec27684c01792d0ae427cc8590b996e7f6a7f8445e6e70bec7f63ac0e4b5d0", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ATtRCAUYrimPzhXvwNHfpQErC3G9rv6HSE", + "type": 0, + "vendorField": "Transaction 5", + "vendorFieldHex": "5472616e73616374696f6e2035", + "amount": "200000000", + "fee": "10000000", + "signature": "304402206c8ffd3d3c28c0e2873e526b774705ae81fff2ee4db765966b6c7865ea223d590220225a29f3fa0f2adc23a6e1e16ee3cd5a48b5d8bd6c6baa49321b720d8646d35e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333400c2eb0b00000000000000001768889616a3bae1ed2ef024f0223ea0b605354ab130450221008e576c71758d736f6cd200ef8faf4f7c9c6419328e46402b4e01f390cf997a0f022068a6a539caf98a7e89ca703258b8bbbb0b70aebd3bc8bdc67d8a8705ae23b791", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203334", + "expiration": 0, + "recipientId": "ARJbYdVb91atBUMNah8XBfsbUfWjwnPW8d", + "signature": "30450221008e576c71758d736f6cd200ef8faf4f7c9c6419328e46402b4e01f390cf997a0f022068a6a539caf98a7e89ca703258b8bbbb0b70aebd3bc8bdc67d8a8705ae23b791", + "vendorField": "Transaction 34", + "id": "d7d564bad2a69a7d0f36d335ef8e3bd01e8eea42b6270e721feeeee6c8a39e07" + }, + "verified": false, + "id": "d7d564bad2a69a7d0f36d335ef8e3bd01e8eea42b6270e721feeeee6c8a39e07", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ARJbYdVb91atBUMNah8XBfsbUfWjwnPW8d", + "type": 0, + "vendorField": "Transaction 34", + "vendorFieldHex": "5472616e73616374696f6e203334", + "amount": "200000000", + "fee": "10000000", + "signature": "30450221008e576c71758d736f6cd200ef8faf4f7c9c6419328e46402b4e01f390cf997a0f022068a6a539caf98a7e89ca703258b8bbbb0b70aebd3bc8bdc67d8a8705ae23b791", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b00000000000000001759a274212ccfe83a17c09d8c13ff6645d4a1c5073045022100b082481a85c259ee986c56ce3c2c8ad768408df9232081664f1a37b65e8f0ff702203084e6e60d2fb70e02bb67f581f5c3bdf7e8d4cc3129d220ecca3a81d47b57a0", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203134", + "expiration": 0, + "recipientId": "APwpQ3hkivvRPeLBL64bYtQi7rKFmB69JP", + "signature": "3045022100b082481a85c259ee986c56ce3c2c8ad768408df9232081664f1a37b65e8f0ff702203084e6e60d2fb70e02bb67f581f5c3bdf7e8d4cc3129d220ecca3a81d47b57a0", + "vendorField": "Transaction 14", + "id": "58efe350ca24b4346545e1cb5e5ed413eb0a9316825509927305a5c9722d0637" + }, + "verified": false, + "id": "58efe350ca24b4346545e1cb5e5ed413eb0a9316825509927305a5c9722d0637", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APwpQ3hkivvRPeLBL64bYtQi7rKFmB69JP", + "type": 0, + "vendorField": "Transaction 14", + "vendorFieldHex": "5472616e73616374696f6e203134", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b082481a85c259ee986c56ce3c2c8ad768408df9232081664f1a37b65e8f0ff702203084e6e60d2fb70e02bb67f581f5c3bdf7e8d4cc3129d220ecca3a81d47b57a0", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b0000000000000000176857f83702fef96513cf41985225b48e34436b80304502210095b3298dc543b24a40f72560170e2275ac832f58fcc67eea128f604a8a9af4e602203d653556edbd647b108c0e15254a7f4cd494c8d2f4baf7b8150d47c4a17fabb7", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2033", + "expiration": 0, + "recipientId": "ARHbJgZbBW7W6sxDhKsdHyrUMz3Kho3vtV", + "signature": "304502210095b3298dc543b24a40f72560170e2275ac832f58fcc67eea128f604a8a9af4e602203d653556edbd647b108c0e15254a7f4cd494c8d2f4baf7b8150d47c4a17fabb7", + "vendorField": "Transaction 3", + "id": "c0d0ea323459548205d559f75499583023a794d378a5977e1ffa2a483efc2aa1" + }, + "verified": false, + "id": "c0d0ea323459548205d559f75499583023a794d378a5977e1ffa2a483efc2aa1", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ARHbJgZbBW7W6sxDhKsdHyrUMz3Kho3vtV", + "type": 0, + "vendorField": "Transaction 3", + "vendorFieldHex": "5472616e73616374696f6e2033", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210095b3298dc543b24a40f72560170e2275ac832f58fcc67eea128f604a8a9af4e602203d653556edbd647b108c0e15254a7f4cd494c8d2f4baf7b8150d47c4a17fabb7", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b00000000000000001732da82ee270f1e2c2fc7deb39e6e26871528dd74304402201feda5a75b2a8959d50e83765dea95f7fc9dcb446cac5bd7fae66b19cc1bf2cd0220397c484c6f76fdea8b284e65fb9c5795bb4ed9f98d7ddf079699b75ec27b341d", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203236", + "expiration": 0, + "recipientId": "ALQmCXmay1kLd1ojxrSXkT3hk2VPtuTCAH", + "signature": "304402201feda5a75b2a8959d50e83765dea95f7fc9dcb446cac5bd7fae66b19cc1bf2cd0220397c484c6f76fdea8b284e65fb9c5795bb4ed9f98d7ddf079699b75ec27b341d", + "vendorField": "Transaction 26", + "id": "6509579164c4079156085f5878810800b9bc8162e5979eff4e94997fb9145039" + }, + "verified": false, + "id": "6509579164c4079156085f5878810800b9bc8162e5979eff4e94997fb9145039", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ALQmCXmay1kLd1ojxrSXkT3hk2VPtuTCAH", + "type": 0, + "vendorField": "Transaction 26", + "vendorFieldHex": "5472616e73616374696f6e203236", + "amount": "200000000", + "fee": "10000000", + "signature": "304402201feda5a75b2a8959d50e83765dea95f7fc9dcb446cac5bd7fae66b19cc1bf2cd0220397c484c6f76fdea8b284e65fb9c5795bb4ed9f98d7ddf079699b75ec27b341d", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b000000000000000017556357283c7a590f3f75446a2c57730709abe737304402200d7ddb69d9c65602a44908106a18dd0d6edd3e1380a68b0a11238f1ecf1ab97502203ab0f152aab1edc581863328e07368553641488ac1db0eaedf8c3280e5d18bd5", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2039", + "expiration": 0, + "recipientId": "APZN66kPF4qoYMvYrrsB58y2FjDYGbbfrr", + "signature": "304402200d7ddb69d9c65602a44908106a18dd0d6edd3e1380a68b0a11238f1ecf1ab97502203ab0f152aab1edc581863328e07368553641488ac1db0eaedf8c3280e5d18bd5", + "vendorField": "Transaction 9", + "id": "156d73a47ffd6364b0b9a5e628aa0775284af02ccb2ca25292247beaa43401d9" + }, + "verified": false, + "id": "156d73a47ffd6364b0b9a5e628aa0775284af02ccb2ca25292247beaa43401d9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APZN66kPF4qoYMvYrrsB58y2FjDYGbbfrr", + "type": 0, + "vendorField": "Transaction 9", + "vendorFieldHex": "5472616e73616374696f6e2039", + "amount": "200000000", + "fee": "10000000", + "signature": "304402200d7ddb69d9c65602a44908106a18dd0d6edd3e1380a68b0a11238f1ecf1ab97502203ab0f152aab1edc581863328e07368553641488ac1db0eaedf8c3280e5d18bd5", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b000000000000000017306330b1af7a053147d782c915501f46a42856703045022100c5409f6d6c89159b29fbc4a5d37f05c868ba78158f1ffab9f0bf10f1040500e4022011bfe8fbae70afcd5d13b24e96d7e077bf1bcae1054cb71a6b343108d2132d50", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2037", + "expiration": 0, + "recipientId": "ALBiuZbozcH3YEc9mbuCZEPj3DFTCiRDuv", + "signature": "3045022100c5409f6d6c89159b29fbc4a5d37f05c868ba78158f1ffab9f0bf10f1040500e4022011bfe8fbae70afcd5d13b24e96d7e077bf1bcae1054cb71a6b343108d2132d50", + "vendorField": "Transaction 7", + "id": "2a29c07a81f38d08626aeee58992d1ab30efb3aba93aa2898042759d0067493c" + }, + "verified": false, + "id": "2a29c07a81f38d08626aeee58992d1ab30efb3aba93aa2898042759d0067493c", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ALBiuZbozcH3YEc9mbuCZEPj3DFTCiRDuv", + "type": 0, + "vendorField": "Transaction 7", + "vendorFieldHex": "5472616e73616374696f6e2037", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c5409f6d6c89159b29fbc4a5d37f05c868ba78158f1ffab9f0bf10f1040500e4022011bfe8fbae70afcd5d13b24e96d7e077bf1bcae1054cb71a6b343108d2132d50", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333800c2eb0b0000000000000000173b362003c69ae3902ff355ba3ab40fda6b6a20fe3045022100ceaa0614b91be2d58f959ddaab130b28247cc3bfea0e224e9e6e155198d48ad2022076fde90c50e6e2d420258fac8007f6f6986bb328ac5df947d6a3746499dc722a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203338", + "expiration": 0, + "recipientId": "AMAxMVSqGsifAEjcoZrpDBYRerH128jBkD", + "signature": "3045022100ceaa0614b91be2d58f959ddaab130b28247cc3bfea0e224e9e6e155198d48ad2022076fde90c50e6e2d420258fac8007f6f6986bb328ac5df947d6a3746499dc722a", + "vendorField": "Transaction 38", + "id": "70b6ca1cbc74bd45a0f5d2dd115e44f88f3a80ccd3db62861910cdc68220715c" + }, + "verified": false, + "id": "70b6ca1cbc74bd45a0f5d2dd115e44f88f3a80ccd3db62861910cdc68220715c", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AMAxMVSqGsifAEjcoZrpDBYRerH128jBkD", + "type": 0, + "vendorField": "Transaction 38", + "vendorFieldHex": "5472616e73616374696f6e203338", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ceaa0614b91be2d58f959ddaab130b28247cc3bfea0e224e9e6e155198d48ad2022076fde90c50e6e2d420258fac8007f6f6986bb328ac5df947d6a3746499dc722a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b00000000000000001754f899a104b8f9bf0baf55f4c75e3fcfc0f76670304402206c78646b85d465a51845908d8909275269ba13ffc735730173b0b7025f0affa3022068bb2b5ca22549c13dcd4b6a1bf41001b166f074181f104d44358bd7d4091248", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203134", + "expiration": 0, + "recipientId": "APXADehRbC7bh7VzPAz4CRwEHv7bFBSDjX", + "signature": "304402206c78646b85d465a51845908d8909275269ba13ffc735730173b0b7025f0affa3022068bb2b5ca22549c13dcd4b6a1bf41001b166f074181f104d44358bd7d4091248", + "vendorField": "Transaction 14", + "id": "8b725504cfedce616efc2cb16ccf983d93cadd519156ee6846665a56473a2e5a" + }, + "verified": false, + "id": "8b725504cfedce616efc2cb16ccf983d93cadd519156ee6846665a56473a2e5a", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APXADehRbC7bh7VzPAz4CRwEHv7bFBSDjX", + "type": 0, + "vendorField": "Transaction 14", + "vendorFieldHex": "5472616e73616374696f6e203134", + "amount": "200000000", + "fee": "10000000", + "signature": "304402206c78646b85d465a51845908d8909275269ba13ffc735730173b0b7025f0affa3022068bb2b5ca22549c13dcd4b6a1bf41001b166f074181f104d44358bd7d4091248", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b000000000000000017f0deb3cb96b71fd78fc597f46f72a082008f0a6e3045022100a3253f00c5384bf3f0a63197d389bd640243350f389b4fc6db38cebd4df11b0f0220222592faaff23d0d063ca7db91116525f9374102f69bd8cd9929f543ca8ee391", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2031", + "expiration": 0, + "recipientId": "AdjUcAcDkbjiA4H6YhgGd4mHx2AFdqx8dN", + "signature": "3045022100a3253f00c5384bf3f0a63197d389bd640243350f389b4fc6db38cebd4df11b0f0220222592faaff23d0d063ca7db91116525f9374102f69bd8cd9929f543ca8ee391", + "vendorField": "Transaction 1", + "id": "f90d4faf12215275914f04fc15e2a565ec2e1a9259b85edb177a7dc002530d92" + }, + "verified": false, + "id": "f90d4faf12215275914f04fc15e2a565ec2e1a9259b85edb177a7dc002530d92", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AdjUcAcDkbjiA4H6YhgGd4mHx2AFdqx8dN", + "type": 0, + "vendorField": "Transaction 1", + "vendorFieldHex": "5472616e73616374696f6e2031", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100a3253f00c5384bf3f0a63197d389bd640243350f389b4fc6db38cebd4df11b0f0220222592faaff23d0d063ca7db91116525f9374102f69bd8cd9929f543ca8ee391", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b000000000000000017ca534e2c38361fbbb25d59cea2e835b4fa954f023045022100fe7f435c46def0f2be3966ec5b54e612465e053ab052534035c8e756796548ad0220019c4bc353605ddd555673d490901063b0fdd571ba03068cdee77135f63cd30c", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203234", + "expiration": 0, + "recipientId": "AaDfwQWRKRHDshBb7kakwEEMZvRS2rFgnk", + "signature": "3045022100fe7f435c46def0f2be3966ec5b54e612465e053ab052534035c8e756796548ad0220019c4bc353605ddd555673d490901063b0fdd571ba03068cdee77135f63cd30c", + "vendorField": "Transaction 24", + "id": "92565bd3fedc8f3a5d2a3df145f6ed5c6ef29b2318731e5f499ba1a928a3edb7" + }, + "verified": false, + "id": "92565bd3fedc8f3a5d2a3df145f6ed5c6ef29b2318731e5f499ba1a928a3edb7", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AaDfwQWRKRHDshBb7kakwEEMZvRS2rFgnk", + "type": 0, + "vendorField": "Transaction 24", + "vendorFieldHex": "5472616e73616374696f6e203234", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100fe7f435c46def0f2be3966ec5b54e612465e053ab052534035c8e756796548ad0220019c4bc353605ddd555673d490901063b0fdd571ba03068cdee77135f63cd30c", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333100c2eb0b000000000000000017407c452cd3cb205bb8588c690866a777280a697c3045022100eefcc04db1af1cea3ffbeb1c2bc53a8e0726ce2c4c435e817786db7c6ef632ed02204dd9eac43ea01295a255aed970edbe118be1adb4d3521d12076eb81e77809110", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203331", + "expiration": 0, + "recipientId": "AMeqmDo3yPyUYiE64zYtmKX6hBfysXoSBh", + "signature": "3045022100eefcc04db1af1cea3ffbeb1c2bc53a8e0726ce2c4c435e817786db7c6ef632ed02204dd9eac43ea01295a255aed970edbe118be1adb4d3521d12076eb81e77809110", + "vendorField": "Transaction 31", + "id": "47c9604d80c43c08f9a1a7ea2310125e40583a129c51e626644f9b9857f86bdf" + }, + "verified": false, + "id": "47c9604d80c43c08f9a1a7ea2310125e40583a129c51e626644f9b9857f86bdf", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AMeqmDo3yPyUYiE64zYtmKX6hBfysXoSBh", + "type": 0, + "vendorField": "Transaction 31", + "vendorFieldHex": "5472616e73616374696f6e203331", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100eefcc04db1af1cea3ffbeb1c2bc53a8e0726ce2c4c435e817786db7c6ef632ed02204dd9eac43ea01295a255aed970edbe118be1adb4d3521d12076eb81e77809110", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b000000000000000017b5b7c3d4561bab29857aad0c20b356b429dc8ca83045022100f76c828f2837c9487a4b1bbd5756170479fa3a4ed48dca031cf3f620f62a99ee02204da13730862ccd2b389dee441a7086f0191282ef2c5e2a3d32290a15401b2a90", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203237", + "expiration": 0, + "recipientId": "AYLi6S4DsQMZZvVYxw5dTEBdseHCF3W8n1", + "signature": "3045022100f76c828f2837c9487a4b1bbd5756170479fa3a4ed48dca031cf3f620f62a99ee02204da13730862ccd2b389dee441a7086f0191282ef2c5e2a3d32290a15401b2a90", + "vendorField": "Transaction 27", + "id": "5ea41b33eb0455e55bacbec38898632da7ebb712a0168137550f86a6a3860a6e" + }, + "verified": false, + "id": "5ea41b33eb0455e55bacbec38898632da7ebb712a0168137550f86a6a3860a6e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYLi6S4DsQMZZvVYxw5dTEBdseHCF3W8n1", + "type": 0, + "vendorField": "Transaction 27", + "vendorFieldHex": "5472616e73616374696f6e203237", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f76c828f2837c9487a4b1bbd5756170479fa3a4ed48dca031cf3f620f62a99ee02204da13730862ccd2b389dee441a7086f0191282ef2c5e2a3d32290a15401b2a90", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b000000000000000017711127026596a58864e8346d1b4b43265dd9be163045022100ada204d17b23ad4cc21a645e5cca0a472a2646526242cf8683236b9aaf85a21c0220348f7e2b164ca4c373557b4426c9953e7b14c4dd130c6857d8ee314db0a92305", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203233", + "expiration": 0, + "recipientId": "AS5iYwfzg9ZJskiz1BHePjHJFYH7Titbxu", + "signature": "3045022100ada204d17b23ad4cc21a645e5cca0a472a2646526242cf8683236b9aaf85a21c0220348f7e2b164ca4c373557b4426c9953e7b14c4dd130c6857d8ee314db0a92305", + "vendorField": "Transaction 23", + "id": "9c4fcbc20df740613ed300ec72ba350b14a50b5a9233f488764c2acdb617a90e" + }, + "verified": false, + "id": "9c4fcbc20df740613ed300ec72ba350b14a50b5a9233f488764c2acdb617a90e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AS5iYwfzg9ZJskiz1BHePjHJFYH7Titbxu", + "type": 0, + "vendorField": "Transaction 23", + "vendorFieldHex": "5472616e73616374696f6e203233", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ada204d17b23ad4cc21a645e5cca0a472a2646526242cf8683236b9aaf85a21c0220348f7e2b164ca4c373557b4426c9953e7b14c4dd130c6857d8ee314db0a92305", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b000000000000000017dde2acf4d0798abf18ec499385dd96a948441e3f3044022041d3dc6d101ccaa3acb9622d9876e0ae610246147b19fc0831928933abb6ad5b022055a06fd34646626595f3b4bee99d49a44cc5dba02ede0f6ea35487a4f89511b3", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203138", + "expiration": 0, + "recipientId": "Ac16XbCJYi9DRn1zcxjfYsQ9ndg9N9idzE", + "signature": "3044022041d3dc6d101ccaa3acb9622d9876e0ae610246147b19fc0831928933abb6ad5b022055a06fd34646626595f3b4bee99d49a44cc5dba02ede0f6ea35487a4f89511b3", + "vendorField": "Transaction 18", + "id": "b6a3df24a42270f18ed26524c28154f17c28c5d012ed303e633cc0c820e91fe6" + }, + "verified": false, + "id": "b6a3df24a42270f18ed26524c28154f17c28c5d012ed303e633cc0c820e91fe6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Ac16XbCJYi9DRn1zcxjfYsQ9ndg9N9idzE", + "type": 0, + "vendorField": "Transaction 18", + "vendorFieldHex": "5472616e73616374696f6e203138", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022041d3dc6d101ccaa3acb9622d9876e0ae610246147b19fc0831928933abb6ad5b022055a06fd34646626595f3b4bee99d49a44cc5dba02ede0f6ea35487a4f89511b3", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b000000000000000017a332076f2167b39601fc396d5813f1df168d5ecf3045022100bb096efaa786f0117b07303ead48985ec0ef4fc80de31fe8f456b3e412f5d8bb02200aff57978be91751b75dd0e5816c64b7248d7a5b3c8c3df00130a2c80608da10", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203139", + "expiration": 0, + "recipientId": "AWemiqyoSy1P2BD9u9LD7vZeibdTU12A2U", + "signature": "3045022100bb096efaa786f0117b07303ead48985ec0ef4fc80de31fe8f456b3e412f5d8bb02200aff57978be91751b75dd0e5816c64b7248d7a5b3c8c3df00130a2c80608da10", + "vendorField": "Transaction 19", + "id": "244da0db919c4a1ab4afa095b81066d95d0167a0786aaa552e3a49331ace4c5b" + }, + "verified": false, + "id": "244da0db919c4a1ab4afa095b81066d95d0167a0786aaa552e3a49331ace4c5b", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AWemiqyoSy1P2BD9u9LD7vZeibdTU12A2U", + "type": 0, + "vendorField": "Transaction 19", + "vendorFieldHex": "5472616e73616374696f6e203139", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100bb096efaa786f0117b07303ead48985ec0ef4fc80de31fe8f456b3e412f5d8bb02200aff57978be91751b75dd0e5816c64b7248d7a5b3c8c3df00130a2c80608da10", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b0000000000000000170a30bc64993a755c45f212733eb031a4285d2d123045022100f4245b3606e03770b98a7fa39565d7509a76f810924055da9a6a600c6c01959a022050282b850eee19017814f547ea3587a033a0c93ff5d2bb9beb1cb67cba138646", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203238", + "expiration": 0, + "recipientId": "AGhkneUqnoVePbAvPBeucjVpCmzoV4fwMc", + "signature": "3045022100f4245b3606e03770b98a7fa39565d7509a76f810924055da9a6a600c6c01959a022050282b850eee19017814f547ea3587a033a0c93ff5d2bb9beb1cb67cba138646", + "vendorField": "Transaction 28", + "id": "53ef20d3d76f25e10104a32919f3777ec66a6dc4260782ab5b1e5d168dd468e0" + }, + "verified": false, + "id": "53ef20d3d76f25e10104a32919f3777ec66a6dc4260782ab5b1e5d168dd468e0", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AGhkneUqnoVePbAvPBeucjVpCmzoV4fwMc", + "type": 0, + "vendorField": "Transaction 28", + "vendorFieldHex": "5472616e73616374696f6e203238", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f4245b3606e03770b98a7fa39565d7509a76f810924055da9a6a600c6c01959a022050282b850eee19017814f547ea3587a033a0c93ff5d2bb9beb1cb67cba138646", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b000000000000000017e8cc0e60adc7d87069f3ee3ce27fab54e28b195b3045022100ef03265298c52635384e183286e4624414bd92fdaa0141d05526cb7869ce9e370220718af74f83c06e5fd13aab1ceb21f4440191275a308811ec7c2b707035a7324e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203236", + "expiration": 0, + "recipientId": "Aczns5EGokPonwGjWdjt2MtvzvLAsy66vB", + "signature": "3045022100ef03265298c52635384e183286e4624414bd92fdaa0141d05526cb7869ce9e370220718af74f83c06e5fd13aab1ceb21f4440191275a308811ec7c2b707035a7324e", + "vendorField": "Transaction 26", + "id": "ba1c5e5693587bf489784f8b5673becea7f0bba9004a11f532d994b56f7b501e" + }, + "verified": false, + "id": "ba1c5e5693587bf489784f8b5673becea7f0bba9004a11f532d994b56f7b501e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Aczns5EGokPonwGjWdjt2MtvzvLAsy66vB", + "type": 0, + "vendorField": "Transaction 26", + "vendorFieldHex": "5472616e73616374696f6e203236", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ef03265298c52635384e183286e4624414bd92fdaa0141d05526cb7869ce9e370220718af74f83c06e5fd13aab1ceb21f4440191275a308811ec7c2b707035a7324e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b000000000000000017710ff266d4bc013a13c9fbebc80b0f3669740de53045022100e083480002bafe1234fb63485f21d1be94419ed69ed432ad182939a9a4fc4d780220367bb8bf10c3c5a8a514db19f0ac65cbd6a1791c8be4dbaf78e4ba32624ac4e1", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203235", + "expiration": 0, + "recipientId": "AS5h7BcxxTR9grNxUDAMwzdbsP5oFqGRaN", + "signature": "3045022100e083480002bafe1234fb63485f21d1be94419ed69ed432ad182939a9a4fc4d780220367bb8bf10c3c5a8a514db19f0ac65cbd6a1791c8be4dbaf78e4ba32624ac4e1", + "vendorField": "Transaction 25", + "id": "ba7ca935c4358cb5818e5f768df1821a570657c5a04994c24a32926015428afa" + }, + "verified": false, + "id": "ba7ca935c4358cb5818e5f768df1821a570657c5a04994c24a32926015428afa", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AS5h7BcxxTR9grNxUDAMwzdbsP5oFqGRaN", + "type": 0, + "vendorField": "Transaction 25", + "vendorFieldHex": "5472616e73616374696f6e203235", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e083480002bafe1234fb63485f21d1be94419ed69ed432ad182939a9a4fc4d780220367bb8bf10c3c5a8a514db19f0ac65cbd6a1791c8be4dbaf78e4ba32624ac4e1", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b000000000000000017eeb555fc83eba31e50fc83abb167759703a1491f3045022100f0efae4a199bdaf36375e9904c5ef61d3b9128f3f0f00136762b8c877084a231022012bfb4eda5252c660c926b4a8881c0752bd658d0692701636430ed1bae39aa21", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203235", + "expiration": 0, + "recipientId": "AdY3hbJ4CKGC62v7D5QXvN5YuWZL2vDR1P", + "signature": "3045022100f0efae4a199bdaf36375e9904c5ef61d3b9128f3f0f00136762b8c877084a231022012bfb4eda5252c660c926b4a8881c0752bd658d0692701636430ed1bae39aa21", + "vendorField": "Transaction 25", + "id": "a9d38be7c04605dd4c42f7d764fa65505fc4e62c85032e904fe2f888e37b7c12" + }, + "verified": false, + "id": "a9d38be7c04605dd4c42f7d764fa65505fc4e62c85032e904fe2f888e37b7c12", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AdY3hbJ4CKGC62v7D5QXvN5YuWZL2vDR1P", + "type": 0, + "vendorField": "Transaction 25", + "vendorFieldHex": "5472616e73616374696f6e203235", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f0efae4a199bdaf36375e9904c5ef61d3b9128f3f0f00136762b8c877084a231022012bfb4eda5252c660c926b4a8881c0752bd658d0692701636430ed1bae39aa21", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333600c2eb0b000000000000000017740cdd08268c4e41b44b6c58fa2b281d02ff9d833044022040f3c91ff3012f2f74d93c2dc22459dba4f5c598d74cecdcfc8ccb095bfad9d30220025d0a85f560c0ac71be17806c2758db315537bc9eda6dfc2ab5eb597e8e7a7a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203336", + "expiration": 0, + "recipientId": "ASMVSWsE8tEu8WDt24oTACXJcpxKwuLpKx", + "signature": "3044022040f3c91ff3012f2f74d93c2dc22459dba4f5c598d74cecdcfc8ccb095bfad9d30220025d0a85f560c0ac71be17806c2758db315537bc9eda6dfc2ab5eb597e8e7a7a", + "vendorField": "Transaction 36", + "id": "55cc3155086caa1b2efd11038dcc9c04cb17cb41c8952d5897687bd176a687b5" + }, + "verified": false, + "id": "55cc3155086caa1b2efd11038dcc9c04cb17cb41c8952d5897687bd176a687b5", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ASMVSWsE8tEu8WDt24oTACXJcpxKwuLpKx", + "type": 0, + "vendorField": "Transaction 36", + "vendorFieldHex": "5472616e73616374696f6e203336", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022040f3c91ff3012f2f74d93c2dc22459dba4f5c598d74cecdcfc8ccb095bfad9d30220025d0a85f560c0ac71be17806c2758db315537bc9eda6dfc2ab5eb597e8e7a7a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b000000000000000017cbb5cc21a6b935cc0ab0c8d27ce3aa73ac23fa503045022100ed3e51d4a1e313a45c423ca67ac851119ebff00f2fe7408f2f8276da09d0e18d02200e679fd84c0ea05bcde16e87752744db95c1d6623500dd6e8b3eac91850e72b6", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203237", + "expiration": 0, + "recipientId": "AaLzbwhXj2PESCwtYjBr9N1V2EkH4enX8y", + "signature": "3045022100ed3e51d4a1e313a45c423ca67ac851119ebff00f2fe7408f2f8276da09d0e18d02200e679fd84c0ea05bcde16e87752744db95c1d6623500dd6e8b3eac91850e72b6", + "vendorField": "Transaction 27", + "id": "9dd92590759a2770bbaa297e1ef1a4d7bd85d764532916ed60e7f7f130d1b91b" + }, + "verified": false, + "id": "9dd92590759a2770bbaa297e1ef1a4d7bd85d764532916ed60e7f7f130d1b91b", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AaLzbwhXj2PESCwtYjBr9N1V2EkH4enX8y", + "type": 0, + "vendorField": "Transaction 27", + "vendorFieldHex": "5472616e73616374696f6e203237", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ed3e51d4a1e313a45c423ca67ac851119ebff00f2fe7408f2f8276da09d0e18d02200e679fd84c0ea05bcde16e87752744db95c1d6623500dd6e8b3eac91850e72b6", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333100c2eb0b000000000000000017c798a993d4823466b864f36fb8ea9b30c51ea2403045022100f8ad6ad2a942b64efbfacce82b6491b1389404b9a6633f8e10e0201369d7bf0902207e1c519cd878036d16e57bdd1cfc4e95c706dda677b98db6334beba7a9a491c5", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203331", + "expiration": 0, + "recipientId": "AZyEzqqqUbqgQhvqdKe9Ruq18ScyR95rUh", + "signature": "3045022100f8ad6ad2a942b64efbfacce82b6491b1389404b9a6633f8e10e0201369d7bf0902207e1c519cd878036d16e57bdd1cfc4e95c706dda677b98db6334beba7a9a491c5", + "vendorField": "Transaction 31", + "id": "da50f7ceda83c293913f5e9e55b032074eb5012f91427c32d9c3768711259c09" + }, + "verified": false, + "id": "da50f7ceda83c293913f5e9e55b032074eb5012f91427c32d9c3768711259c09", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AZyEzqqqUbqgQhvqdKe9Ruq18ScyR95rUh", + "type": 0, + "vendorField": "Transaction 31", + "vendorFieldHex": "5472616e73616374696f6e203331", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f8ad6ad2a942b64efbfacce82b6491b1389404b9a6633f8e10e0201369d7bf0902207e1c519cd878036d16e57bdd1cfc4e95c706dda677b98db6334beba7a9a491c5", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b0000000000000000170e98048c8bb91aa761e72235da3de66efdfdbd963045022100b4b976f7303b3d4638a2432d9ecc99c2ea790761274c613f4730991ff1c40cf302203a749033f2dfba987a103e27d55d8280b8e55f0fc121204eae576ef72ea7687f", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2035", + "expiration": 0, + "recipientId": "AH73DZeigYYRwR4msu9AEyuNpPWGit35gz", + "signature": "3045022100b4b976f7303b3d4638a2432d9ecc99c2ea790761274c613f4730991ff1c40cf302203a749033f2dfba987a103e27d55d8280b8e55f0fc121204eae576ef72ea7687f", + "vendorField": "Transaction 5", + "id": "db77b5b2488e5208c861b12224adc1d66b1bfb6754badc66e0d5dd49122edff2" + }, + "verified": false, + "id": "db77b5b2488e5208c861b12224adc1d66b1bfb6754badc66e0d5dd49122edff2", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AH73DZeigYYRwR4msu9AEyuNpPWGit35gz", + "type": 0, + "vendorField": "Transaction 5", + "vendorFieldHex": "5472616e73616374696f6e2035", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b4b976f7303b3d4638a2432d9ecc99c2ea790761274c613f4730991ff1c40cf302203a749033f2dfba987a103e27d55d8280b8e55f0fc121204eae576ef72ea7687f", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b0000000000000000170a654cb7b1392a92a702d532d73557cd81c4ec7430440220533598e3d736748f22a7f96e362b3f33c6ddccddcdd46acebce7ef598864b284022063ed82a97e87173926cf48e69cd1656dfdbcd69c1d5dde0e31b2d7a63828fd6f", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2032", + "expiration": 0, + "recipientId": "AGiqkqmYooLZ9oMEiqusKFLaxS8W5H6mRk", + "signature": "30440220533598e3d736748f22a7f96e362b3f33c6ddccddcdd46acebce7ef598864b284022063ed82a97e87173926cf48e69cd1656dfdbcd69c1d5dde0e31b2d7a63828fd6f", + "vendorField": "Transaction 2", + "id": "1a701201d3bfbcde486e4eae9458e7e23f66ea665db7beb4c4f560488d910f41" + }, + "verified": false, + "id": "1a701201d3bfbcde486e4eae9458e7e23f66ea665db7beb4c4f560488d910f41", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AGiqkqmYooLZ9oMEiqusKFLaxS8W5H6mRk", + "type": 0, + "vendorField": "Transaction 2", + "vendorFieldHex": "5472616e73616374696f6e2032", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220533598e3d736748f22a7f96e362b3f33c6ddccddcdd46acebce7ef598864b284022063ed82a97e87173926cf48e69cd1656dfdbcd69c1d5dde0e31b2d7a63828fd6f", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b000000000000000017b527062800be5242c6dd288e5fc842d323b880df3045022100e03cd37d0dca988195191175306878832915fac89923ea3e6c4db35e424e8b2f02202fac9bfa81869d2bd6bf4e239a58aee6aab5eb795f06e6a20bb41a9ea692eb72", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2032", + "expiration": 0, + "recipientId": "AYHihf7pU41bhvqnJ3TGqeWzb2LM31ux7W", + "signature": "3045022100e03cd37d0dca988195191175306878832915fac89923ea3e6c4db35e424e8b2f02202fac9bfa81869d2bd6bf4e239a58aee6aab5eb795f06e6a20bb41a9ea692eb72", + "vendorField": "Transaction 2", + "id": "def30141e316c3d27a8f8d7dc35513ed0c88f58409bef0ae51d234e392544e80" + }, + "verified": false, + "id": "def30141e316c3d27a8f8d7dc35513ed0c88f58409bef0ae51d234e392544e80", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYHihf7pU41bhvqnJ3TGqeWzb2LM31ux7W", + "type": 0, + "vendorField": "Transaction 2", + "vendorFieldHex": "5472616e73616374696f6e2032", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e03cd37d0dca988195191175306878832915fac89923ea3e6c4db35e424e8b2f02202fac9bfa81869d2bd6bf4e239a58aee6aab5eb795f06e6a20bb41a9ea692eb72", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b000000000000000017e2ede64fdaadc3535d06d208b820edcb175162a63044022024c14e8b3d656e736890766706713f1224c58a461de599201a2cccb586cad23f02202d6fa8ae3ae24f494037827d1e27511073422496b244a25ce610348f52e7c3ff", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203239", + "expiration": 0, + "recipientId": "AcTmMQj6cvfWCAppoCTxEHsou1jZGBXxBv", + "signature": "3044022024c14e8b3d656e736890766706713f1224c58a461de599201a2cccb586cad23f02202d6fa8ae3ae24f494037827d1e27511073422496b244a25ce610348f52e7c3ff", + "vendorField": "Transaction 29", + "id": "d0558448ca870e6d57d1badeaa7573988bbe3aa0f4f3e2d563eb5cb14fffb5e9" + }, + "verified": false, + "id": "d0558448ca870e6d57d1badeaa7573988bbe3aa0f4f3e2d563eb5cb14fffb5e9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AcTmMQj6cvfWCAppoCTxEHsou1jZGBXxBv", + "type": 0, + "vendorField": "Transaction 29", + "vendorFieldHex": "5472616e73616374696f6e203239", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022024c14e8b3d656e736890766706713f1224c58a461de599201a2cccb586cad23f02202d6fa8ae3ae24f494037827d1e27511073422496b244a25ce610348f52e7c3ff", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333300c2eb0b000000000000000017424f8c905f2c4533176b88af2cd8cbd16bcb1d873045022100adb464c5e55b27e3575b3c0d721b69fb99f13299cd6b3191d16fa6b298683b5a02200b920190f7b37551c217ff94570c0b3eebf572a64765f7212ec324f9a2034ed5", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203333", + "expiration": 0, + "recipientId": "AMpVYKrowfTSQsTY8A24PkcNZeh9FHxN9C", + "signature": "3045022100adb464c5e55b27e3575b3c0d721b69fb99f13299cd6b3191d16fa6b298683b5a02200b920190f7b37551c217ff94570c0b3eebf572a64765f7212ec324f9a2034ed5", + "vendorField": "Transaction 33", + "id": "bd0a417ee133d277354dabec0d1cd59db0e1f53aab3a9c197f7403fda378f3d5" + }, + "verified": false, + "id": "bd0a417ee133d277354dabec0d1cd59db0e1f53aab3a9c197f7403fda378f3d5", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AMpVYKrowfTSQsTY8A24PkcNZeh9FHxN9C", + "type": 0, + "vendorField": "Transaction 33", + "vendorFieldHex": "5472616e73616374696f6e203333", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100adb464c5e55b27e3575b3c0d721b69fb99f13299cd6b3191d16fa6b298683b5a02200b920190f7b37551c217ff94570c0b3eebf572a64765f7212ec324f9a2034ed5", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b000000000000000017d3fece02925920575ef90eb229aa631707084bd73045022100b632230b766f89e91e24519a93446f7c8a7c6e68e34c7ca00e1ee41b945d78b302207401fd3c6be212dba6763564f5d25865168b8ff54fe1dc52f5d5b12479f3ded4", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203134", + "expiration": 0, + "recipientId": "Ab6oU7ky1rVN1LdTFvSph9tDVChErPobBt", + "signature": "3045022100b632230b766f89e91e24519a93446f7c8a7c6e68e34c7ca00e1ee41b945d78b302207401fd3c6be212dba6763564f5d25865168b8ff54fe1dc52f5d5b12479f3ded4", + "vendorField": "Transaction 14", + "id": "ea5f93dc1a8ecef5ca71d8450dbcfb3ba8e55829ba6dd494ee40b844c9ad5f68" + }, + "verified": false, + "id": "ea5f93dc1a8ecef5ca71d8450dbcfb3ba8e55829ba6dd494ee40b844c9ad5f68", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Ab6oU7ky1rVN1LdTFvSph9tDVChErPobBt", + "type": 0, + "vendorField": "Transaction 14", + "vendorFieldHex": "5472616e73616374696f6e203134", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b632230b766f89e91e24519a93446f7c8a7c6e68e34c7ca00e1ee41b945d78b302207401fd3c6be212dba6763564f5d25865168b8ff54fe1dc52f5d5b12479f3ded4", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333200c2eb0b000000000000000017737f4c47c74919c2aa05befa3a17145334fc0b2330440220142eb40c16bbf50d63ee294a9cd87945e7a942c2c3cf852443be583fba20542e022053050ef63794d52a261e531f16cb3f01a118619fd1c4763bd93928eee7fa036b", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203332", + "expiration": 0, + "recipientId": "ASJZrNp1N14Mwj9NGYc8z9qFtiB9CPVcQU", + "signature": "30440220142eb40c16bbf50d63ee294a9cd87945e7a942c2c3cf852443be583fba20542e022053050ef63794d52a261e531f16cb3f01a118619fd1c4763bd93928eee7fa036b", + "vendorField": "Transaction 32", + "id": "644acb276c22a56cdcb86f10870194a37d9a4fc13d20a6d67594bfcfb49ab7bb" + }, + "verified": false, + "id": "644acb276c22a56cdcb86f10870194a37d9a4fc13d20a6d67594bfcfb49ab7bb", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ASJZrNp1N14Mwj9NGYc8z9qFtiB9CPVcQU", + "type": 0, + "vendorField": "Transaction 32", + "vendorFieldHex": "5472616e73616374696f6e203332", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220142eb40c16bbf50d63ee294a9cd87945e7a942c2c3cf852443be583fba20542e022053050ef63794d52a261e531f16cb3f01a118619fd1c4763bd93928eee7fa036b", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b0000000000000000179d2c9324e662ac5cf922b9f5a0bdf806baf737273045022100c3ba0ffcbfa9db3bcef1595e5836b501de96ce676f0aa33fe885d0721292d93502205cfbf32420b07708340c3ed60ffd29a22cd37b3e55abbfcd2aaa118d48e30347", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203233", + "expiration": 0, + "recipientId": "AW6w8j3yW3Gtn6ApFxCHu8fkcaTpVzG9E4", + "signature": "3045022100c3ba0ffcbfa9db3bcef1595e5836b501de96ce676f0aa33fe885d0721292d93502205cfbf32420b07708340c3ed60ffd29a22cd37b3e55abbfcd2aaa118d48e30347", + "vendorField": "Transaction 23", + "id": "0cf9d9ecaac1c3c423539194f58a0a53585d87ea4f583552fba58fee13da3c11" + }, + "verified": false, + "id": "0cf9d9ecaac1c3c423539194f58a0a53585d87ea4f583552fba58fee13da3c11", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AW6w8j3yW3Gtn6ApFxCHu8fkcaTpVzG9E4", + "type": 0, + "vendorField": "Transaction 23", + "vendorFieldHex": "5472616e73616374696f6e203233", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c3ba0ffcbfa9db3bcef1595e5836b501de96ce676f0aa33fe885d0721292d93502205cfbf32420b07708340c3ed60ffd29a22cd37b3e55abbfcd2aaa118d48e30347", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b0000000000000000174d48fbcb340a2691556715b1995e51c360adffe73044022019c6fb89c93e746bf5ec3a2eb1bca072da112663427f2db5778d1f9a7e4b7b22022018f48b791669852e13837236d5eddcfc61fc575c8fd15ceb8dc54304b1edb1a2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2038", + "expiration": 0, + "recipientId": "ANpX7FqMm9ZfwYJFeFhFEAmryXu5vTaUNF", + "signature": "3044022019c6fb89c93e746bf5ec3a2eb1bca072da112663427f2db5778d1f9a7e4b7b22022018f48b791669852e13837236d5eddcfc61fc575c8fd15ceb8dc54304b1edb1a2", + "vendorField": "Transaction 8", + "id": "f6a8bc081c1f8ff8012f3b447f425aab78eb71079b177ac724505cf265ee1fc0" + }, + "verified": false, + "id": "f6a8bc081c1f8ff8012f3b447f425aab78eb71079b177ac724505cf265ee1fc0", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ANpX7FqMm9ZfwYJFeFhFEAmryXu5vTaUNF", + "type": 0, + "vendorField": "Transaction 8", + "vendorFieldHex": "5472616e73616374696f6e2038", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022019c6fb89c93e746bf5ec3a2eb1bca072da112663427f2db5778d1f9a7e4b7b22022018f48b791669852e13837236d5eddcfc61fc575c8fd15ceb8dc54304b1edb1a2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b000000000000000017d190f1e81c972becdaa6d8ac66628d1b5a8e82de304402205547a585292262f56a6534c99685e1786b13812773d132476d09c19cad545a5002204af895618f5cf873edfda3f1a20f46ff8eb40ba1027021f5670b112a1a8d4b1e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203137", + "expiration": 0, + "recipientId": "AasxWYQykcpFSRoVEnU1n7ZSbT8MtP4juo", + "signature": "304402205547a585292262f56a6534c99685e1786b13812773d132476d09c19cad545a5002204af895618f5cf873edfda3f1a20f46ff8eb40ba1027021f5670b112a1a8d4b1e", + "vendorField": "Transaction 17", + "id": "5330521c33ac88a7232e9d9fe111d87f0f02d7e5f92b6acb4253f2a847e9886d" + }, + "verified": false, + "id": "5330521c33ac88a7232e9d9fe111d87f0f02d7e5f92b6acb4253f2a847e9886d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AasxWYQykcpFSRoVEnU1n7ZSbT8MtP4juo", + "type": 0, + "vendorField": "Transaction 17", + "vendorFieldHex": "5472616e73616374696f6e203137", + "amount": "200000000", + "fee": "10000000", + "signature": "304402205547a585292262f56a6534c99685e1786b13812773d132476d09c19cad545a5002204af895618f5cf873edfda3f1a20f46ff8eb40ba1027021f5670b112a1a8d4b1e", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b000000000000000017d25825969c0d9d28ffd5bb1b3478901080edb0de3044022026416f54ae43a24634b705374dfe8cc1abace3c3013ba51eb350d0a02bde75440220726b910bf7304342b5eac57cd19c494cb2ef04b3ae0eba0bed266a26b9348c24", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2036", + "expiration": 0, + "recipientId": "Aax59LCQ2Eeiix3KbbJaBcLiK51w7iy6R4", + "signature": "3044022026416f54ae43a24634b705374dfe8cc1abace3c3013ba51eb350d0a02bde75440220726b910bf7304342b5eac57cd19c494cb2ef04b3ae0eba0bed266a26b9348c24", + "vendorField": "Transaction 6", + "id": "40a66e28f1a9a9fd902c7990b9765662c2442d2ef6ca85ac9ab36baaca488669" + }, + "verified": false, + "id": "40a66e28f1a9a9fd902c7990b9765662c2442d2ef6ca85ac9ab36baaca488669", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Aax59LCQ2Eeiix3KbbJaBcLiK51w7iy6R4", + "type": 0, + "vendorField": "Transaction 6", + "vendorFieldHex": "5472616e73616374696f6e2036", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022026416f54ae43a24634b705374dfe8cc1abace3c3013ba51eb350d0a02bde75440220726b910bf7304342b5eac57cd19c494cb2ef04b3ae0eba0bed266a26b9348c24", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b000000000000000017bbc327b6644dc42d55a8e8f32ffeabac37f742c63045022100f92ba535acd2d4cb2aa14103ad2b2f894cc78167899d16061a85e215f5ec213802206ce4ad145df8663dc16bfa18bc8397daef98c3d2b05f02a1a1630f2ba59157fd", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203136", + "expiration": 0, + "recipientId": "AYtfnzAzEYxK8DKUmZcBwT61jtmPpwFjH8", + "signature": "3045022100f92ba535acd2d4cb2aa14103ad2b2f894cc78167899d16061a85e215f5ec213802206ce4ad145df8663dc16bfa18bc8397daef98c3d2b05f02a1a1630f2ba59157fd", + "vendorField": "Transaction 16", + "id": "49defe929cedf5bac4e47964c97d61a763c8f0b1103e5b1aaa6bbd2b5b2a3d03" + }, + "verified": false, + "id": "49defe929cedf5bac4e47964c97d61a763c8f0b1103e5b1aaa6bbd2b5b2a3d03", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYtfnzAzEYxK8DKUmZcBwT61jtmPpwFjH8", + "type": 0, + "vendorField": "Transaction 16", + "vendorFieldHex": "5472616e73616374696f6e203136", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f92ba535acd2d4cb2aa14103ad2b2f894cc78167899d16061a85e215f5ec213802206ce4ad145df8663dc16bfa18bc8397daef98c3d2b05f02a1a1630f2ba59157fd", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20343000c2eb0b000000000000000017abe730919c31b471ac0dbf9b6f12bad34416f8bb3045022100f9400dabee7c2749136995628b0813cfab1dd763bd7450717cc0af2bb11810560220683de15f7d713affe5e6de9e3321a6248481246a571ec9f96e648e2f1a0ee24a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203430", + "expiration": 0, + "recipientId": "AXSp9eMQ9GDQPfwG8b94QHWEUPs2CnVh8g", + "signature": "3045022100f9400dabee7c2749136995628b0813cfab1dd763bd7450717cc0af2bb11810560220683de15f7d713affe5e6de9e3321a6248481246a571ec9f96e648e2f1a0ee24a", + "vendorField": "Transaction 40", + "id": "0d9ce68fd7204c0e870cbc6ea16c54bbd33f173834efbdb5dc10492afef52230" + }, + "verified": false, + "id": "0d9ce68fd7204c0e870cbc6ea16c54bbd33f173834efbdb5dc10492afef52230", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AXSp9eMQ9GDQPfwG8b94QHWEUPs2CnVh8g", + "type": 0, + "vendorField": "Transaction 40", + "vendorFieldHex": "5472616e73616374696f6e203430", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f9400dabee7c2749136995628b0813cfab1dd763bd7450717cc0af2bb11810560220683de15f7d713affe5e6de9e3321a6248481246a571ec9f96e648e2f1a0ee24a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b000000000000000017527ce40209b488f22a14f1ea2880bbe37c15e5963044022005cb3b17865f2ef8580115395aa593abf9b074a70aa2859c17f5fa1f2b5629fd0220512306967c8f5e33eed952bb3d26de6f451b2c3954198e8db1bb5cb045521c50", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203238", + "expiration": 0, + "recipientId": "APJ2fnPPkt1F2kCVjg6q3eu8VrcQKxhRMm", + "signature": "3044022005cb3b17865f2ef8580115395aa593abf9b074a70aa2859c17f5fa1f2b5629fd0220512306967c8f5e33eed952bb3d26de6f451b2c3954198e8db1bb5cb045521c50", + "vendorField": "Transaction 28", + "id": "d1e887f6b3f3203f9b32203120606fd935452c140721d094308cb0800d3c95a3" + }, + "verified": false, + "id": "d1e887f6b3f3203f9b32203120606fd935452c140721d094308cb0800d3c95a3", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APJ2fnPPkt1F2kCVjg6q3eu8VrcQKxhRMm", + "type": 0, + "vendorField": "Transaction 28", + "vendorFieldHex": "5472616e73616374696f6e203238", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022005cb3b17865f2ef8580115395aa593abf9b074a70aa2859c17f5fa1f2b5629fd0220512306967c8f5e33eed952bb3d26de6f451b2c3954198e8db1bb5cb045521c50", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b000000000000000017e9beb02e59ebcf1e87f5b2990cd5fc8a523ed152304402200746188cd79a980f63a0011884ea6d34d4af34e533ab0bb18e622bc4b1b1b1b102201de516bd3458ada944d0d46eb2295873e5ab5f929c2fb728fb45ad81a53bbc6f", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203231", + "expiration": 0, + "recipientId": "Ad5oXSgxt7eVHjmFTMud9KtFpXhDyXiD7F", + "signature": "304402200746188cd79a980f63a0011884ea6d34d4af34e533ab0bb18e622bc4b1b1b1b102201de516bd3458ada944d0d46eb2295873e5ab5f929c2fb728fb45ad81a53bbc6f", + "vendorField": "Transaction 21", + "id": "35486bab705e07df164eeb939de1d6e771c42d5bb20854f10cf95a6a6a4af764" + }, + "verified": false, + "id": "35486bab705e07df164eeb939de1d6e771c42d5bb20854f10cf95a6a6a4af764", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Ad5oXSgxt7eVHjmFTMud9KtFpXhDyXiD7F", + "type": 0, + "vendorField": "Transaction 21", + "vendorFieldHex": "5472616e73616374696f6e203231", + "amount": "200000000", + "fee": "10000000", + "signature": "304402200746188cd79a980f63a0011884ea6d34d4af34e533ab0bb18e622bc4b1b1b1b102201de516bd3458ada944d0d46eb2295873e5ab5f929c2fb728fb45ad81a53bbc6f", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b0000000000000000173a8aefd16949d537bbc3fe0a4535920af7501aab3044022010cd7a8ce57ce47221da59de3f75e571fe56e86626d9017ca6b80a0ef3a3508b0220078ee6036fcd468eebadd85b0e0a0a6f8dbb952bdc24feef3ca4decf1e058155", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203131", + "expiration": 0, + "recipientId": "AM7RH827Rv9Y4VnzCwsQjf3BW1PpcAwaSL", + "signature": "3044022010cd7a8ce57ce47221da59de3f75e571fe56e86626d9017ca6b80a0ef3a3508b0220078ee6036fcd468eebadd85b0e0a0a6f8dbb952bdc24feef3ca4decf1e058155", + "vendorField": "Transaction 11", + "id": "a30d605b13c94574c88954ab7c8f3526a19eb47b3b8b271c32fa67154eba41f5" + }, + "verified": false, + "id": "a30d605b13c94574c88954ab7c8f3526a19eb47b3b8b271c32fa67154eba41f5", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AM7RH827Rv9Y4VnzCwsQjf3BW1PpcAwaSL", + "type": 0, + "vendorField": "Transaction 11", + "vendorFieldHex": "5472616e73616374696f6e203131", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022010cd7a8ce57ce47221da59de3f75e571fe56e86626d9017ca6b80a0ef3a3508b0220078ee6036fcd468eebadd85b0e0a0a6f8dbb952bdc24feef3ca4decf1e058155", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b000000000000000017fb982d2e09e48f462322061b520f5c4c95567c673045022100b2e14e6c3b68c1d819c8da960c6d47b25bdd25c4856c3e1bd718265b292080c5022040dabf8f95d8e98c962d1733d10b8cfa6749da5b2dd1a521b7778a0306f05d9c", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203132", + "expiration": 0, + "recipientId": "AeiBZ4A1HDwLkDf2kKoBjpaTRH7BZapZ6F", + "signature": "3045022100b2e14e6c3b68c1d819c8da960c6d47b25bdd25c4856c3e1bd718265b292080c5022040dabf8f95d8e98c962d1733d10b8cfa6749da5b2dd1a521b7778a0306f05d9c", + "vendorField": "Transaction 12", + "id": "3050cde59690739f92f84b1f93a5f5bb25723f342fe6eccb0cb42558e80c1025" + }, + "verified": false, + "id": "3050cde59690739f92f84b1f93a5f5bb25723f342fe6eccb0cb42558e80c1025", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AeiBZ4A1HDwLkDf2kKoBjpaTRH7BZapZ6F", + "type": 0, + "vendorField": "Transaction 12", + "vendorFieldHex": "5472616e73616374696f6e203132", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b2e14e6c3b68c1d819c8da960c6d47b25bdd25c4856c3e1bd718265b292080c5022040dabf8f95d8e98c962d1733d10b8cfa6749da5b2dd1a521b7778a0306f05d9c", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b000000000000000017a045594b433e0b2c1f50dd607688a182d624d5a8304402206bae61e2254f416d8be818e16ad5eb508f2f5dadd9ecbca1ffe8ae427721974902204be5a5e1e9103898eb8e8922391fdfaa5c9d49c0015d28d2b7c462fb8f0838cc", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203234", + "expiration": 0, + "recipientId": "AWPJqdXeNXZ6xE2VTx9aym5sSt8dYAUdo7", + "signature": "304402206bae61e2254f416d8be818e16ad5eb508f2f5dadd9ecbca1ffe8ae427721974902204be5a5e1e9103898eb8e8922391fdfaa5c9d49c0015d28d2b7c462fb8f0838cc", + "vendorField": "Transaction 24", + "id": "3978312d18ab98a7f381b338961c591db5c3e2b648fd023c0c739d7de7d8f204" + }, + "verified": false, + "id": "3978312d18ab98a7f381b338961c591db5c3e2b648fd023c0c739d7de7d8f204", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AWPJqdXeNXZ6xE2VTx9aym5sSt8dYAUdo7", + "type": 0, + "vendorField": "Transaction 24", + "vendorFieldHex": "5472616e73616374696f6e203234", + "amount": "200000000", + "fee": "10000000", + "signature": "304402206bae61e2254f416d8be818e16ad5eb508f2f5dadd9ecbca1ffe8ae427721974902204be5a5e1e9103898eb8e8922391fdfaa5c9d49c0015d28d2b7c462fb8f0838cc", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b000000000000000017ae968f9f5c4b0427d350ec8502fb3867c52e79903044022009959bb250bab7e1fd893fe7fd841fb17431892df577953bafb31f16ab20babc02205859d9bef6e191aff9c75f799fd7bdae6d8646ecfadd3d8b29a7c13471dfc3e2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203230", + "expiration": 0, + "recipientId": "AXh1b3HhCcZs91mFA1H7KrBW9UXPUftGqg", + "signature": "3044022009959bb250bab7e1fd893fe7fd841fb17431892df577953bafb31f16ab20babc02205859d9bef6e191aff9c75f799fd7bdae6d8646ecfadd3d8b29a7c13471dfc3e2", + "vendorField": "Transaction 20", + "id": "8e07995a33f7c57997beb07887aa91c1e7e386c75ce865be31fd4e84dd0f6cd3" + }, + "verified": false, + "id": "8e07995a33f7c57997beb07887aa91c1e7e386c75ce865be31fd4e84dd0f6cd3", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AXh1b3HhCcZs91mFA1H7KrBW9UXPUftGqg", + "type": 0, + "vendorField": "Transaction 20", + "vendorFieldHex": "5472616e73616374696f6e203230", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022009959bb250bab7e1fd893fe7fd841fb17431892df577953bafb31f16ab20babc02205859d9bef6e191aff9c75f799fd7bdae6d8646ecfadd3d8b29a7c13471dfc3e2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b000000000000000017d0c946c5e6e51a6c879677042422799a80e6e5fd304402204bdcd333bc3b41557b7b805a89f1e8b77fdb6d499736078ccb48c58f6431621e02202b727f6b6708d9459556c02950a350eb75e804bfdce9fcffb67c8cc95a8e532c", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203232", + "expiration": 0, + "recipientId": "AaoqKLEhoHYZAm5tvxo9tEEVUmA8P98tEc", + "signature": "304402204bdcd333bc3b41557b7b805a89f1e8b77fdb6d499736078ccb48c58f6431621e02202b727f6b6708d9459556c02950a350eb75e804bfdce9fcffb67c8cc95a8e532c", + "vendorField": "Transaction 22", + "id": "b5cd93b7e0b6e659ebf10c0a39237e59a585e18f3c83e77e6f652dd4f73e234d" + }, + "verified": false, + "id": "b5cd93b7e0b6e659ebf10c0a39237e59a585e18f3c83e77e6f652dd4f73e234d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AaoqKLEhoHYZAm5tvxo9tEEVUmA8P98tEc", + "type": 0, + "vendorField": "Transaction 22", + "vendorFieldHex": "5472616e73616374696f6e203232", + "amount": "200000000", + "fee": "10000000", + "signature": "304402204bdcd333bc3b41557b7b805a89f1e8b77fdb6d499736078ccb48c58f6431621e02202b727f6b6708d9459556c02950a350eb75e804bfdce9fcffb67c8cc95a8e532c", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b000000000000000017bdf12bd72e628fa60082963673586e9b96a8725c3045022100ebf7c3d8abefd8fe062fe52d1a226179fe2165f013099b46ba3724c345d51ed202203d8444c63f2d7a0f36bacb1643b33cbd47b02d800d0dd233fbb6e64385779380", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203234", + "expiration": 0, + "recipientId": "AZ6CGdJHTdskyuchUvj9N6WodAPc5LE5V2", + "signature": "3045022100ebf7c3d8abefd8fe062fe52d1a226179fe2165f013099b46ba3724c345d51ed202203d8444c63f2d7a0f36bacb1643b33cbd47b02d800d0dd233fbb6e64385779380", + "vendorField": "Transaction 24", + "id": "459051ee27928fe97d06ab88850f794a60bf8e8cbcb688bb794d38f8e24d428f" + }, + "verified": false, + "id": "459051ee27928fe97d06ab88850f794a60bf8e8cbcb688bb794d38f8e24d428f", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AZ6CGdJHTdskyuchUvj9N6WodAPc5LE5V2", + "type": 0, + "vendorField": "Transaction 24", + "vendorFieldHex": "5472616e73616374696f6e203234", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ebf7c3d8abefd8fe062fe52d1a226179fe2165f013099b46ba3724c345d51ed202203d8444c63f2d7a0f36bacb1643b33cbd47b02d800d0dd233fbb6e64385779380", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b0000000000000000177cb1f05f837c26723f4a3412fb2353226b245a7f3045022100e7b66efac64def1c8001cfb6b68e2b16f85c0f20684a760bc93aae385126d25702200253492c751f8c704eb042192cd524791ead3b5eda6e9afd0e71c58576127fb2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203132", + "expiration": 0, + "recipientId": "AT9CbhbmQRYPmuLe2d6aCVLzTFciN6SWFd", + "signature": "3045022100e7b66efac64def1c8001cfb6b68e2b16f85c0f20684a760bc93aae385126d25702200253492c751f8c704eb042192cd524791ead3b5eda6e9afd0e71c58576127fb2", + "vendorField": "Transaction 12", + "id": "ed5002c4698331b4b3c0a56d19f74dfaf2156acb20457cb3ead7ec3ead3c64e7" + }, + "verified": false, + "id": "ed5002c4698331b4b3c0a56d19f74dfaf2156acb20457cb3ead7ec3ead3c64e7", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AT9CbhbmQRYPmuLe2d6aCVLzTFciN6SWFd", + "type": 0, + "vendorField": "Transaction 12", + "vendorFieldHex": "5472616e73616374696f6e203132", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e7b66efac64def1c8001cfb6b68e2b16f85c0f20684a760bc93aae385126d25702200253492c751f8c704eb042192cd524791ead3b5eda6e9afd0e71c58576127fb2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b000000000000000017ef886d3c11609a79d95ae6f1ec733c0dcd9a6ebe3045022100f5396a022fccf2e70b2eb3873271e9021dfbd5effd706997f704e24ad722d6f502201343d51f913ecbc9a9ea6be1a97e5531f98107132a9ffe6a0e5b860d4333d039", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203330", + "expiration": 0, + "recipientId": "AdcQaSQrQskuN7yrKFcPhV9kkwpCBSgc5z", + "signature": "3045022100f5396a022fccf2e70b2eb3873271e9021dfbd5effd706997f704e24ad722d6f502201343d51f913ecbc9a9ea6be1a97e5531f98107132a9ffe6a0e5b860d4333d039", + "vendorField": "Transaction 30", + "id": "9844e01a0333c7996aa842b0e328a3b76035cf3103416e32116713aadc656f46" + }, + "verified": false, + "id": "9844e01a0333c7996aa842b0e328a3b76035cf3103416e32116713aadc656f46", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AdcQaSQrQskuN7yrKFcPhV9kkwpCBSgc5z", + "type": 0, + "vendorField": "Transaction 30", + "vendorFieldHex": "5472616e73616374696f6e203330", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f5396a022fccf2e70b2eb3873271e9021dfbd5effd706997f704e24ad722d6f502201343d51f913ecbc9a9ea6be1a97e5531f98107132a9ffe6a0e5b860d4333d039", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b000000000000000017841a086dcdb01b5a3688648e467449ff4ccc71473045022100e9221f75315a65c2b7fadba4dd5f668df15f339061b8e862e926fee8d7990e44022005f3a33f1a680fb9822c5a0ccdaee45a8db4c3f2fabe5b81ccd4c0e93ea8daa6", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203135", + "expiration": 0, + "recipientId": "ATpN2csdQKh7TqLYNWuwdVTYrVVWSeabtC", + "signature": "3045022100e9221f75315a65c2b7fadba4dd5f668df15f339061b8e862e926fee8d7990e44022005f3a33f1a680fb9822c5a0ccdaee45a8db4c3f2fabe5b81ccd4c0e93ea8daa6", + "vendorField": "Transaction 15", + "id": "72a1d155ec6b8463c423489cd14996967eec26fd08cd4c35ff67a742b00c7c12" + }, + "verified": false, + "id": "72a1d155ec6b8463c423489cd14996967eec26fd08cd4c35ff67a742b00c7c12", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ATpN2csdQKh7TqLYNWuwdVTYrVVWSeabtC", + "type": 0, + "vendorField": "Transaction 15", + "vendorFieldHex": "5472616e73616374696f6e203135", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e9221f75315a65c2b7fadba4dd5f668df15f339061b8e862e926fee8d7990e44022005f3a33f1a680fb9822c5a0ccdaee45a8db4c3f2fabe5b81ccd4c0e93ea8daa6", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b000000000000000017559f8ef0575e8ade7a3b7040f02f54652e95bc943045022100c21caaecd718c72077f0d09e8ce04293d1ac27473c0545f3e3b1c8d0283b99ab022000c1a1a65dec757dfae0bc41d220a142aeb9ee311f14cbebe6bf977a0cfbe53b", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203139", + "expiration": 0, + "recipientId": "APacE7VarcoLLYwDDnApGiT3Zgm8uZizos", + "signature": "3045022100c21caaecd718c72077f0d09e8ce04293d1ac27473c0545f3e3b1c8d0283b99ab022000c1a1a65dec757dfae0bc41d220a142aeb9ee311f14cbebe6bf977a0cfbe53b", + "vendorField": "Transaction 19", + "id": "363f20cbd56703fbd06227d0103eb0db40ae109c5f2c877fd8a215f572b9d626" + }, + "verified": false, + "id": "363f20cbd56703fbd06227d0103eb0db40ae109c5f2c877fd8a215f572b9d626", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APacE7VarcoLLYwDDnApGiT3Zgm8uZizos", + "type": 0, + "vendorField": "Transaction 19", + "vendorFieldHex": "5472616e73616374696f6e203139", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c21caaecd718c72077f0d09e8ce04293d1ac27473c0545f3e3b1c8d0283b99ab022000c1a1a65dec757dfae0bc41d220a142aeb9ee311f14cbebe6bf977a0cfbe53b", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333800c2eb0b00000000000000001797982b30f6b666ec86b0e2fdc3b08a3b7479cbf0304402202e93971c4bef3ec7a4117177ad3be74b0067dc863d136d14e50e43feb62a6515022044bca36c4e4df2d40bb1f610eaeb0ce814acc0acbeb4c46c876a10d533071619", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203338", + "expiration": 0, + "recipientId": "AVbRyLEdaiKoiYEojRAt3xzP3bXxRwEQKG", + "signature": "304402202e93971c4bef3ec7a4117177ad3be74b0067dc863d136d14e50e43feb62a6515022044bca36c4e4df2d40bb1f610eaeb0ce814acc0acbeb4c46c876a10d533071619", + "vendorField": "Transaction 38", + "id": "0b95146bd6388cc6b678402ff4db5ac0a104647b4bee8762e0c846c3d5f57d98" + }, + "verified": false, + "id": "0b95146bd6388cc6b678402ff4db5ac0a104647b4bee8762e0c846c3d5f57d98", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AVbRyLEdaiKoiYEojRAt3xzP3bXxRwEQKG", + "type": 0, + "vendorField": "Transaction 38", + "vendorFieldHex": "5472616e73616374696f6e203338", + "amount": "200000000", + "fee": "10000000", + "signature": "304402202e93971c4bef3ec7a4117177ad3be74b0067dc863d136d14e50e43feb62a6515022044bca36c4e4df2d40bb1f610eaeb0ce814acc0acbeb4c46c876a10d533071619", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333200c2eb0b000000000000000017b406dcd28615b6787189c1c4e9f6e3348037d8b03045022100d09d4720d0517c00672733b12f328a6a2d98d7ab150bdd810c82df4a0e021df7022066aa615c7b9e481ad31c06c77cb6f3c92018c6ed8c120b2cf6e14320679b5738", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203332", + "expiration": 0, + "recipientId": "AYBmVrBPAFVqpy63Yrov7Sg3TiMR8qnY6k", + "signature": "3045022100d09d4720d0517c00672733b12f328a6a2d98d7ab150bdd810c82df4a0e021df7022066aa615c7b9e481ad31c06c77cb6f3c92018c6ed8c120b2cf6e14320679b5738", + "vendorField": "Transaction 32", + "id": "d765387d4dc54a6362643beaae925500ec0a27a8c06ebf2f61a60e26500bc9c9" + }, + "verified": false, + "id": "d765387d4dc54a6362643beaae925500ec0a27a8c06ebf2f61a60e26500bc9c9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYBmVrBPAFVqpy63Yrov7Sg3TiMR8qnY6k", + "type": 0, + "vendorField": "Transaction 32", + "vendorFieldHex": "5472616e73616374696f6e203332", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100d09d4720d0517c00672733b12f328a6a2d98d7ab150bdd810c82df4a0e021df7022066aa615c7b9e481ad31c06c77cb6f3c92018c6ed8c120b2cf6e14320679b5738", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b00000000000000001798c09cff1628733ff3baca4b61e838abd4fd17663044022056d79e8035d942eaf1c5de8722904e4ebdf4dc05a72ed6bb21fd16dc6808044d02202ec527272752ce4d75886dd0e39690e6bbe2a652db51addd4279dfd149f4dbe4", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203137", + "expiration": 0, + "recipientId": "AVhZ6fHoZAc3vdBLor1izJFFxRJaBMj7xy", + "signature": "3044022056d79e8035d942eaf1c5de8722904e4ebdf4dc05a72ed6bb21fd16dc6808044d02202ec527272752ce4d75886dd0e39690e6bbe2a652db51addd4279dfd149f4dbe4", + "vendorField": "Transaction 17", + "id": "f080cb1b2b377d7722a0b84628da10422bc7d35485979038b5352de2a743d27e" + }, + "verified": false, + "id": "f080cb1b2b377d7722a0b84628da10422bc7d35485979038b5352de2a743d27e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AVhZ6fHoZAc3vdBLor1izJFFxRJaBMj7xy", + "type": 0, + "vendorField": "Transaction 17", + "vendorFieldHex": "5472616e73616374696f6e203137", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022056d79e8035d942eaf1c5de8722904e4ebdf4dc05a72ed6bb21fd16dc6808044d02202ec527272752ce4d75886dd0e39690e6bbe2a652db51addd4279dfd149f4dbe4", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b0000000000000000177a10018a8e0b53ef2ba10a669892a7ed8120e4d43045022100dd67d44ee667c3223ca757fed91fa2cbe1cb9cb42109fabce6eb83dcfd7d8747022045811f62696dc8c6af6554f55d8a9c5f2bd3f80271897c18dfdf03f60349036c", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203235", + "expiration": 0, + "recipientId": "ASuHG25ddYuaaF6hACzHf3mAKd4b66kjfm", + "signature": "3045022100dd67d44ee667c3223ca757fed91fa2cbe1cb9cb42109fabce6eb83dcfd7d8747022045811f62696dc8c6af6554f55d8a9c5f2bd3f80271897c18dfdf03f60349036c", + "vendorField": "Transaction 25", + "id": "2e0fa5c36c09d4eda9cfa53ce69ddcc6e4d2d113f9562c0a68c90163b54625a8" + }, + "verified": false, + "id": "2e0fa5c36c09d4eda9cfa53ce69ddcc6e4d2d113f9562c0a68c90163b54625a8", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ASuHG25ddYuaaF6hACzHf3mAKd4b66kjfm", + "type": 0, + "vendorField": "Transaction 25", + "vendorFieldHex": "5472616e73616374696f6e203235", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100dd67d44ee667c3223ca757fed91fa2cbe1cb9cb42109fabce6eb83dcfd7d8747022045811f62696dc8c6af6554f55d8a9c5f2bd3f80271897c18dfdf03f60349036c", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b0000000000000000177b5daa22d0d47c8aabd77e099011a5c5de5ec0d130440220348a8a248a43b290989893e559fe0a041d77357a62edc4dc990555da00fa817202206aa92a6d3b06695e7600550540204c7b8abedf0b686449a07a4e6a70c1fac395", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203131", + "expiration": 0, + "recipientId": "AT2Ay2KRUTjvBm7p3NbWWyQhhU4P1hH2Ns", + "signature": "30440220348a8a248a43b290989893e559fe0a041d77357a62edc4dc990555da00fa817202206aa92a6d3b06695e7600550540204c7b8abedf0b686449a07a4e6a70c1fac395", + "vendorField": "Transaction 11", + "id": "da77cf38c9507f29b4bee0e29b6fa8b49fcd636fd5261cf773efc1c1332f8ad3" + }, + "verified": false, + "id": "da77cf38c9507f29b4bee0e29b6fa8b49fcd636fd5261cf773efc1c1332f8ad3", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AT2Ay2KRUTjvBm7p3NbWWyQhhU4P1hH2Ns", + "type": 0, + "vendorField": "Transaction 11", + "vendorFieldHex": "5472616e73616374696f6e203131", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220348a8a248a43b290989893e559fe0a041d77357a62edc4dc990555da00fa817202206aa92a6d3b06695e7600550540204c7b8abedf0b686449a07a4e6a70c1fac395", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b0000000000000000179c89afefd44cae11fa228af7cfe1470208eeef4e304502210099c1981e0e8f968bc115039c025eeb94d371b241a19d98002821f10002c8aeab022046e9aed58e820cf371e194d408d01090bb7b867ac918d47015225ab62157e462", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2037", + "expiration": 0, + "recipientId": "AW3a16sCCRn2LSnGnWZtQCPUnZMsdE6uVU", + "signature": "304502210099c1981e0e8f968bc115039c025eeb94d371b241a19d98002821f10002c8aeab022046e9aed58e820cf371e194d408d01090bb7b867ac918d47015225ab62157e462", + "vendorField": "Transaction 7", + "id": "f0971e62d506eefbced73550c73dd21423bfc9d9d76ae49ad7b6a52f0d894e74" + }, + "verified": false, + "id": "f0971e62d506eefbced73550c73dd21423bfc9d9d76ae49ad7b6a52f0d894e74", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AW3a16sCCRn2LSnGnWZtQCPUnZMsdE6uVU", + "type": 0, + "vendorField": "Transaction 7", + "vendorFieldHex": "5472616e73616374696f6e2037", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210099c1981e0e8f968bc115039c025eeb94d371b241a19d98002821f10002c8aeab022046e9aed58e820cf371e194d408d01090bb7b867ac918d47015225ab62157e462", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b000000000000000017fe49b6f1b2bacbbafe7263bf3f4c66c2332e71bb304402206b86a185dce4e776bb5f9f95d4b5cec4eba62873fe88ccdb864946fbbe60cc7902200a821a5bb1d0f2d983f91d282ce60826b7c616005ff6db0d4dbe3ea11ddf8ee2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2032", + "expiration": 0, + "recipientId": "AexRb1FqLzBZPGVsLWSYZ1haE5NqUZuNA6", + "signature": "304402206b86a185dce4e776bb5f9f95d4b5cec4eba62873fe88ccdb864946fbbe60cc7902200a821a5bb1d0f2d983f91d282ce60826b7c616005ff6db0d4dbe3ea11ddf8ee2", + "vendorField": "Transaction 2", + "id": "3a7f71110427141aec4353f9ce6ec167ae9d7d7df0b020474b59876792577b30" + }, + "verified": false, + "id": "3a7f71110427141aec4353f9ce6ec167ae9d7d7df0b020474b59876792577b30", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AexRb1FqLzBZPGVsLWSYZ1haE5NqUZuNA6", + "type": 0, + "vendorField": "Transaction 2", + "vendorFieldHex": "5472616e73616374696f6e2032", + "amount": "200000000", + "fee": "10000000", + "signature": "304402206b86a185dce4e776bb5f9f95d4b5cec4eba62873fe88ccdb864946fbbe60cc7902200a821a5bb1d0f2d983f91d282ce60826b7c616005ff6db0d4dbe3ea11ddf8ee2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333700c2eb0b000000000000000017bb95af90f1587796c7d844c28d016ecb96cf7b323045022100cd1b94922995ba4e16935f2380234ce7b6da26fcbab29f3e957aeb8ba8e8d87d022071757f37077132b0db0f217b657791ebb971fbaa7ff95e183ba3cb5858d525f4", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203337", + "expiration": 0, + "recipientId": "AYsjKisSCaZ1rVMo8ciezerDdWrZ8RYVfc", + "signature": "3045022100cd1b94922995ba4e16935f2380234ce7b6da26fcbab29f3e957aeb8ba8e8d87d022071757f37077132b0db0f217b657791ebb971fbaa7ff95e183ba3cb5858d525f4", + "vendorField": "Transaction 37", + "id": "9042462c36186b082e1f503081c0695c9f9d49a7dac64cf2804097812f769b03" + }, + "verified": false, + "id": "9042462c36186b082e1f503081c0695c9f9d49a7dac64cf2804097812f769b03", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYsjKisSCaZ1rVMo8ciezerDdWrZ8RYVfc", + "type": 0, + "vendorField": "Transaction 37", + "vendorFieldHex": "5472616e73616374696f6e203337", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100cd1b94922995ba4e16935f2380234ce7b6da26fcbab29f3e957aeb8ba8e8d87d022071757f37077132b0db0f217b657791ebb971fbaa7ff95e183ba3cb5858d525f4", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333300c2eb0b0000000000000000170e2059d5f792f009d8457f67d29d72ebd5cd8e35304402200a1537addb2f2f2e434a8c6e4ab32acc59f7653226218b7fe9bd5f82f98778b202205c77341d3ffc0e049aac2a476ee978acca2829d111552645797613f3ca616a4b", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203333", + "expiration": 0, + "recipientId": "AH4ZrxwiuaL9gWxTguvCtfgQdYWHPegK2B", + "signature": "304402200a1537addb2f2f2e434a8c6e4ab32acc59f7653226218b7fe9bd5f82f98778b202205c77341d3ffc0e049aac2a476ee978acca2829d111552645797613f3ca616a4b", + "vendorField": "Transaction 33", + "id": "c75d1303e71739561e9515e3d6ecb4e9f7627968c5b16b54bb03ef3933da224d" + }, + "verified": false, + "id": "c75d1303e71739561e9515e3d6ecb4e9f7627968c5b16b54bb03ef3933da224d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AH4ZrxwiuaL9gWxTguvCtfgQdYWHPegK2B", + "type": 0, + "vendorField": "Transaction 33", + "vendorFieldHex": "5472616e73616374696f6e203333", + "amount": "200000000", + "fee": "10000000", + "signature": "304402200a1537addb2f2f2e434a8c6e4ab32acc59f7653226218b7fe9bd5f82f98778b202205c77341d3ffc0e049aac2a476ee978acca2829d111552645797613f3ca616a4b", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333800c2eb0b00000000000000001756cf720576a7f775bd1d4ac4f74bb17e45138740304402206af353161fc543cfab5d90a58dba1438571506cad30a5c78e49cd2f28ebd542002206ab2833769213fa2603ceb14e1bf88a1647dfa4a28e5459fbc47c829adc34026", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203338", + "expiration": 0, + "recipientId": "APgtGZ7i6cju7pAw5z6Wa3igbXyTxvmCym", + "signature": "304402206af353161fc543cfab5d90a58dba1438571506cad30a5c78e49cd2f28ebd542002206ab2833769213fa2603ceb14e1bf88a1647dfa4a28e5459fbc47c829adc34026", + "vendorField": "Transaction 38", + "id": "2b8dcee1e8bbb486509b85350bebd747e6e837e29ea674581aa529299d74fac6" + }, + "verified": false, + "id": "2b8dcee1e8bbb486509b85350bebd747e6e837e29ea674581aa529299d74fac6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APgtGZ7i6cju7pAw5z6Wa3igbXyTxvmCym", + "type": 0, + "vendorField": "Transaction 38", + "vendorFieldHex": "5472616e73616374696f6e203338", + "amount": "200000000", + "fee": "10000000", + "signature": "304402206af353161fc543cfab5d90a58dba1438571506cad30a5c78e49cd2f28ebd542002206ab2833769213fa2603ceb14e1bf88a1647dfa4a28e5459fbc47c829adc34026", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b000000000000000017b858992acb67521bcafc16e7d4d1e93005c380443045022100ef53b0b4dee8630494f812ef6056c5a5821eb2478a0cabf55039d35db9ac3838022061845377b9eee8d4c20cf1a99c22f424a9ea21d842bc85cdaa8c4f4ba05b1f35", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203132", + "expiration": 0, + "recipientId": "AYac7iLi12Dsgi1J9cT5bfDp22ruSDnVdZ", + "signature": "3045022100ef53b0b4dee8630494f812ef6056c5a5821eb2478a0cabf55039d35db9ac3838022061845377b9eee8d4c20cf1a99c22f424a9ea21d842bc85cdaa8c4f4ba05b1f35", + "vendorField": "Transaction 12", + "id": "a1588a7cf0a141a10e0c75b3b586b7b3137aec5781a904d2d2a749f5b6f98447" + }, + "verified": false, + "id": "a1588a7cf0a141a10e0c75b3b586b7b3137aec5781a904d2d2a749f5b6f98447", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYac7iLi12Dsgi1J9cT5bfDp22ruSDnVdZ", + "type": 0, + "vendorField": "Transaction 12", + "vendorFieldHex": "5472616e73616374696f6e203132", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ef53b0b4dee8630494f812ef6056c5a5821eb2478a0cabf55039d35db9ac3838022061845377b9eee8d4c20cf1a99c22f424a9ea21d842bc85cdaa8c4f4ba05b1f35", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b0000000000000000175060e8c493e5772f02b9e88e3973ae1ad35c4b6d3043021f0ef0634992f4b2ad6b038e58d247ef4476a1eda7f2ce48ae48e24a1d3bb54902203a769d1199251a4df6d83d0db299e3bd0c1363c51fa3e9f545dd5c13d730bbd1", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203237", + "expiration": 0, + "recipientId": "AP6soDZoHGm5j48dqsSeJkn8KEnPi5HB9e", + "signature": "3043021f0ef0634992f4b2ad6b038e58d247ef4476a1eda7f2ce48ae48e24a1d3bb54902203a769d1199251a4df6d83d0db299e3bd0c1363c51fa3e9f545dd5c13d730bbd1", + "vendorField": "Transaction 27", + "id": "bb0dcfd1488eaed1a2c99d776cb8fbed8b56d7bff33ffd479299761ff945580d" + }, + "verified": false, + "id": "bb0dcfd1488eaed1a2c99d776cb8fbed8b56d7bff33ffd479299761ff945580d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AP6soDZoHGm5j48dqsSeJkn8KEnPi5HB9e", + "type": 0, + "vendorField": "Transaction 27", + "vendorFieldHex": "5472616e73616374696f6e203237", + "amount": "200000000", + "fee": "10000000", + "signature": "3043021f0ef0634992f4b2ad6b038e58d247ef4476a1eda7f2ce48ae48e24a1d3bb54902203a769d1199251a4df6d83d0db299e3bd0c1363c51fa3e9f545dd5c13d730bbd1", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b000000000000000017229c0d0469156c68ff3bedef21be4a5a652549f630440220435a475d12adb82f40a30520bfde3072e9a8fbf016529faacfde40dea4c7603102202d7d784c34f089ce93af3a902936de4960005d17a9d64f096a87364d4cd98625", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203235", + "expiration": 0, + "recipientId": "AJvsZd83zVB3S1JWjKg3bpHjuGNSxSHzo3", + "signature": "30440220435a475d12adb82f40a30520bfde3072e9a8fbf016529faacfde40dea4c7603102202d7d784c34f089ce93af3a902936de4960005d17a9d64f096a87364d4cd98625", + "vendorField": "Transaction 25", + "id": "04b65d78d21317a83e4f6b14ec7bcdcbd51090ccf76f0a4d6e53b1e65d4ef5cf" + }, + "verified": false, + "id": "04b65d78d21317a83e4f6b14ec7bcdcbd51090ccf76f0a4d6e53b1e65d4ef5cf", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AJvsZd83zVB3S1JWjKg3bpHjuGNSxSHzo3", + "type": 0, + "vendorField": "Transaction 25", + "vendorFieldHex": "5472616e73616374696f6e203235", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220435a475d12adb82f40a30520bfde3072e9a8fbf016529faacfde40dea4c7603102202d7d784c34f089ce93af3a902936de4960005d17a9d64f096a87364d4cd98625", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b000000000000000017e7f5bc11a5b2237519bf751220a5968483b3dfc93045022100ce0abaa1e6656daf0cab3652b8f9355633ac16beb264dff7f076c9760b4259d3022070a870ab901efa8fa4e54b9c11fe201716ad922aa73e69cc9b7c88b71acc73ee", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2037", + "expiration": 0, + "recipientId": "AcvN7kfFEbBX9uBRi9dqmToM94W7Bs2oVV", + "signature": "3045022100ce0abaa1e6656daf0cab3652b8f9355633ac16beb264dff7f076c9760b4259d3022070a870ab901efa8fa4e54b9c11fe201716ad922aa73e69cc9b7c88b71acc73ee", + "vendorField": "Transaction 7", + "id": "823ee127cfec5125bd6a32bacfbfc231f25b2bb294962f353a39ffe52bf2a2e9" + }, + "verified": false, + "id": "823ee127cfec5125bd6a32bacfbfc231f25b2bb294962f353a39ffe52bf2a2e9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AcvN7kfFEbBX9uBRi9dqmToM94W7Bs2oVV", + "type": 0, + "vendorField": "Transaction 7", + "vendorFieldHex": "5472616e73616374696f6e2037", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ce0abaa1e6656daf0cab3652b8f9355633ac16beb264dff7f076c9760b4259d3022070a870ab901efa8fa4e54b9c11fe201716ad922aa73e69cc9b7c88b71acc73ee", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333900c2eb0b000000000000000017f0157c4c48288c93e420b6932ee7c10c119d7d983045022100c33a6b77cd9cd63bc5257537dc90f0506ecee8fd0ad34facfed9132c54b9c17902205546cc10e43e60167964f37881412c1f4e742be6f1218749e63e1bdbd5be5023", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203339", + "expiration": 0, + "recipientId": "AdfKZNwNHLMw12KhtfSrWpSaRAXbTqJgwC", + "signature": "3045022100c33a6b77cd9cd63bc5257537dc90f0506ecee8fd0ad34facfed9132c54b9c17902205546cc10e43e60167964f37881412c1f4e742be6f1218749e63e1bdbd5be5023", + "vendorField": "Transaction 39", + "id": "5b9a04a10927103d683007a7ad99692684bcd07c8f149adb0a24c0852cf7b3d0" + }, + "verified": false, + "id": "5b9a04a10927103d683007a7ad99692684bcd07c8f149adb0a24c0852cf7b3d0", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AdfKZNwNHLMw12KhtfSrWpSaRAXbTqJgwC", + "type": 0, + "vendorField": "Transaction 39", + "vendorFieldHex": "5472616e73616374696f6e203339", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c33a6b77cd9cd63bc5257537dc90f0506ecee8fd0ad34facfed9132c54b9c17902205546cc10e43e60167964f37881412c1f4e742be6f1218749e63e1bdbd5be5023", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b0000000000000000177f106853bea88b3b4ca2daaa2e9677ffb019d274304402205d25a0dbb4bd258e2392bf3ac4faedea4e6d24bdbbcdae653641db5eb532cf9e02200a38c7ada6731f470861b929d5f15f9c63a56a1985345b9d86ae005acf13b073", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203236", + "expiration": 0, + "recipientId": "ATMj7skUvdwcJ7c1s8yjbBu8ZQ8o5BdRyU", + "signature": "304402205d25a0dbb4bd258e2392bf3ac4faedea4e6d24bdbbcdae653641db5eb532cf9e02200a38c7ada6731f470861b929d5f15f9c63a56a1985345b9d86ae005acf13b073", + "vendorField": "Transaction 26", + "id": "53c85885bc55b42afb7e8768f53b3981d711a88419649a930cbb28aa0fc74faa" + }, + "verified": false, + "id": "53c85885bc55b42afb7e8768f53b3981d711a88419649a930cbb28aa0fc74faa", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ATMj7skUvdwcJ7c1s8yjbBu8ZQ8o5BdRyU", + "type": 0, + "vendorField": "Transaction 26", + "vendorFieldHex": "5472616e73616374696f6e203236", + "amount": "200000000", + "fee": "10000000", + "signature": "304402205d25a0dbb4bd258e2392bf3ac4faedea4e6d24bdbbcdae653641db5eb532cf9e02200a38c7ada6731f470861b929d5f15f9c63a56a1985345b9d86ae005acf13b073", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b0000000000000000172ff8cc458a6815c2c15687aadc6351bd3dabb1993045022100d2aa5946b05215cd1cca45fefe390ec78081406d6b8f13e724cff599f046d6f1022035a2e6ff995bac39bae2dae1232f7ea2d0ba5897b1fe0ffccf1a0c1172d89308", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203135", + "expiration": 0, + "recipientId": "AL9XTJEn4nMjvwX9oWqCh7oLxCn1Z25xAY", + "signature": "3045022100d2aa5946b05215cd1cca45fefe390ec78081406d6b8f13e724cff599f046d6f1022035a2e6ff995bac39bae2dae1232f7ea2d0ba5897b1fe0ffccf1a0c1172d89308", + "vendorField": "Transaction 15", + "id": "07aa07921ae7251b6c54abd394439031eed86272509fcecc1f9bde91d25460e8" + }, + "verified": false, + "id": "07aa07921ae7251b6c54abd394439031eed86272509fcecc1f9bde91d25460e8", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AL9XTJEn4nMjvwX9oWqCh7oLxCn1Z25xAY", + "type": 0, + "vendorField": "Transaction 15", + "vendorFieldHex": "5472616e73616374696f6e203135", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100d2aa5946b05215cd1cca45fefe390ec78081406d6b8f13e724cff599f046d6f1022035a2e6ff995bac39bae2dae1232f7ea2d0ba5897b1fe0ffccf1a0c1172d89308", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b0000000000000000174e4248b0e82e17bce3d97fd49c9b9190e252b62d3045022100e88aaf7ef7fce75a0554d61932375ec058d4f0e35979bc2d3114e8f1c9d08040022051dd6c51e46258ce1b5c6c57e84194325629213b7f9bcabb879d51ac037aa6b5", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203135", + "expiration": 0, + "recipientId": "ANufkx6DZAWtpEVfnhYrRdtW5T5brh4ro1", + "signature": "3045022100e88aaf7ef7fce75a0554d61932375ec058d4f0e35979bc2d3114e8f1c9d08040022051dd6c51e46258ce1b5c6c57e84194325629213b7f9bcabb879d51ac037aa6b5", + "vendorField": "Transaction 15", + "id": "3e9f6aff929dfc4ceaad1ec02cfdc7991c0409082278b5724b1344dee50f0912" + }, + "verified": false, + "id": "3e9f6aff929dfc4ceaad1ec02cfdc7991c0409082278b5724b1344dee50f0912", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ANufkx6DZAWtpEVfnhYrRdtW5T5brh4ro1", + "type": 0, + "vendorField": "Transaction 15", + "vendorFieldHex": "5472616e73616374696f6e203135", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e88aaf7ef7fce75a0554d61932375ec058d4f0e35979bc2d3114e8f1c9d08040022051dd6c51e46258ce1b5c6c57e84194325629213b7f9bcabb879d51ac037aa6b5", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333500c2eb0b000000000000000017d320a267170f665d111adaa1960d95f9708e7d073045022100accbc39d4f4e1e863962caf8e6cc548dea20ee1afa9e6e1806e067da9ce4d3d202200a1a38c83468157af05584a85ccfebb9e4c551c2237befbfd54a51fdcc96b168", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203335", + "expiration": 0, + "recipientId": "Ab2DKT8n2i87bXtwnGedLhfFCW83qWwWHf", + "signature": "3045022100accbc39d4f4e1e863962caf8e6cc548dea20ee1afa9e6e1806e067da9ce4d3d202200a1a38c83468157af05584a85ccfebb9e4c551c2237befbfd54a51fdcc96b168", + "vendorField": "Transaction 35", + "id": "807d8c88ae5f5addc24abfbea35c14d4e8c1a7557ba094dd5c63a8c4cbf97517" + }, + "verified": false, + "id": "807d8c88ae5f5addc24abfbea35c14d4e8c1a7557ba094dd5c63a8c4cbf97517", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Ab2DKT8n2i87bXtwnGedLhfFCW83qWwWHf", + "type": 0, + "vendorField": "Transaction 35", + "vendorFieldHex": "5472616e73616374696f6e203335", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100accbc39d4f4e1e863962caf8e6cc548dea20ee1afa9e6e1806e067da9ce4d3d202200a1a38c83468157af05584a85ccfebb9e4c551c2237befbfd54a51fdcc96b168", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b00000000000000001768c6c5f98b69bd4d2f43ccd0d7842d24d0ddf23e3045022100c7c0193c159cbde8a09dead106919732a3b4e30a5cf51617314133454c65e1ef022074b355a40e59075104f694c19d785c2b8ccd3c1ffc0ee17fc8a2c919bbdc3ef2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203138", + "expiration": 0, + "recipientId": "ARKt3Tmo23EYt7cTkrZiaoW9CZ5Z7mKcHm", + "signature": "3045022100c7c0193c159cbde8a09dead106919732a3b4e30a5cf51617314133454c65e1ef022074b355a40e59075104f694c19d785c2b8ccd3c1ffc0ee17fc8a2c919bbdc3ef2", + "vendorField": "Transaction 18", + "id": "86fea37d45a620a5920849271c85f1cba4525f93c2f18785986f8d93e2c33152" + }, + "verified": false, + "id": "86fea37d45a620a5920849271c85f1cba4525f93c2f18785986f8d93e2c33152", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ARKt3Tmo23EYt7cTkrZiaoW9CZ5Z7mKcHm", + "type": 0, + "vendorField": "Transaction 18", + "vendorFieldHex": "5472616e73616374696f6e203138", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c7c0193c159cbde8a09dead106919732a3b4e30a5cf51617314133454c65e1ef022074b355a40e59075104f694c19d785c2b8ccd3c1ffc0ee17fc8a2c919bbdc3ef2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20343000c2eb0b00000000000000001730cc52afbe325a52f2bf8983937210c52650a5ed3045022100eaec6482374438ccc03a12bfc06b8ada1c4f2769d1b8451eb643213be07f25f802207bf68341be74bc813eb1ad74a0b56d16e27cb5954a61aa971296f81823d65d6b", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203430", + "expiration": 0, + "recipientId": "ALDtrKKeA8bgmiJ8obDPkEvjfWT1AUhf1Q", + "signature": "3045022100eaec6482374438ccc03a12bfc06b8ada1c4f2769d1b8451eb643213be07f25f802207bf68341be74bc813eb1ad74a0b56d16e27cb5954a61aa971296f81823d65d6b", + "vendorField": "Transaction 40", + "id": "7f1dd47f8316475a37888d72537091ab3529778127d8752f2689d585ecad248f" + }, + "verified": false, + "id": "7f1dd47f8316475a37888d72537091ab3529778127d8752f2689d585ecad248f", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ALDtrKKeA8bgmiJ8obDPkEvjfWT1AUhf1Q", + "type": 0, + "vendorField": "Transaction 40", + "vendorFieldHex": "5472616e73616374696f6e203430", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100eaec6482374438ccc03a12bfc06b8ada1c4f2769d1b8451eb643213be07f25f802207bf68341be74bc813eb1ad74a0b56d16e27cb5954a61aa971296f81823d65d6b", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b000000000000000017042b228f903384321ef7e2e38f413a9049052170304502210090998a023faa9149d75cb51f38f98adf38e39cbb5dc15cdfcc0eb5beff9c2eec022034f33fa9ccb43a93965804e455e08adb9f00910063cd5a87ee70ef376a4c5db7", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203330", + "expiration": 0, + "recipientId": "AG9v2LZyt8f6dAjb72z1MCoy1MVQs4gP3C", + "signature": "304502210090998a023faa9149d75cb51f38f98adf38e39cbb5dc15cdfcc0eb5beff9c2eec022034f33fa9ccb43a93965804e455e08adb9f00910063cd5a87ee70ef376a4c5db7", + "vendorField": "Transaction 30", + "id": "050f1ee15b00a7cd89a5c7faf7af6c3522d3aa7413eadf272eb17611613ef242" + }, + "verified": false, + "id": "050f1ee15b00a7cd89a5c7faf7af6c3522d3aa7413eadf272eb17611613ef242", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AG9v2LZyt8f6dAjb72z1MCoy1MVQs4gP3C", + "type": 0, + "vendorField": "Transaction 30", + "vendorFieldHex": "5472616e73616374696f6e203330", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210090998a023faa9149d75cb51f38f98adf38e39cbb5dc15cdfcc0eb5beff9c2eec022034f33fa9ccb43a93965804e455e08adb9f00910063cd5a87ee70ef376a4c5db7", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b0000000000000000175a821c2ba6fd549322c6a7994ac6fe5a9417f9b930440220494a3190e1fc982d01badc71006b776df163583ae1ac365d7dc4cda0a061373a02201aa1f3bb1d2ba405750c585784caf24f25b83f57fc11e0f675d12d2fe3d666de", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203231", + "expiration": 0, + "recipientId": "AQ2SKy3vxivCKiELzHJPdnmEcm5VYFdFcw", + "signature": "30440220494a3190e1fc982d01badc71006b776df163583ae1ac365d7dc4cda0a061373a02201aa1f3bb1d2ba405750c585784caf24f25b83f57fc11e0f675d12d2fe3d666de", + "vendorField": "Transaction 21", + "id": "e2d37d92b291a8286fe0fd345d1235769a083b627b0927adbe6044491101a5e0" + }, + "verified": false, + "id": "e2d37d92b291a8286fe0fd345d1235769a083b627b0927adbe6044491101a5e0", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AQ2SKy3vxivCKiELzHJPdnmEcm5VYFdFcw", + "type": 0, + "vendorField": "Transaction 21", + "vendorFieldHex": "5472616e73616374696f6e203231", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220494a3190e1fc982d01badc71006b776df163583ae1ac365d7dc4cda0a061373a02201aa1f3bb1d2ba405750c585784caf24f25b83f57fc11e0f675d12d2fe3d666de", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333500c2eb0b00000000000000001759854238ed033c028098256d30c4bd03e090f233304502210080d7de0342c4855c0c1de956e716623341fce1b4856a1c6e00ebba7bdeee4b6d0220172e8ec11914e21ed369fad2615dfa95ce09b3eec8f3a03e453b4050b7baefd6", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203335", + "expiration": 0, + "recipientId": "APwDRYfREvgg1ymd53WDd4VtLBw2gEx1fY", + "signature": "304502210080d7de0342c4855c0c1de956e716623341fce1b4856a1c6e00ebba7bdeee4b6d0220172e8ec11914e21ed369fad2615dfa95ce09b3eec8f3a03e453b4050b7baefd6", + "vendorField": "Transaction 35", + "id": "78f40efe33dd9aeab22596360a534f4cae1fca08342540f3666e1a66ee40fc59" + }, + "verified": false, + "id": "78f40efe33dd9aeab22596360a534f4cae1fca08342540f3666e1a66ee40fc59", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APwDRYfREvgg1ymd53WDd4VtLBw2gEx1fY", + "type": 0, + "vendorField": "Transaction 35", + "vendorFieldHex": "5472616e73616374696f6e203335", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210080d7de0342c4855c0c1de956e716623341fce1b4856a1c6e00ebba7bdeee4b6d0220172e8ec11914e21ed369fad2615dfa95ce09b3eec8f3a03e453b4050b7baefd6", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017927ac44a3d0a64edebbfe4dafc8510001eff90a530440220257e5ed2aaea59a5f1a4ae74acc59dd1c68a821ecd9eb506ae7fbb01cfe6511f02203eadfccae466fda0800b0cd401284f79016742f94cb6b8e23766e625e7634855", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203133", + "expiration": 0, + "recipientId": "AV8PNVLjPSfuS8xijSPsxHFQ57HQpBUV67", + "signature": "30440220257e5ed2aaea59a5f1a4ae74acc59dd1c68a821ecd9eb506ae7fbb01cfe6511f02203eadfccae466fda0800b0cd401284f79016742f94cb6b8e23766e625e7634855", + "vendorField": "Transaction 13", + "id": "2695b3c15c822b4d76c551db00bf12f0994bb51ac8228429596d7658ad5cacdf" + }, + "verified": false, + "id": "2695b3c15c822b4d76c551db00bf12f0994bb51ac8228429596d7658ad5cacdf", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AV8PNVLjPSfuS8xijSPsxHFQ57HQpBUV67", + "type": 0, + "vendorField": "Transaction 13", + "vendorFieldHex": "5472616e73616374696f6e203133", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220257e5ed2aaea59a5f1a4ae74acc59dd1c68a821ecd9eb506ae7fbb01cfe6511f02203eadfccae466fda0800b0cd401284f79016742f94cb6b8e23766e625e7634855", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b000000000000000017bf515df18a873bf775604cf142690c5c9e2216af304502210089973d46853f64b22a8e5ea082741ce61a5b65ea627c2e7d65c6b68761ea9307022064f1232ac967e636f86ba2a1cb84b898e6ef433fc201b893914ec350ae5424c9", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203138", + "expiration": 0, + "recipientId": "AZDUBcZSWweTX73zhckwqSzNSfSo92THV3", + "signature": "304502210089973d46853f64b22a8e5ea082741ce61a5b65ea627c2e7d65c6b68761ea9307022064f1232ac967e636f86ba2a1cb84b898e6ef433fc201b893914ec350ae5424c9", + "vendorField": "Transaction 18", + "id": "ae70da6e453d96ea9d51be065112cdec453657ab46f87ea98ca60aaf93c961f9" + }, + "verified": false, + "id": "ae70da6e453d96ea9d51be065112cdec453657ab46f87ea98ca60aaf93c961f9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AZDUBcZSWweTX73zhckwqSzNSfSo92THV3", + "type": 0, + "vendorField": "Transaction 18", + "vendorFieldHex": "5472616e73616374696f6e203138", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210089973d46853f64b22a8e5ea082741ce61a5b65ea627c2e7d65c6b68761ea9307022064f1232ac967e636f86ba2a1cb84b898e6ef433fc201b893914ec350ae5424c9", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000179a245b5d5ace31e5432c7cf0e0ef83ddce7143a33045022100d399c822102d6ef40c2e9d4117055604358ef4db80943936c99c3dbe2fe0c9e0022076522ed280a8b48dcf2e1050ada11da7d0ba410cd405cc3575886e524bf74b3b", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2034", + "expiration": 0, + "recipientId": "AVpuGAKLThfwEWPrpDD5oMG4HeyC9fRLG8", + "signature": "3045022100d399c822102d6ef40c2e9d4117055604358ef4db80943936c99c3dbe2fe0c9e0022076522ed280a8b48dcf2e1050ada11da7d0ba410cd405cc3575886e524bf74b3b", + "vendorField": "Transaction 4", + "id": "8ac3927c38bcc0dc237f1241aa03663cb09f0adcb28645e26093a8219e3247cb" + }, + "verified": false, + "id": "8ac3927c38bcc0dc237f1241aa03663cb09f0adcb28645e26093a8219e3247cb", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AVpuGAKLThfwEWPrpDD5oMG4HeyC9fRLG8", + "type": 0, + "vendorField": "Transaction 4", + "vendorFieldHex": "5472616e73616374696f6e2034", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100d399c822102d6ef40c2e9d4117055604358ef4db80943936c99c3dbe2fe0c9e0022076522ed280a8b48dcf2e1050ada11da7d0ba410cd405cc3575886e524bf74b3b", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333900c2eb0b0000000000000000170ce3d695aa0015271cadb4ae7cabde52e7bf881b3045022100f4160aad6909f8159bb2ba3b677a8d4ccdafd769fb7fb193cf45ef5c24e90b6702206f9c06f30f72624b140d0e79994d655095b9acfc633d65662ef982b52b3554df", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203339", + "expiration": 0, + "recipientId": "AGx2hH3XAaqmm26HfKCN3BbPSafuWdhoFA", + "signature": "3045022100f4160aad6909f8159bb2ba3b677a8d4ccdafd769fb7fb193cf45ef5c24e90b6702206f9c06f30f72624b140d0e79994d655095b9acfc633d65662ef982b52b3554df", + "vendorField": "Transaction 39", + "id": "4d210f7ba219a822077319a43ed31173675617b5fe5ecbccfde23acbdf5724ec" + }, + "verified": false, + "id": "4d210f7ba219a822077319a43ed31173675617b5fe5ecbccfde23acbdf5724ec", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AGx2hH3XAaqmm26HfKCN3BbPSafuWdhoFA", + "type": 0, + "vendorField": "Transaction 39", + "vendorFieldHex": "5472616e73616374696f6e203339", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f4160aad6909f8159bb2ba3b677a8d4ccdafd769fb7fb193cf45ef5c24e90b6702206f9c06f30f72624b140d0e79994d655095b9acfc633d65662ef982b52b3554df", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333700c2eb0b000000000000000017b6f21649c7b1b1b9975312888b2809b028cbb8353045022100f21501ede148ae4b1825747d5a35b2f212392ed213a3de1353cc554cf9fb450002203540a0542bdc74befa5e7737af0a76fd511fcc1d663f3c0ab21e1ca9bdfe69d2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203337", + "expiration": 0, + "recipientId": "AYTCdv41dTobzqLctawRcoG4h1v8wcaNya", + "signature": "3045022100f21501ede148ae4b1825747d5a35b2f212392ed213a3de1353cc554cf9fb450002203540a0542bdc74befa5e7737af0a76fd511fcc1d663f3c0ab21e1ca9bdfe69d2", + "vendorField": "Transaction 37", + "id": "4d69c457fcc88f8d068a41070e0298771f4ff9c9adb58fbc4ab20910ff8baa4e" + }, + "verified": false, + "id": "4d69c457fcc88f8d068a41070e0298771f4ff9c9adb58fbc4ab20910ff8baa4e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYTCdv41dTobzqLctawRcoG4h1v8wcaNya", + "type": 0, + "vendorField": "Transaction 37", + "vendorFieldHex": "5472616e73616374696f6e203337", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f21501ede148ae4b1825747d5a35b2f212392ed213a3de1353cc554cf9fb450002203540a0542bdc74befa5e7737af0a76fd511fcc1d663f3c0ab21e1ca9bdfe69d2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b0000000000000000173894e92a8d11945bfb593acdb9e619c22c989d2d3045022100d6ba427e0f702f74d0e7ce778354f5ff0376c775e53991bf9f4057957feb2b7902207ef90dfb96a4c8c5a296ec89589e631913145e9736251a82ea93c48fc765dc3d", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203232", + "expiration": 0, + "recipientId": "ALw3skAPFsg8v5JCQSeaBQ3gUfXqJBmKZF", + "signature": "3045022100d6ba427e0f702f74d0e7ce778354f5ff0376c775e53991bf9f4057957feb2b7902207ef90dfb96a4c8c5a296ec89589e631913145e9736251a82ea93c48fc765dc3d", + "vendorField": "Transaction 22", + "id": "6d5dac8209b4bb2d7f7230ff06d04950d66585dad88fff1f37cc3876d9cfc71b" + }, + "verified": false, + "id": "6d5dac8209b4bb2d7f7230ff06d04950d66585dad88fff1f37cc3876d9cfc71b", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ALw3skAPFsg8v5JCQSeaBQ3gUfXqJBmKZF", + "type": 0, + "vendorField": "Transaction 22", + "vendorFieldHex": "5472616e73616374696f6e203232", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100d6ba427e0f702f74d0e7ce778354f5ff0376c775e53991bf9f4057957feb2b7902207ef90dfb96a4c8c5a296ec89589e631913145e9736251a82ea93c48fc765dc3d", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333700c2eb0b00000000000000001779389f9aefdd03d9d3788bb08f1e5d8b6567aa1c3045022100e921d8a535a948010be53a418b14537894e5cd59f4798c28afb3a28b1eef3a6702201933fc9972f945e6a75b98bad43ab4c1d5aa50108ecfb0fc6d4d98692cea74c2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203337", + "expiration": 0, + "recipientId": "ASpqEybey5oEhiFkLuZw2j83n3gDRJRqQv", + "signature": "3045022100e921d8a535a948010be53a418b14537894e5cd59f4798c28afb3a28b1eef3a6702201933fc9972f945e6a75b98bad43ab4c1d5aa50108ecfb0fc6d4d98692cea74c2", + "vendorField": "Transaction 37", + "id": "fc9d4fb1d1130b060ddc08a7e1188b288cbe82d2af2ba2c3fad73f28c851c411" + }, + "verified": false, + "id": "fc9d4fb1d1130b060ddc08a7e1188b288cbe82d2af2ba2c3fad73f28c851c411", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ASpqEybey5oEhiFkLuZw2j83n3gDRJRqQv", + "type": 0, + "vendorField": "Transaction 37", + "vendorFieldHex": "5472616e73616374696f6e203337", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e921d8a535a948010be53a418b14537894e5cd59f4798c28afb3a28b1eef3a6702201933fc9972f945e6a75b98bad43ab4c1d5aa50108ecfb0fc6d4d98692cea74c2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b0000000000000000171785c644be770b6b42ba6d168e8bd5dbe1067cf63044022069b32ab58125181997f13326bd5bbd8a51c05ce6ca1cb3e05d211aabcdf094c202203ff626f8686e5eaadfbb3f0b0fd3c2d97a27da67df60d449bdfd1a7111691998", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203239", + "expiration": 0, + "recipientId": "AHvFSiz8JHpEtRMLxckmaM5cCZyzrWXvKd", + "signature": "3044022069b32ab58125181997f13326bd5bbd8a51c05ce6ca1cb3e05d211aabcdf094c202203ff626f8686e5eaadfbb3f0b0fd3c2d97a27da67df60d449bdfd1a7111691998", + "vendorField": "Transaction 29", + "id": "a79c95b5373df15f4168dffe812bf47ce65dbf4a7787d912b967e33fa8596d6d" + }, + "verified": false, + "id": "a79c95b5373df15f4168dffe812bf47ce65dbf4a7787d912b967e33fa8596d6d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AHvFSiz8JHpEtRMLxckmaM5cCZyzrWXvKd", + "type": 0, + "vendorField": "Transaction 29", + "vendorFieldHex": "5472616e73616374696f6e203239", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022069b32ab58125181997f13326bd5bbd8a51c05ce6ca1cb3e05d211aabcdf094c202203ff626f8686e5eaadfbb3f0b0fd3c2d97a27da67df60d449bdfd1a7111691998", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b000000000000000017067c78e18d6221da34c66c5b99c49b6acdf7d6a03044022046e3fe677c86fe992839c0b4b6c8d52ed9b0468d746144f090a3210c3b22839a02200edefed7a6009d614c52d0258f137a9469e1b945cc44a9a966074039037b6050", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203136", + "expiration": 0, + "recipientId": "AGNAp8RYuM5EaT44P2r3JnfAqF5gLqWwSR", + "signature": "3044022046e3fe677c86fe992839c0b4b6c8d52ed9b0468d746144f090a3210c3b22839a02200edefed7a6009d614c52d0258f137a9469e1b945cc44a9a966074039037b6050", + "vendorField": "Transaction 16", + "id": "2f493c6bacc89027ac4d4c81f0d72678e992782a4ab5778ebfcc7b0bf5747bc1" + }, + "verified": false, + "id": "2f493c6bacc89027ac4d4c81f0d72678e992782a4ab5778ebfcc7b0bf5747bc1", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AGNAp8RYuM5EaT44P2r3JnfAqF5gLqWwSR", + "type": 0, + "vendorField": "Transaction 16", + "vendorFieldHex": "5472616e73616374696f6e203136", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022046e3fe677c86fe992839c0b4b6c8d52ed9b0468d746144f090a3210c3b22839a02200edefed7a6009d614c52d0258f137a9469e1b945cc44a9a966074039037b6050", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b000000000000000017da2752064521eadb7cac06cc69a82ba14bd78abe3045022100991d550584f937ab579d30b9836d2ddb9d07a53b62088b9ec7d812e831eea3270220130e6d8efe60fec26dd14dc6190c1f68bd37b044c5e48669da7d129671d2fbe9", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2039", + "expiration": 0, + "recipientId": "AbfN4M6HPEYzii1jGGyp28U6dFZnjRX3tU", + "signature": "3045022100991d550584f937ab579d30b9836d2ddb9d07a53b62088b9ec7d812e831eea3270220130e6d8efe60fec26dd14dc6190c1f68bd37b044c5e48669da7d129671d2fbe9", + "vendorField": "Transaction 9", + "id": "3faa245ee252b5d55c5e78ca10972aa88ae814d2f2fa4dfecbee07769f9731da" + }, + "verified": false, + "id": "3faa245ee252b5d55c5e78ca10972aa88ae814d2f2fa4dfecbee07769f9731da", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AbfN4M6HPEYzii1jGGyp28U6dFZnjRX3tU", + "type": 0, + "vendorField": "Transaction 9", + "vendorFieldHex": "5472616e73616374696f6e2039", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100991d550584f937ab579d30b9836d2ddb9d07a53b62088b9ec7d812e831eea3270220130e6d8efe60fec26dd14dc6190c1f68bd37b044c5e48669da7d129671d2fbe9", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333400c2eb0b000000000000000017dce1e04722ba02e42d74bd57303917048a3b9a0c304402203d37d32b85c84245f3cf89d3e3187c61eb28f9140cf07914891da61d58f8268b02203e68eec8d8c2c2e06261e5a39d0a58fafd90c46123f7e09b28c6ba88a489ff24", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203334", + "expiration": 0, + "recipientId": "Abuntr48GXDBuFRUTepCySB4YUdkTJmyBB", + "signature": "304402203d37d32b85c84245f3cf89d3e3187c61eb28f9140cf07914891da61d58f8268b02203e68eec8d8c2c2e06261e5a39d0a58fafd90c46123f7e09b28c6ba88a489ff24", + "vendorField": "Transaction 34", + "id": "2c0286e103c53db7932ba9c407fe5844a7ae390cef6e21279012fb9e7c77378d" + }, + "verified": false, + "id": "2c0286e103c53db7932ba9c407fe5844a7ae390cef6e21279012fb9e7c77378d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Abuntr48GXDBuFRUTepCySB4YUdkTJmyBB", + "type": 0, + "vendorField": "Transaction 34", + "vendorFieldHex": "5472616e73616374696f6e203334", + "amount": "200000000", + "fee": "10000000", + "signature": "304402203d37d32b85c84245f3cf89d3e3187c61eb28f9140cf07914891da61d58f8268b02203e68eec8d8c2c2e06261e5a39d0a58fafd90c46123f7e09b28c6ba88a489ff24", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b000000000000000017fbe158a34cc27bb228e101f482ed036f96b824743045022100c1fbc4813b8370edb55d74cff95931f9e5d2e44587c362a184e0a60861aaaf94022075e2c4fabcbeeb54f0ac98f3197bb7aa064e5609a4f7004a51b180030d98ec78", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203230", + "expiration": 0, + "recipientId": "AejhCyk3z4zFaSQQqtv8qy6zdm6hhEmCLj", + "signature": "3045022100c1fbc4813b8370edb55d74cff95931f9e5d2e44587c362a184e0a60861aaaf94022075e2c4fabcbeeb54f0ac98f3197bb7aa064e5609a4f7004a51b180030d98ec78", + "vendorField": "Transaction 20", + "id": "2f8dbafc501d882fe41752ac497dc9ad164c5a909c3eb5b827f8c4f0ef3e46d6" + }, + "verified": false, + "id": "2f8dbafc501d882fe41752ac497dc9ad164c5a909c3eb5b827f8c4f0ef3e46d6", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AejhCyk3z4zFaSQQqtv8qy6zdm6hhEmCLj", + "type": 0, + "vendorField": "Transaction 20", + "vendorFieldHex": "5472616e73616374696f6e203230", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c1fbc4813b8370edb55d74cff95931f9e5d2e44587c362a184e0a60861aaaf94022075e2c4fabcbeeb54f0ac98f3197bb7aa064e5609a4f7004a51b180030d98ec78", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000171e0f108e44028a522525837008ba0dbc307e5a1d3045022100ddb81efcdf3bd77b70c048f3d8cf9752bf1bcef935c73e5f9a92066a4f2c5c18022034a7cd1b0d4a62e88a1d289244462794648d0643015c3ae2fd6c3e92bf9269a8", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2034", + "expiration": 0, + "recipientId": "AJWoxxvEdq5d8QJAnBwACKiQb7YNbgrjJG", + "signature": "3045022100ddb81efcdf3bd77b70c048f3d8cf9752bf1bcef935c73e5f9a92066a4f2c5c18022034a7cd1b0d4a62e88a1d289244462794648d0643015c3ae2fd6c3e92bf9269a8", + "vendorField": "Transaction 4", + "id": "31135b9668f682268a53f6591c6ec7857dea4d6ed5d3149ea79c47132f7dfc41" + }, + "verified": false, + "id": "31135b9668f682268a53f6591c6ec7857dea4d6ed5d3149ea79c47132f7dfc41", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AJWoxxvEdq5d8QJAnBwACKiQb7YNbgrjJG", + "type": 0, + "vendorField": "Transaction 4", + "vendorFieldHex": "5472616e73616374696f6e2034", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ddb81efcdf3bd77b70c048f3d8cf9752bf1bcef935c73e5f9a92066a4f2c5c18022034a7cd1b0d4a62e88a1d289244462794648d0643015c3ae2fd6c3e92bf9269a8", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b000000000000000017ba531b66b0cdecdbc3cf271612d42265b1d3a5503044022048e574b2570893304e81957458715508df9160125def85414f5120bb8d5120610220737e6c0775977e19ff310a5480a0a402fc6b6ae6868cdd551d2b30244b15285a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203132", + "expiration": 0, + "recipientId": "AYm4tZFX4HWiwCrsXRRxTj4reW3svsEmXr", + "signature": "3044022048e574b2570893304e81957458715508df9160125def85414f5120bb8d5120610220737e6c0775977e19ff310a5480a0a402fc6b6ae6868cdd551d2b30244b15285a", + "vendorField": "Transaction 12", + "id": "fee20788502217f052eb27d20c9686364fef47f43f8838941e43448559d0df3b" + }, + "verified": false, + "id": "fee20788502217f052eb27d20c9686364fef47f43f8838941e43448559d0df3b", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AYm4tZFX4HWiwCrsXRRxTj4reW3svsEmXr", + "type": 0, + "vendorField": "Transaction 12", + "vendorFieldHex": "5472616e73616374696f6e203132", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022048e574b2570893304e81957458715508df9160125def85414f5120bb8d5120610220737e6c0775977e19ff310a5480a0a402fc6b6ae6868cdd551d2b30244b15285a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b000000000000000017257049419ec6524607a68b33a0a36ca96ac30d5d30440220106fcf7ed726167522846df88446c32ba548a325254710ee229de51b6f1dda6b022054bb708966d6978b949cbc5697d3fa653281d16c7881443377d27307ace5bccc", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203237", + "expiration": 0, + "recipientId": "AKBqANhvyzT6x99rfK1FPqk54uZ7cBz8Qa", + "signature": "30440220106fcf7ed726167522846df88446c32ba548a325254710ee229de51b6f1dda6b022054bb708966d6978b949cbc5697d3fa653281d16c7881443377d27307ace5bccc", + "vendorField": "Transaction 27", + "id": "49fc8777ddc49162a2b4badf83a18e559851ad0430b6e7a922f16ae897f4e829" + }, + "verified": false, + "id": "49fc8777ddc49162a2b4badf83a18e559851ad0430b6e7a922f16ae897f4e829", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AKBqANhvyzT6x99rfK1FPqk54uZ7cBz8Qa", + "type": 0, + "vendorField": "Transaction 27", + "vendorFieldHex": "5472616e73616374696f6e203237", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220106fcf7ed726167522846df88446c32ba548a325254710ee229de51b6f1dda6b022054bb708966d6978b949cbc5697d3fa653281d16c7881443377d27307ace5bccc", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b0000000000000000172c04bc6a74f4c3dd84a9a58123906e30786c68ca3045022100f348c5c1571df94075fe18c72472afff2a8163e6af45cd6bfc4c55bc7af895b302200140edc244687b44fdc245bc0a18edc09352a953f6ce7ff659f8935508027e93", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203239", + "expiration": 0, + "recipientId": "AKnd414ZAXQM5HX1pGKt45M6AuX9EY92d9", + "signature": "3045022100f348c5c1571df94075fe18c72472afff2a8163e6af45cd6bfc4c55bc7af895b302200140edc244687b44fdc245bc0a18edc09352a953f6ce7ff659f8935508027e93", + "vendorField": "Transaction 29", + "id": "add61d18b7230d3b8c5ccb9f9f170bfb5f9cd9b41bb57e03c7cd481c23336133" + }, + "verified": false, + "id": "add61d18b7230d3b8c5ccb9f9f170bfb5f9cd9b41bb57e03c7cd481c23336133", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AKnd414ZAXQM5HX1pGKt45M6AuX9EY92d9", + "type": 0, + "vendorField": "Transaction 29", + "vendorFieldHex": "5472616e73616374696f6e203239", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f348c5c1571df94075fe18c72472afff2a8163e6af45cd6bfc4c55bc7af895b302200140edc244687b44fdc245bc0a18edc09352a953f6ce7ff659f8935508027e93", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b0000000000000000174222f8f0ed1b116bd939ba2d602e0ece7b3f49b43045022100ec192e32473517250c25ce2dfb79082f34c63017ba2532a6e56406dad64dd8cc02207c5f0d699c9b76987e06fff3ad9788dbab9019653f0ae15555763b4e22bb02b4", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203137", + "expiration": 0, + "recipientId": "AMoa95wFinKdBnrYCSqHc7RWqWNAuVGS3d", + "signature": "3045022100ec192e32473517250c25ce2dfb79082f34c63017ba2532a6e56406dad64dd8cc02207c5f0d699c9b76987e06fff3ad9788dbab9019653f0ae15555763b4e22bb02b4", + "vendorField": "Transaction 17", + "id": "8473fd6929f72f146933a2b4e4d5e10e9eeaf2e720a1ba12095579483164e51e" + }, + "verified": false, + "id": "8473fd6929f72f146933a2b4e4d5e10e9eeaf2e720a1ba12095579483164e51e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AMoa95wFinKdBnrYCSqHc7RWqWNAuVGS3d", + "type": 0, + "vendorField": "Transaction 17", + "vendorFieldHex": "5472616e73616374696f6e203137", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ec192e32473517250c25ce2dfb79082f34c63017ba2532a6e56406dad64dd8cc02207c5f0d699c9b76987e06fff3ad9788dbab9019653f0ae15555763b4e22bb02b4", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b0000000000000000171b272cd1e6b978ae9fb815b40329988d2dc113e93044022059870246ff0cf10fd681b29f392c8d046d8344838222f0b53b96f2b436e73c42022070b12ca14c2dee42057a4bb5986461d415502f08abfded6ce85abecdaf851615", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203233", + "expiration": 0, + "recipientId": "AJFSpcKsjiK2WqspHpU6uiCSD5zGUAoMTQ", + "signature": "3044022059870246ff0cf10fd681b29f392c8d046d8344838222f0b53b96f2b436e73c42022070b12ca14c2dee42057a4bb5986461d415502f08abfded6ce85abecdaf851615", + "vendorField": "Transaction 23", + "id": "9c0afc8a3c5e80a93157df60c77191606dc4479e7de9cae4a215e94cbfd0ed30" + }, + "verified": false, + "id": "9c0afc8a3c5e80a93157df60c77191606dc4479e7de9cae4a215e94cbfd0ed30", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AJFSpcKsjiK2WqspHpU6uiCSD5zGUAoMTQ", + "type": 0, + "vendorField": "Transaction 23", + "vendorFieldHex": "5472616e73616374696f6e203233", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022059870246ff0cf10fd681b29f392c8d046d8344838222f0b53b96f2b436e73c42022070b12ca14c2dee42057a4bb5986461d415502f08abfded6ce85abecdaf851615", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017afb8147376f4e4b1c486e12385d9f5516d32785930440220105f2ab1c8a75d33f29ffd4ceddfd3b175a39cd141a976645faafee06a0708e50220178e2dd98924ed91d1691a3d747fcf8233edc33e98272a24b17b77602dd0fcee", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203133", + "expiration": 0, + "recipientId": "AXnzRARyqdectrBNQSzLQXyCXxDRzHPfBm", + "signature": "30440220105f2ab1c8a75d33f29ffd4ceddfd3b175a39cd141a976645faafee06a0708e50220178e2dd98924ed91d1691a3d747fcf8233edc33e98272a24b17b77602dd0fcee", + "vendorField": "Transaction 13", + "id": "5b4f5596695cebb5e1b9a24ed449bcfcce356f2aa1b6bf9e1d380b8048e8ad5d" + }, + "verified": false, + "id": "5b4f5596695cebb5e1b9a24ed449bcfcce356f2aa1b6bf9e1d380b8048e8ad5d", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AXnzRARyqdectrBNQSzLQXyCXxDRzHPfBm", + "type": 0, + "vendorField": "Transaction 13", + "vendorFieldHex": "5472616e73616374696f6e203133", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220105f2ab1c8a75d33f29ffd4ceddfd3b175a39cd141a976645faafee06a0708e50220178e2dd98924ed91d1691a3d747fcf8233edc33e98272a24b17b77602dd0fcee", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b0000000000000000171b21d99d4c50d090209cfb717e7e3171445a61e83044022070d12ef771ea3708d2145923d4c0b4c1eb31f8b3719a30cb9fd32566ded28eea02201a744120749b42c39479ad088a462f6f9fec468d85cdc9cf83e5cedc90940832", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2036", + "expiration": 0, + "recipientId": "AJFLScxy42BZy1dGPr8DRvnQui3suX93o7", + "signature": "3044022070d12ef771ea3708d2145923d4c0b4c1eb31f8b3719a30cb9fd32566ded28eea02201a744120749b42c39479ad088a462f6f9fec468d85cdc9cf83e5cedc90940832", + "vendorField": "Transaction 6", + "id": "27cf3c5737d61c7c84bfff48c4822da9e31220c320d3cc1d177821c066282423" + }, + "verified": false, + "id": "27cf3c5737d61c7c84bfff48c4822da9e31220c320d3cc1d177821c066282423", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AJFLScxy42BZy1dGPr8DRvnQui3suX93o7", + "type": 0, + "vendorField": "Transaction 6", + "vendorFieldHex": "5472616e73616374696f6e2036", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022070d12ef771ea3708d2145923d4c0b4c1eb31f8b3719a30cb9fd32566ded28eea02201a744120749b42c39479ad088a462f6f9fec468d85cdc9cf83e5cedc90940832", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333400c2eb0b000000000000000017263931c7543c6fc43bc3312498b241707bca3c043045022100cf63f159f40a6f75ddf5c4b206acce3813d94ab1cdee8c910dede9657d50afe60220724da18645f4ee23be41b697690e55a5e82a2ce1ed3808e104f0902de2c01c67", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203334", + "expiration": 0, + "recipientId": "AKFyqjAWiyWLHnNm7ZgRkabq5LUgimHUuf", + "signature": "3045022100cf63f159f40a6f75ddf5c4b206acce3813d94ab1cdee8c910dede9657d50afe60220724da18645f4ee23be41b697690e55a5e82a2ce1ed3808e104f0902de2c01c67", + "vendorField": "Transaction 34", + "id": "12fa416fc5d1d6dfb141d6052537b0e37462a8fd0301847dc20bfe64e5c7f33a" + }, + "verified": false, + "id": "12fa416fc5d1d6dfb141d6052537b0e37462a8fd0301847dc20bfe64e5c7f33a", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AKFyqjAWiyWLHnNm7ZgRkabq5LUgimHUuf", + "type": 0, + "vendorField": "Transaction 34", + "vendorFieldHex": "5472616e73616374696f6e203334", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100cf63f159f40a6f75ddf5c4b206acce3813d94ab1cdee8c910dede9657d50afe60220724da18645f4ee23be41b697690e55a5e82a2ce1ed3808e104f0902de2c01c67", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b0000000000000000173e1b19bf4d66515f03c6f15e868001ab959b4f403045022100e5936646e020734f85e7601323f4be503f67da4a31f05210210a4d02808af39602205db4b337e1d883b1312bc03ca0195fc77a74f860ceb7867e197d9647f5d26d33", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2035", + "expiration": 0, + "recipientId": "AMSG1NaLZswewAjDLbHEQz2haZ3wGhf3ZB", + "signature": "3045022100e5936646e020734f85e7601323f4be503f67da4a31f05210210a4d02808af39602205db4b337e1d883b1312bc03ca0195fc77a74f860ceb7867e197d9647f5d26d33", + "vendorField": "Transaction 5", + "id": "3edd9cd75a9c4da0bd0829d9a15bfa93f9e4cc832f09b430474574ae28bfa659" + }, + "verified": false, + "id": "3edd9cd75a9c4da0bd0829d9a15bfa93f9e4cc832f09b430474574ae28bfa659", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AMSG1NaLZswewAjDLbHEQz2haZ3wGhf3ZB", + "type": 0, + "vendorField": "Transaction 5", + "vendorFieldHex": "5472616e73616374696f6e2035", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e5936646e020734f85e7601323f4be503f67da4a31f05210210a4d02808af39602205db4b337e1d883b1312bc03ca0195fc77a74f860ceb7867e197d9647f5d26d33", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000170dcd69f016fb9efd5f9ae47d578ed0d04bd58b01304402205e0fe17e03c3666f986dd243bc057dff4b190f14ba1cb7e19804e4c1042deec0022059cf68c76c5b2931b83e9bd3f75cfd75267b8f35aa957cf63cce3b2226665707", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2034", + "expiration": 0, + "recipientId": "AH2rWPqT9WeFvwrUM56EeLY7Ew2eTQTr5r", + "signature": "304402205e0fe17e03c3666f986dd243bc057dff4b190f14ba1cb7e19804e4c1042deec0022059cf68c76c5b2931b83e9bd3f75cfd75267b8f35aa957cf63cce3b2226665707", + "vendorField": "Transaction 4", + "id": "34d18166b8fd36708268bc343c378fd3e1d72ec4c89563078b9616386e8c7763" + }, + "verified": false, + "id": "34d18166b8fd36708268bc343c378fd3e1d72ec4c89563078b9616386e8c7763", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AH2rWPqT9WeFvwrUM56EeLY7Ew2eTQTr5r", + "type": 0, + "vendorField": "Transaction 4", + "vendorFieldHex": "5472616e73616374696f6e2034", + "amount": "200000000", + "fee": "10000000", + "signature": "304402205e0fe17e03c3666f986dd243bc057dff4b190f14ba1cb7e19804e4c1042deec0022059cf68c76c5b2931b83e9bd3f75cfd75267b8f35aa957cf63cce3b2226665707", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b00000000000000001705902ff13f07ff4a607ca76390eca20df01539183045022100967f65a4252ef1e2f1f69b754cc8f3b19046af3c7efae86baf81a85031311709022075f0ebbdf75c720696c925bee4f6161bf11a279bf7e9aaaf67234a53dcd614c2", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203234", + "expiration": 0, + "recipientId": "AGHHkmKccHarK97aMYHvhFMpDTwX5cuJpG", + "signature": "3045022100967f65a4252ef1e2f1f69b754cc8f3b19046af3c7efae86baf81a85031311709022075f0ebbdf75c720696c925bee4f6161bf11a279bf7e9aaaf67234a53dcd614c2", + "vendorField": "Transaction 24", + "id": "2614740b90b174b59976ba912df669f2f0be380831c9973dcb73972cc04674b7" + }, + "verified": false, + "id": "2614740b90b174b59976ba912df669f2f0be380831c9973dcb73972cc04674b7", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AGHHkmKccHarK97aMYHvhFMpDTwX5cuJpG", + "type": 0, + "vendorField": "Transaction 24", + "vendorFieldHex": "5472616e73616374696f6e203234", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100967f65a4252ef1e2f1f69b754cc8f3b19046af3c7efae86baf81a85031311709022075f0ebbdf75c720696c925bee4f6161bf11a279bf7e9aaaf67234a53dcd614c2", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b0000000000000000175e0496aeecc3ed4be7f98c8679efc4b3d9a398fb3045022100f5bf4a65a49d712c81d8ceb9180a17527852ae393f3fbee6f556f695ea1be8100220332353e697d4916247ec68f40764c5b6411a45d0b3db65d19b699b92e5fa4fb7", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2038", + "expiration": 0, + "recipientId": "AQLzfMPcSMoVzSuTtqsmmuMM43ocf4sz1c", + "signature": "3045022100f5bf4a65a49d712c81d8ceb9180a17527852ae393f3fbee6f556f695ea1be8100220332353e697d4916247ec68f40764c5b6411a45d0b3db65d19b699b92e5fa4fb7", + "vendorField": "Transaction 8", + "id": "f68eb2d1d4527e80901cd19b31c87f44845fd9659141c07278859911d0555f0a" + }, + "verified": false, + "id": "f68eb2d1d4527e80901cd19b31c87f44845fd9659141c07278859911d0555f0a", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AQLzfMPcSMoVzSuTtqsmmuMM43ocf4sz1c", + "type": 0, + "vendorField": "Transaction 8", + "vendorFieldHex": "5472616e73616374696f6e2038", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f5bf4a65a49d712c81d8ceb9180a17527852ae393f3fbee6f556f695ea1be8100220332353e697d4916247ec68f40764c5b6411a45d0b3db65d19b699b92e5fa4fb7", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b000000000000000017af608296a6d81f700f792a6ac9da2d170749c40e3044022049637b4877124be3de9ba317970c35fe2f16dce72fe3f5752c1df56b1c14bc1c0220765bd7c7245298676e9943b7443548d723c60def0b7217fa6d991d5866406932", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2038", + "expiration": 0, + "recipientId": "AXmBWi4TBzmAZgdmRZdLT7sk2VJZtHtvUP", + "signature": "3044022049637b4877124be3de9ba317970c35fe2f16dce72fe3f5752c1df56b1c14bc1c0220765bd7c7245298676e9943b7443548d723c60def0b7217fa6d991d5866406932", + "vendorField": "Transaction 8", + "id": "53dc7dcf859b985e43a81d1813a1d54937de846d935d333500d7e5fc5c3bb548" + }, + "verified": false, + "id": "53dc7dcf859b985e43a81d1813a1d54937de846d935d333500d7e5fc5c3bb548", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AXmBWi4TBzmAZgdmRZdLT7sk2VJZtHtvUP", + "type": 0, + "vendorField": "Transaction 8", + "vendorFieldHex": "5472616e73616374696f6e2038", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022049637b4877124be3de9ba317970c35fe2f16dce72fe3f5752c1df56b1c14bc1c0220765bd7c7245298676e9943b7443548d723c60def0b7217fa6d991d5866406932", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b00000000000000001755d63414206f2d2acfb498cb2c2c6f0b9b84b97e3045022100ff478f5c5d86597ebc267e5e819782a5316e79987df57d154046c9c7b742d2e602206a0092b9a8709c3fd1f6168e1cba5bb8e84789140111addd99a3cff0c8dc663d", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203330", + "expiration": 0, + "recipientId": "APbjgvGm593CBT9qVzvVUUqpeHXsPoep1M", + "signature": "3045022100ff478f5c5d86597ebc267e5e819782a5316e79987df57d154046c9c7b742d2e602206a0092b9a8709c3fd1f6168e1cba5bb8e84789140111addd99a3cff0c8dc663d", + "vendorField": "Transaction 30", + "id": "d8e05bf677daeff8b4b0a54e08b3e03656baed782379a15bf9c2514c7c2e7012" + }, + "verified": false, + "id": "d8e05bf677daeff8b4b0a54e08b3e03656baed782379a15bf9c2514c7c2e7012", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APbjgvGm593CBT9qVzvVUUqpeHXsPoep1M", + "type": 0, + "vendorField": "Transaction 30", + "vendorFieldHex": "5472616e73616374696f6e203330", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100ff478f5c5d86597ebc267e5e819782a5316e79987df57d154046c9c7b742d2e602206a0092b9a8709c3fd1f6168e1cba5bb8e84789140111addd99a3cff0c8dc663d", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b00000000000000001723f80cdfc6e8737f06a9c02d6ca93594388767f83044022055b7ca7c018bf871e7200ebef17ea826b817305e942b3403ab57a1d2f138b75f02202e8a79588f7802aed93470cce823a5f764715b23ecd84fe531d68019b5792bfb", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203134", + "expiration": 0, + "recipientId": "AK44T3mwTGAduzFB3V61sVUwoZiXsypLob", + "signature": "3044022055b7ca7c018bf871e7200ebef17ea826b817305e942b3403ab57a1d2f138b75f02202e8a79588f7802aed93470cce823a5f764715b23ecd84fe531d68019b5792bfb", + "vendorField": "Transaction 14", + "id": "fd678492c1a5ee208ab815e8bb3868aa296cd1c0f574635205969249b42eff1b" + }, + "verified": false, + "id": "fd678492c1a5ee208ab815e8bb3868aa296cd1c0f574635205969249b42eff1b", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AK44T3mwTGAduzFB3V61sVUwoZiXsypLob", + "type": 0, + "vendorField": "Transaction 14", + "vendorFieldHex": "5472616e73616374696f6e203134", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022055b7ca7c018bf871e7200ebef17ea826b817305e942b3403ab57a1d2f138b75f02202e8a79588f7802aed93470cce823a5f764715b23ecd84fe531d68019b5792bfb", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b00000000000000001773938849133e49752e6322fb06520820eb82ab5230440220546f1457a0e381e332b2ae915a4505a86cb15816cd2cb143411a433847c17aed02202e05926f9270e36d9d9371531d68d365de3ed3ccbb7a2331c8a3215a318c451a", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203131", + "expiration": 0, + "recipientId": "ASJz6Hcak9JeMpCKgiQPVf3U4b8UnZ79Rq", + "signature": "30440220546f1457a0e381e332b2ae915a4505a86cb15816cd2cb143411a433847c17aed02202e05926f9270e36d9d9371531d68d365de3ed3ccbb7a2331c8a3215a318c451a", + "vendorField": "Transaction 11", + "id": "0f9e0912bc9c1c845a116281be23b3aaab97ebed8b172de4aa4321a6e8203505" + }, + "verified": false, + "id": "0f9e0912bc9c1c845a116281be23b3aaab97ebed8b172de4aa4321a6e8203505", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ASJz6Hcak9JeMpCKgiQPVf3U4b8UnZ79Rq", + "type": 0, + "vendorField": "Transaction 11", + "vendorFieldHex": "5472616e73616374696f6e203131", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220546f1457a0e381e332b2ae915a4505a86cb15816cd2cb143411a433847c17aed02202e05926f9270e36d9d9371531d68d365de3ed3ccbb7a2331c8a3215a318c451a", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333600c2eb0b0000000000000000173abaff16c478b48ec930bc54dcb2a85767ae61833045022100b9db8445e61c6645aae74c4ee5c536ae0bfe18f63dbc5316085f895db7ae8706022029bbda8d6f0609bdb34eca51a050327a86120733cca1df7eee94f598d0baa7f5", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203336", + "expiration": 0, + "recipientId": "AM8QrN87nGuYB1Jwni8WdSceLdvNTjCPoC", + "signature": "3045022100b9db8445e61c6645aae74c4ee5c536ae0bfe18f63dbc5316085f895db7ae8706022029bbda8d6f0609bdb34eca51a050327a86120733cca1df7eee94f598d0baa7f5", + "vendorField": "Transaction 36", + "id": "1eb72cbf3f052aa1e13f6b5838b22a05685ffc99f5c19f5051da2e63be88befe" + }, + "verified": false, + "id": "1eb72cbf3f052aa1e13f6b5838b22a05685ffc99f5c19f5051da2e63be88befe", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AM8QrN87nGuYB1Jwni8WdSceLdvNTjCPoC", + "type": 0, + "vendorField": "Transaction 36", + "vendorFieldHex": "5472616e73616374696f6e203336", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100b9db8445e61c6645aae74c4ee5c536ae0bfe18f63dbc5316085f895db7ae8706022029bbda8d6f0609bdb34eca51a050327a86120733cca1df7eee94f598d0baa7f5", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b000000000000000017236b377b2f235de51e10128a7a5c5cad4c6e1dd130440220186bc71fdfaedc0c31285917f74997cfd66ea4a72e690d480d5f8ef5e7a1d1a20220710c383ea6e8b4f64ed2eaffae33a1f0bafff1fb1c578a1b369dd8151ef3f0bc", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203130", + "expiration": 0, + "recipientId": "AK19jm62mDF8b62XtszDf1sqGeVkGmp7CE", + "signature": "30440220186bc71fdfaedc0c31285917f74997cfd66ea4a72e690d480d5f8ef5e7a1d1a20220710c383ea6e8b4f64ed2eaffae33a1f0bafff1fb1c578a1b369dd8151ef3f0bc", + "vendorField": "Transaction 10", + "id": "e75b3d52fcf745c1b64f6fc2863b673bbe61d687ed0ca7f68c7e88ceb6fae8f9" + }, + "verified": false, + "id": "e75b3d52fcf745c1b64f6fc2863b673bbe61d687ed0ca7f68c7e88ceb6fae8f9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AK19jm62mDF8b62XtszDf1sqGeVkGmp7CE", + "type": 0, + "vendorField": "Transaction 10", + "vendorFieldHex": "5472616e73616374696f6e203130", + "amount": "200000000", + "fee": "10000000", + "signature": "30440220186bc71fdfaedc0c31285917f74997cfd66ea4a72e690d480d5f8ef5e7a1d1a20220710c383ea6e8b4f64ed2eaffae33a1f0bafff1fb1c578a1b369dd8151ef3f0bc", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b000000000000000017b35eba0e5bbe1a15c6e2504779938e94572bc7e1304402204f1f1527f8d037d69ff3de5766b719b4b84f29d5a79ff390877a84bb9856d5da022008656e66808082b8dad2ef6d2168e031f04db664eaa3bed647395b42ffc9f759", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203230", + "expiration": 0, + "recipientId": "AY8J5ZuVC4p5QdEsuZSLHKjXsGGvvvr2mE", + "signature": "304402204f1f1527f8d037d69ff3de5766b719b4b84f29d5a79ff390877a84bb9856d5da022008656e66808082b8dad2ef6d2168e031f04db664eaa3bed647395b42ffc9f759", + "vendorField": "Transaction 20", + "id": "a67eea8b6191b8d35cbab1dc45d9a6795f86c0299557764a7d3ed7794b64d699" + }, + "verified": false, + "id": "a67eea8b6191b8d35cbab1dc45d9a6795f86c0299557764a7d3ed7794b64d699", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AY8J5ZuVC4p5QdEsuZSLHKjXsGGvvvr2mE", + "type": 0, + "vendorField": "Transaction 20", + "vendorFieldHex": "5472616e73616374696f6e203230", + "amount": "200000000", + "fee": "10000000", + "signature": "304402204f1f1527f8d037d69ff3de5766b719b4b84f29d5a79ff390877a84bb9856d5da022008656e66808082b8dad2ef6d2168e031f04db664eaa3bed647395b42ffc9f759", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b0000000000000000179c375cc35993d1b822494819e4e36f0035d2fc9b3045022100eacd0a48fcee232b4592465351612f802e31e0aec809a04aedb1ffc77b67970a02207ddeb454d49fcd5dd03eabcb28fd6a54356ee8ce513ee4eef6c3dad2bcc035d1", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2038", + "expiration": 0, + "recipientId": "AW1sP4tJNL5PdXGFrnxvTzDhgVkXYtEKWW", + "signature": "3045022100eacd0a48fcee232b4592465351612f802e31e0aec809a04aedb1ffc77b67970a02207ddeb454d49fcd5dd03eabcb28fd6a54356ee8ce513ee4eef6c3dad2bcc035d1", + "vendorField": "Transaction 8", + "id": "17f6fe38989462776bfee0b769db550913f6f18f5d7a0901998fcd3203f2b224" + }, + "verified": false, + "id": "17f6fe38989462776bfee0b769db550913f6f18f5d7a0901998fcd3203f2b224", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AW1sP4tJNL5PdXGFrnxvTzDhgVkXYtEKWW", + "type": 0, + "vendorField": "Transaction 8", + "vendorFieldHex": "5472616e73616374696f6e2038", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100eacd0a48fcee232b4592465351612f802e31e0aec809a04aedb1ffc77b67970a02207ddeb454d49fcd5dd03eabcb28fd6a54356ee8ce513ee4eef6c3dad2bcc035d1", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333300c2eb0b000000000000000017db12692f9dc88fe4d380436e4a3824076a4f843f30450221008aaa31bd67638a4896b251f1e34148abcb9834c8324ad2aea68b85747525a98502203021aae408cd18175eb04e45a8bcedc5fffaa6e6effdb1c11a336976be49f240", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203333", + "expiration": 0, + "recipientId": "AbkDgihWECuCuz5hNmag2w25JQ9E1FGaNX", + "signature": "30450221008aaa31bd67638a4896b251f1e34148abcb9834c8324ad2aea68b85747525a98502203021aae408cd18175eb04e45a8bcedc5fffaa6e6effdb1c11a336976be49f240", + "vendorField": "Transaction 33", + "id": "0058abf9805e5e8ea182830c638704da731da75407c3b676dc56ac37c97de148" + }, + "verified": false, + "id": "0058abf9805e5e8ea182830c638704da731da75407c3b676dc56ac37c97de148", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AbkDgihWECuCuz5hNmag2w25JQ9E1FGaNX", + "type": 0, + "vendorField": "Transaction 33", + "vendorFieldHex": "5472616e73616374696f6e203333", + "amount": "200000000", + "fee": "10000000", + "signature": "30450221008aaa31bd67638a4896b251f1e34148abcb9834c8324ad2aea68b85747525a98502203021aae408cd18175eb04e45a8bcedc5fffaa6e6effdb1c11a336976be49f240", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000176d9b88a2a27357f2d7eeae78b386d2edf8df3b84304402200b48815ef066a0eac8a79ec4d0fc4f22b105eff437163af0573683d879b5a93802200bf1f2c2977b4065f5522bb8b917141cb82806446e57593d14dd9d632e0f7013", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2034", + "expiration": 0, + "recipientId": "ARmRd5edg5V3mRas7gLMyfsvtSoXacZPjo", + "signature": "304402200b48815ef066a0eac8a79ec4d0fc4f22b105eff437163af0573683d879b5a93802200bf1f2c2977b4065f5522bb8b917141cb82806446e57593d14dd9d632e0f7013", + "vendorField": "Transaction 4", + "id": "a2da696115bf832cc4ed9458ff608ce10f339876f77d04ea28bbb22eea10aec2" + }, + "verified": false, + "id": "a2da696115bf832cc4ed9458ff608ce10f339876f77d04ea28bbb22eea10aec2", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "ARmRd5edg5V3mRas7gLMyfsvtSoXacZPjo", + "type": 0, + "vendorField": "Transaction 4", + "vendorFieldHex": "5472616e73616374696f6e2034", + "amount": "200000000", + "fee": "10000000", + "signature": "304402200b48815ef066a0eac8a79ec4d0fc4f22b105eff437163af0573683d879b5a93802200bf1f2c2977b4065f5522bb8b917141cb82806446e57593d14dd9d632e0f7013", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b0000000000000000176358f2ca1a4c272cce0eedb92f3bf8b837fd8cf03044022070c9c7d962d31b5189bf1747aa6e06fd35ae00e0b12efa6899173b3ea5e343180220146328f4a1ed1548a5a95ca8489af16a095cf0095924084f5a508c9e010d9e86", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203130", + "expiration": 0, + "recipientId": "AQqB6jSG9a4xKwSFCnwY2yRJJAfYAjyvZA", + "signature": "3044022070c9c7d962d31b5189bf1747aa6e06fd35ae00e0b12efa6899173b3ea5e343180220146328f4a1ed1548a5a95ca8489af16a095cf0095924084f5a508c9e010d9e86", + "vendorField": "Transaction 10", + "id": "3a125825e9e154c2cb546f5aadabd72e2a15cb75e6746d9edd425ff4c921bc51" + }, + "verified": false, + "id": "3a125825e9e154c2cb546f5aadabd72e2a15cb75e6746d9edd425ff4c921bc51", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AQqB6jSG9a4xKwSFCnwY2yRJJAfYAjyvZA", + "type": 0, + "vendorField": "Transaction 10", + "vendorFieldHex": "5472616e73616374696f6e203130", + "amount": "200000000", + "fee": "10000000", + "signature": "3044022070c9c7d962d31b5189bf1747aa6e06fd35ae00e0b12efa6899173b3ea5e343180220146328f4a1ed1548a5a95ca8489af16a095cf0095924084f5a508c9e010d9e86", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333900c2eb0b0000000000000000171b96e2b2a75c0ae6c7fa5626e3d79c20175f65913045022100c8ec04674367b578456e43088752a04b0f59de4bbda197ae38a94a1ad0db39f2022011bedc437d4a025b2eb0a0df29d61b55aa2d4e49a0ab01dd48e050833c153d15", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203339", + "expiration": 0, + "recipientId": "AJHkePW35duSTiqbVxcFbtTvvnQr2PBgYK", + "signature": "3045022100c8ec04674367b578456e43088752a04b0f59de4bbda197ae38a94a1ad0db39f2022011bedc437d4a025b2eb0a0df29d61b55aa2d4e49a0ab01dd48e050833c153d15", + "vendorField": "Transaction 39", + "id": "9d1d3da7979adffb6a7d6dd4e4d6a4863ae412c1e909595bca2bb6dee68b812c" + }, + "verified": false, + "id": "9d1d3da7979adffb6a7d6dd4e4d6a4863ae412c1e909595bca2bb6dee68b812c", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AJHkePW35duSTiqbVxcFbtTvvnQr2PBgYK", + "type": 0, + "vendorField": "Transaction 39", + "vendorFieldHex": "5472616e73616374696f6e203339", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100c8ec04674367b578456e43088752a04b0f59de4bbda197ae38a94a1ad0db39f2022011bedc437d4a025b2eb0a0df29d61b55aa2d4e49a0ab01dd48e050833c153d15", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017f8c689bc5455e9c7add499f969c3979f6cf1e6c8304502210096e14fce152f9f532f1836d260f535c3316fa8ab5e1bdf1e4c1275fcea5444dd02203be1c81790b12abacd63b89b3fe11cd664a2c9a3519eac0303e3d091dfaac54c", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203133", + "expiration": 0, + "recipientId": "AeTH4jfbYikFC52t4DaDM2sBF83d5AaDM1", + "signature": "304502210096e14fce152f9f532f1836d260f535c3316fa8ab5e1bdf1e4c1275fcea5444dd02203be1c81790b12abacd63b89b3fe11cd664a2c9a3519eac0303e3d091dfaac54c", + "vendorField": "Transaction 13", + "id": "14d7359e411e9c9eac713973be00b3bb4e2960648b67f0e6f0dc469a25a7c6d9" + }, + "verified": false, + "id": "14d7359e411e9c9eac713973be00b3bb4e2960648b67f0e6f0dc469a25a7c6d9", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AeTH4jfbYikFC52t4DaDM2sBF83d5AaDM1", + "type": 0, + "vendorField": "Transaction 13", + "vendorFieldHex": "5472616e73616374696f6e203133", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210096e14fce152f9f532f1836d260f535c3316fa8ab5e1bdf1e4c1275fcea5444dd02203be1c81790b12abacd63b89b3fe11cd664a2c9a3519eac0303e3d091dfaac54c", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b0000000000000000172fe38431dd11fa9a38bac772feeeb266a13ddb0b3045022100e67098e07447a4f767c9309ba315e89b332cabee4259887111745e83f9cdc4cd0220531cb5f0372caac1ed702394f9722dcf1106f115dc772272fb470db885eb49eb", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203238", + "expiration": 0, + "recipientId": "AL95xdVWbvjmU7132YdCBKfLUC9CVGqjDL", + "signature": "3045022100e67098e07447a4f767c9309ba315e89b332cabee4259887111745e83f9cdc4cd0220531cb5f0372caac1ed702394f9722dcf1106f115dc772272fb470db885eb49eb", + "vendorField": "Transaction 28", + "id": "82001d46768ed6b5b456159f6af708140711135403d57119aa931c1683b2a26e" + }, + "verified": false, + "id": "82001d46768ed6b5b456159f6af708140711135403d57119aa931c1683b2a26e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AL95xdVWbvjmU7132YdCBKfLUC9CVGqjDL", + "type": 0, + "vendorField": "Transaction 28", + "vendorFieldHex": "5472616e73616374696f6e203238", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100e67098e07447a4f767c9309ba315e89b332cabee4259887111745e83f9cdc4cd0220531cb5f0372caac1ed702394f9722dcf1106f115dc772272fb470db885eb49eb", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b000000000000000017fee784411bbe0538f549eee19cf6593ee1d0378f304402202fd4dd9351912093bf7289fc6a2204cedea60dee16bf6c169f82fd919e94326402207d2f7ffa0cd4e559e39421fec818020859e080d66d4511f6b86af8922c4bbfcf", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2036", + "expiration": 0, + "recipientId": "Af1gdHBt627NsJXZUwW3Dn8gWQxnmeGj1F", + "signature": "304402202fd4dd9351912093bf7289fc6a2204cedea60dee16bf6c169f82fd919e94326402207d2f7ffa0cd4e559e39421fec818020859e080d66d4511f6b86af8922c4bbfcf", + "vendorField": "Transaction 6", + "id": "4003f9ce2b82fe56150892d2592de29b7878342dd92fcf431f4fc4c261f57156" + }, + "verified": false, + "id": "4003f9ce2b82fe56150892d2592de29b7878342dd92fcf431f4fc4c261f57156", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "Af1gdHBt627NsJXZUwW3Dn8gWQxnmeGj1F", + "type": 0, + "vendorField": "Transaction 6", + "vendorFieldHex": "5472616e73616374696f6e2036", + "amount": "200000000", + "fee": "10000000", + "signature": "304402202fd4dd9351912093bf7289fc6a2204cedea60dee16bf6c169f82fd919e94326402207d2f7ffa0cd4e559e39421fec818020859e080d66d4511f6b86af8922c4bbfcf", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b000000000000000017910efbc8d502d7a24a13533a6de93b41c5c74f4c3045022100f1fdf68f3d428be0f9dabd8cf20aabd4bc576ab63e666c339d7d17597f8b023b02203854f0db9240abf17d2f245478ad287d8514c1670fe2604fd7772adc23fae095", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e203136", + "expiration": 0, + "recipientId": "AUzsaPT53NvnhgUwJmKmy7RKWkD5RrZQ1t", + "signature": "3045022100f1fdf68f3d428be0f9dabd8cf20aabd4bc576ab63e666c339d7d17597f8b023b02203854f0db9240abf17d2f245478ad287d8514c1670fe2604fd7772adc23fae095", + "vendorField": "Transaction 16", + "id": "f7b2236a623feff92b3dbb7728c2d15c120693a16007cf3087d288b7beb80797" + }, + "verified": false, + "id": "f7b2236a623feff92b3dbb7728c2d15c120693a16007cf3087d288b7beb80797", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AUzsaPT53NvnhgUwJmKmy7RKWkD5RrZQ1t", + "type": 0, + "vendorField": "Transaction 16", + "vendorFieldHex": "5472616e73616374696f6e203136", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100f1fdf68f3d428be0f9dabd8cf20aabd4bc576ab63e666c339d7d17597f8b023b02203854f0db9240abf17d2f245478ad287d8514c1670fe2604fd7772adc23fae095", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b00000000000000001753fdee9530e0d82aa9fcb0d8a378bc0647584765304502210081b45f9b0bdf11c4e0944eef58a960b99ed022643b2f3d18dba890c1f02d4364022050eeca2c2e24440f6f18fec7f019dd27c54f7cb04b152a1a75b324816a859c79", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2035", + "expiration": 0, + "recipientId": "APRyvvTGyM5yRRzmTDjVEHhP7xMsEPyNrV", + "signature": "304502210081b45f9b0bdf11c4e0944eef58a960b99ed022643b2f3d18dba890c1f02d4364022050eeca2c2e24440f6f18fec7f019dd27c54f7cb04b152a1a75b324816a859c79", + "vendorField": "Transaction 5", + "id": "374d96b38602581d6074ba4822f8b19264236a255453d9c7ac37f4cd5eab1b3e" + }, + "verified": false, + "id": "374d96b38602581d6074ba4822f8b19264236a255453d9c7ac37f4cd5eab1b3e", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "APRyvvTGyM5yRRzmTDjVEHhP7xMsEPyNrV", + "type": 0, + "vendorField": "Transaction 5", + "vendorFieldHex": "5472616e73616374696f6e2035", + "amount": "200000000", + "fee": "10000000", + "signature": "304502210081b45f9b0bdf11c4e0944eef58a960b99ed022643b2f3d18dba890c1f02d4364022050eeca2c2e24440f6f18fec7f019dd27c54f7cb04b152a1a75b324816a859c79", + "expiration": 0 + }, + { + "serialized": "ff01170052f0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b000000000000000017751964a7f75cd482dcc3838024c6dfa7e05f2eb83045022100af03a6ea5d5d9a997465d16580a7e59fe6bc1a4cdf85304f0fd14b76ea83d88e02206be74c7f22acbfb8496bb10c60024dfbe0924de7e26b912735826c8a90908c4e", + "data": { + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2031", + "expiration": 0, + "recipientId": "AST38HtwFJrJctcmFRP8Rqyy1eVZ4sVYBL", + "signature": "3045022100af03a6ea5d5d9a997465d16580a7e59fe6bc1a4cdf85304f0fd14b76ea83d88e02206be74c7f22acbfb8496bb10c60024dfbe0924de7e26b912735826c8a90908c4e", + "vendorField": "Transaction 1", + "id": "804b281bf92f80cba9fa7098d729d3a04371f98be93c39d8b54ecf3ce3bdd842" + }, + "verified": false, + "id": "804b281bf92f80cba9fa7098d729d3a04371f98be93c39d8b54ecf3ce3bdd842", + "version": 1, + "timestamp": 58126418, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "recipientId": "AST38HtwFJrJctcmFRP8Rqyy1eVZ4sVYBL", + "type": 0, + "vendorField": "Transaction 1", + "vendorFieldHex": "5472616e73616374696f6e2031", + "amount": "200000000", + "fee": "10000000", + "signature": "3045022100af03a6ea5d5d9a997465d16580a7e59fe6bc1a4cdf85304f0fd14b76ea83d88e02206be74c7f22acbfb8496bb10c60024dfbe0924de7e26b912735826c8a90908c4e", + "expiration": 0 + } + ], + "idHex": "d9401ad36a03b8b4", + "id": "15654541800058894516" +} diff --git a/benchmark/fixtures/block/serialized/no-transactions.txt b/benchmark/fixtures/block/serialized/no-transactions.txt new file mode 100644 index 0000000000..508d427dca --- /dev/null +++ b/benchmark/fixtures/block/serialized/no-transactions.txt @@ -0,0 +1 @@ +0000000052f0760313000000a099b7651f0e5eb89600000000ac23fc06000000002f6859000000000000000000000000c0120000e5a7e9b5a8a8e2f47f7d8a532e0e9c43d44052dc6c6339ad57246e9a339665e303a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a304402204e31f1ae02cbcf2bb936e225f9f9db332ac275577b777a389b2d713e48b78c9002203f11c4ee0d30d2e10b2cb4a7fb59569e761571971ffe1be5abaa32fdc42a056b diff --git a/benchmark/fixtures/block/serialized/transactions.txt b/benchmark/fixtures/block/serialized/transactions.txt new file mode 100644 index 0000000000..689bed3e80 --- /dev/null +++ b/benchmark/fixtures/block/serialized/transactions.txt @@ -0,0 +1 @@ +0000000052f0760313000000a099b7651f0e5eb89600000000ac23fc06000000002f6859000000000000000000000000c0120000e5a7e9b5a8a8e2f47f7d8a532e0e9c43d44052dc6c6339ad57246e9a339665e303a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a304402204e31f1ae02cbcf2bb936e225f9f9db332ac275577b777a389b2d713e48b78c9002203f11c4ee0d30d2e10b2cb4a7fb59569e761571971ffe1be5abaa32fdc42a056ba7000000a8000000a7000000a8000000a8000000a7000000a8000000a7000000a6000000a8000000a6000000a8000000a7000000a7000000a7000000a8000000a8000000a6000000a7000000a7000000a8000000a7000000a8000000a7000000a7000000a7000000a7000000a7000000a8000000a8000000a8000000a6000000a8000000a8000000a7000000a6000000a8000000a8000000a7000000a7000000a6000000a7000000a8000000a7000000a7000000a8000000a8000000a8000000a8000000a7000000a8000000a8000000a8000000a8000000a8000000a7000000a8000000a8000000a7000000a6000000a7000000a7000000a8000000a8000000a7000000a8000000a6000000a7000000a6000000a8000000a8000000a7000000a7000000a7000000a8000000a7000000a7000000a7000000a8000000a8000000a8000000a8000000a8000000a7000000a8000000a7000000a8000000a7000000a7000000a6000000a8000000a7000000a7000000a8000000a6000000a7000000a7000000a8000000a7000000a8000000a8000000a8000000a8000000a8000000a8000000a7000000a8000000a7000000a8000000a7000000a8000000a8000000a8000000a8000000a7000000a7000000a7000000a7000000a8000000a7000000a7000000a7000000a8000000a8000000a7000000a7000000a6000000a8000000a7000000a6000000a8000000a7000000a6000000a8000000a7000000a7000000a8000000a7000000a7000000a7000000a8000000a6000000a7000000a8000000a8000000a8000000a6000000a8000000a7000000a7000000ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b00000000000000001759e7dc56557733804418f0ea6fd3b2573a9aabdd3045022100bac5b7699748a891b39ff5439e16ea1a694e93954b248be6b8082da01e5386310220129eb06a58b9f80d36ea3cdc903e6cc0240bbe1d371339ffe15c87742af1427dff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20343000c2eb0b00000000000000001779265225b00860251a567d176a8927c8416d27f7304502210090d88b95320b5e0d51eec36a007bfe1f0a95c7b2c4f9ad00e833bc9f2dd4160102203c4f24c4cb1f8faa60ab139a2245c977eb39b4b6e09ea1d575beede498cf0908ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333600c2eb0b00000000000000001740d8fa2bdb69bd24ff0b861241632fa355be348a3044022014e308901e42c8881964cb4abc5af1122e1be51cf39548fb0461f44cf26394410220484234dd58a91ffffd0439c158c47f08652f1c180148fc41caa62421df962dceff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b000000000000000017cce58b0a597c0eb2b10b68178e6bddd32e1e15193045022100c0cd610e0230d66200c15b6b4e1e34806ee17db7ec593b395f0b33f1544c3b8c0220211ce028811af38210c3f4768110df1173c373ed89574e3d0eee7275c9b660caff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b00000000000000001737cb57f4bf6e9f4e64fe975a2b26d8ad37b5859b304502210083bd4d5ac41a8072672573c2fe008d5a626306b7d5bb3b775e429bd50a3a51a20220422ee1674f478bcca69527ccd5347c711aa04026702eb258658935aeeb451621ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b0000000000000000174736fe7ebfdf155ab1eaa5da635391ec140ceff43045022100c0f7ddd725c781c57efcec81aa42872b1791320889b820d8db2da9172df6cc150220560872a2e522fe8851f9642e42551e72ebc8e0bfa0428cb535dc4285a62337d5ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b0000000000000000172f5a665ddf88e839f2be0898b0a00b30606988233045022100e768703636589e566a237a52b7ffc47f33a90b43378b72ed8c4d8a253c3beff102205cbd729b8b00e0d27c342e174b136f11152b52b8c243b0f0d16cbfe9408eb700ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017b384f7340916f6343bb04f6839faaa3743c2f28f304402206f203df1a332df9029eb362d75af589f391f301330ac4f9ac9311f4c46e26ac602201fc0ac2e97b2ed60737644f1906fb7265815db6a2013c3df674e8274a8352f0eff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b0000000000000000178c86c220a027fb8e55429eaa91c92fbe9fc978aa3044022061dd2066ec8fed52d6992b91f724fc4b10d5ee772d2bd2e7d980501d773c0c1a0220189932d1ea738fd54227ec0019ddd7446b216cb0d839f716a7899521d47ca15cff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b0000000000000000170e52656c664a3401457644d379eccf63219051ee304502210090364cc31769fe896d265797f1048147f374ce55d5b4544403459e95f66567cb0220246454cdf2961c4246b00a70f0c9e5f853f5e3ba34ada1f45ba5b40ad86a5c8eff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b000000000000000017c277b97583e4e3709292528d50a988631d73bb27304402201955362c3285d9452ff7a212fdb963cc80346e1d1ec883cffba07e7fa53929d7022013cc3982a6c3fa482e1545b5a856b4589f2212782c7aa4efde6863d092971187ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b0000000000000000170a953805b93337eac6e84e7546ce431ffa11e071304502210089b1238ad440434ddadd661bbe1c9545990f9d7e18625ea6d1501608cb408168022002bd4e6022f29d967fa28b3030ad6f5a50d3d82b8a2d0f851b6c1d266745deefff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b0000000000000000176b7f10a49c1e137a728a6952f0b5236b841d677d30440220779694b27ec4fc1861b071708caaa0f57975f1335e9cacd71d4e7880cd4b1fcb022006badd77518de6eca6d7149c240ca7d028063552f4653db46794e818b21e047aff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b000000000000000017fdebc4b239afc2e6903ed93105125aa256416a3130440220405044882c636f9cd266ba6dab3fb1044b7225e6c8c31823b1c07f643dc2246c02202bc752c7c077310a1a9b07431b32f6333492b6cd18f0eabdd6250fe827e8da74ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b000000000000000017d5da08ba4319905bc4d2052d603e0ca776b73fb73045022100aceb5f53684a8c9c8339132f30369a82677cee0ed4485421a70a34016ac61a8d0220499ddca0d7a6180452c25f109f737a8d982674f0dd3f2d42a191c5790dfbf34eff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b000000000000000017594547725060beb91e991aceb0cceeec84d4b7953045022100f966cc6d0d00932284e8088557cc113378847236cb22151b8bbd10bd9acfaa7602203bd4e93dcd72343a9dfe60b36d7e0deaaaffe7676ef6083e839cd2451aac7f36ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333200c2eb0b00000000000000001762ca3bd2c81b0ee0730b2edfb63dafc7510ffd703045022100cfdefde6923cd0c64d2048d6859dde6ec75b335883043cd547153df64d83e80c0220341247ce11b798fb90cc0a3f49e2413e826d8b020a0dbabfbf7104b8d4829b16ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b0000000000000000175099ae927c19d292911fcdc5427e5ffbe3fb104030440220177594e9b7d1966081acb422076bf4a585ff2e715667b048d74f63ca7c7ff8e502203b2505cec5776e092bcf6a56bab9fa8198d607f554f7794da9d51aa75282bdf2ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b0000000000000000174a0005e4612043b8a2f0b8d6a4fddf19a4c5a4103044022051cd54f7d947ef4f7f673ae2af5b6410c109a9fe788cd99c0273d0effdf6109702206705b4362c7fdbc57b10afac5e98ad274686de1d30ac3859d6cd73241e3c1851ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b0000000000000000177c6239437aa46aa5ef2075666ca4297eb9f8410f30440220185d5b31db25b2feb61564f36d57262e2c86826dd71d0100f8fe7e208aecfa2002200c38836a96b603cef11d506ae4fd6604b364197a342dd3ba6bf4944d1b1a7985ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b0000000000000000177fe700020debbafcaf8f8c1a862289916a11fb693045022100b47e7694ac15badd2f31e1cdac3be26dc454902a0855d8c989246c31126631c5022049691f87c1c178db989a6d8c1f42e24ba4ae1e767163378afb9a028112c37500ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b0000000000000000174c2241fe6d8f9d5d11fad9462c878784095518df304402205f405a4cd637263fe0a168b9ceaa848c96db344ecd7b16ef0263e2db7d9c3020022063978a6a0b77c796a279e404d8d736e99810205517f797e5787f10db7ed469d9ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333500c2eb0b00000000000000001711beac93f240795e4b480aebc0103bee86580ba33045022100b9c5d38b2eb6a7cd625ca8d4d9f9856a95516c9eb0971bb77ce71e6f2c3955b5022029d762f117a27b4ec61abd6b9f929e4b95bff88b687a8fbcd424d51a958bb7aaff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b000000000000000017a4fda16aad9000bd4fadbecca4d3a0a2deb0103d3044022070700d8de3ecac8750cf3eb624972cb1b9395eb9cc06871c59392ffad2b879b80220161eaa96bb92ce95c345981866ca67dedd1df04a137e27b7cbbed5b236293539ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b000000000000000017840121097caceef4c5a46fbe679ba6ecbc3bc05e30450221009b1ac5b9b02b87a1cec17e945e70ec1c1940b6d48d09679029dbef496f8d031c0220567914580441466a1339d92d99cf0893234ed6b5e7b9f00407e6e3bd972051b7ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b000000000000000017822df13e7b00e0f671a7f120a8f78bd78eb31adf3044022059f6d5aa1a5e59b9d5b378ed40ab3d109f175d97c8c2cc1e0fa98ff45ca93ef50220056f0a2fe0c3b6fafdb3eae2c49ae05ac6cb62a3b6b2571dd45d1774b90bf384ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b000000000000000017efad13da9347cbbc96683505e808c8ed14d46dc230440220538781f8c1a5587feda6b1333b86d979360a9f53cf93b29230c13416dad81634022019daed2374f9c6d767635413bba689ee3ca28b308c600f86d1a5fde4080164c7ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b0000000000000000171217f283de4bcf1b28e1e05c5320373f9cdc195630450221008fe76370d9968412848282580eb9a8c253d027fc4e610673857c15271246774b022003c249b93d51d344c2c92435c6adf782dff83da7a38b37445c7e0f007fdea786ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333100c2eb0b0000000000000000179a9e5009b0145145d954df555acf50b36b0469443045022100b3861e0c3333c7bef777d9b3342749cabeb006d898c028c39cc331f17d8e379702207eebde356b9f3f5dc5c2f9db0f46fb1fd2e350ec796d023a065c8dd23faf4bb1ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b000000000000000017305bcd10e47d011cc8a30c8680a09bb192253ff23045022100cf711a506ab8e2abf5bf7323c5e5ac51776fc7778845dc7f9686286d7044f20302200bc0fc09b71567a83447983623f45266a68d8a6428dd3d18acc82a042945251fff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b00000000000000001747ba8f15f9b7d8f3a4f7d450d5544b985692110c3045022100c1657829e9bf119301eb33228787b438aed6f3f33b61e3c174080dacfb9990ad022022759aaa39ee30254c272d8015b4fb33fd466e830f9c484e530f4169acc17b85ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b00000000000000001779e55b60c0e72d3d785bebbd33df99d6bea7af843044022052a46f491850b93b1520071b6f7b6525b97b604fa7e93f8172eeaf30baad247802203ceb22389721211041ccaaff83c9d0279c81e25afe632bb2e254f35e521cb94eff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b000000000000000017cf09b439324369cd49096fd2746367081689895c3045022100b7e1e1bc99c5d3144bce62785d82189232bd332afb98732b51f80747dcd2701302207caf6782b25e97c47eb6ea1d436f84b528b3d8f3af7998625b74e68845de7f6aff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b000000000000000017ebb33c4df59859924201cbb64ced6bef3adbc5a43045022100f7a8046fd9560543185d153cc9ee7785a295b2b32357d635131cc242dc42749d02204fb0b2238ffca4fc4af42607f435ece3fa35ade4d6bfeff6ffeee817f5498412ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b000000000000000017ec4d31936cd27e442479e0b3fcb0085c66e150173045022100ec3d0752a914671cb143dc3c3020c47ccb72876b268c0ef1d4d82fc2ee3f000c02207e7e8bcee0a10e659e350af5ff00e5c937f9ed1f016004d9841d988fc162e237ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b00000000000000001784de56a2ae261edbf5713002f4a606030a5488a7304402206c8ffd3d3c28c0e2873e526b774705ae81fff2ee4db765966b6c7865ea223d590220225a29f3fa0f2adc23a6e1e16ee3cd5a48b5d8bd6c6baa49321b720d8646d35eff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333400c2eb0b00000000000000001768889616a3bae1ed2ef024f0223ea0b605354ab130450221008e576c71758d736f6cd200ef8faf4f7c9c6419328e46402b4e01f390cf997a0f022068a6a539caf98a7e89ca703258b8bbbb0b70aebd3bc8bdc67d8a8705ae23b791ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b00000000000000001759a274212ccfe83a17c09d8c13ff6645d4a1c5073045022100b082481a85c259ee986c56ce3c2c8ad768408df9232081664f1a37b65e8f0ff702203084e6e60d2fb70e02bb67f581f5c3bdf7e8d4cc3129d220ecca3a81d47b57a0ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203300c2eb0b0000000000000000176857f83702fef96513cf41985225b48e34436b80304502210095b3298dc543b24a40f72560170e2275ac832f58fcc67eea128f604a8a9af4e602203d653556edbd647b108c0e15254a7f4cd494c8d2f4baf7b8150d47c4a17fabb7ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b00000000000000001732da82ee270f1e2c2fc7deb39e6e26871528dd74304402201feda5a75b2a8959d50e83765dea95f7fc9dcb446cac5bd7fae66b19cc1bf2cd0220397c484c6f76fdea8b284e65fb9c5795bb4ed9f98d7ddf079699b75ec27b341dff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b000000000000000017556357283c7a590f3f75446a2c57730709abe737304402200d7ddb69d9c65602a44908106a18dd0d6edd3e1380a68b0a11238f1ecf1ab97502203ab0f152aab1edc581863328e07368553641488ac1db0eaedf8c3280e5d18bd5ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b000000000000000017306330b1af7a053147d782c915501f46a42856703045022100c5409f6d6c89159b29fbc4a5d37f05c868ba78158f1ffab9f0bf10f1040500e4022011bfe8fbae70afcd5d13b24e96d7e077bf1bcae1054cb71a6b343108d2132d50ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333800c2eb0b0000000000000000173b362003c69ae3902ff355ba3ab40fda6b6a20fe3045022100ceaa0614b91be2d58f959ddaab130b28247cc3bfea0e224e9e6e155198d48ad2022076fde90c50e6e2d420258fac8007f6f6986bb328ac5df947d6a3746499dc722aff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b00000000000000001754f899a104b8f9bf0baf55f4c75e3fcfc0f76670304402206c78646b85d465a51845908d8909275269ba13ffc735730173b0b7025f0affa3022068bb2b5ca22549c13dcd4b6a1bf41001b166f074181f104d44358bd7d4091248ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b000000000000000017f0deb3cb96b71fd78fc597f46f72a082008f0a6e3045022100a3253f00c5384bf3f0a63197d389bd640243350f389b4fc6db38cebd4df11b0f0220222592faaff23d0d063ca7db91116525f9374102f69bd8cd9929f543ca8ee391ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b000000000000000017ca534e2c38361fbbb25d59cea2e835b4fa954f023045022100fe7f435c46def0f2be3966ec5b54e612465e053ab052534035c8e756796548ad0220019c4bc353605ddd555673d490901063b0fdd571ba03068cdee77135f63cd30cff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333100c2eb0b000000000000000017407c452cd3cb205bb8588c690866a777280a697c3045022100eefcc04db1af1cea3ffbeb1c2bc53a8e0726ce2c4c435e817786db7c6ef632ed02204dd9eac43ea01295a255aed970edbe118be1adb4d3521d12076eb81e77809110ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b000000000000000017b5b7c3d4561bab29857aad0c20b356b429dc8ca83045022100f76c828f2837c9487a4b1bbd5756170479fa3a4ed48dca031cf3f620f62a99ee02204da13730862ccd2b389dee441a7086f0191282ef2c5e2a3d32290a15401b2a90ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b000000000000000017711127026596a58864e8346d1b4b43265dd9be163045022100ada204d17b23ad4cc21a645e5cca0a472a2646526242cf8683236b9aaf85a21c0220348f7e2b164ca4c373557b4426c9953e7b14c4dd130c6857d8ee314db0a92305ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b000000000000000017dde2acf4d0798abf18ec499385dd96a948441e3f3044022041d3dc6d101ccaa3acb9622d9876e0ae610246147b19fc0831928933abb6ad5b022055a06fd34646626595f3b4bee99d49a44cc5dba02ede0f6ea35487a4f89511b3ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b000000000000000017a332076f2167b39601fc396d5813f1df168d5ecf3045022100bb096efaa786f0117b07303ead48985ec0ef4fc80de31fe8f456b3e412f5d8bb02200aff57978be91751b75dd0e5816c64b7248d7a5b3c8c3df00130a2c80608da10ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b0000000000000000170a30bc64993a755c45f212733eb031a4285d2d123045022100f4245b3606e03770b98a7fa39565d7509a76f810924055da9a6a600c6c01959a022050282b850eee19017814f547ea3587a033a0c93ff5d2bb9beb1cb67cba138646ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b000000000000000017e8cc0e60adc7d87069f3ee3ce27fab54e28b195b3045022100ef03265298c52635384e183286e4624414bd92fdaa0141d05526cb7869ce9e370220718af74f83c06e5fd13aab1ceb21f4440191275a308811ec7c2b707035a7324eff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b000000000000000017710ff266d4bc013a13c9fbebc80b0f3669740de53045022100e083480002bafe1234fb63485f21d1be94419ed69ed432ad182939a9a4fc4d780220367bb8bf10c3c5a8a514db19f0ac65cbd6a1791c8be4dbaf78e4ba32624ac4e1ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b000000000000000017eeb555fc83eba31e50fc83abb167759703a1491f3045022100f0efae4a199bdaf36375e9904c5ef61d3b9128f3f0f00136762b8c877084a231022012bfb4eda5252c660c926b4a8881c0752bd658d0692701636430ed1bae39aa21ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333600c2eb0b000000000000000017740cdd08268c4e41b44b6c58fa2b281d02ff9d833044022040f3c91ff3012f2f74d93c2dc22459dba4f5c598d74cecdcfc8ccb095bfad9d30220025d0a85f560c0ac71be17806c2758db315537bc9eda6dfc2ab5eb597e8e7a7aff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b000000000000000017cbb5cc21a6b935cc0ab0c8d27ce3aa73ac23fa503045022100ed3e51d4a1e313a45c423ca67ac851119ebff00f2fe7408f2f8276da09d0e18d02200e679fd84c0ea05bcde16e87752744db95c1d6623500dd6e8b3eac91850e72b6ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333100c2eb0b000000000000000017c798a993d4823466b864f36fb8ea9b30c51ea2403045022100f8ad6ad2a942b64efbfacce82b6491b1389404b9a6633f8e10e0201369d7bf0902207e1c519cd878036d16e57bdd1cfc4e95c706dda677b98db6334beba7a9a491c5ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b0000000000000000170e98048c8bb91aa761e72235da3de66efdfdbd963045022100b4b976f7303b3d4638a2432d9ecc99c2ea790761274c613f4730991ff1c40cf302203a749033f2dfba987a103e27d55d8280b8e55f0fc121204eae576ef72ea7687fff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b0000000000000000170a654cb7b1392a92a702d532d73557cd81c4ec7430440220533598e3d736748f22a7f96e362b3f33c6ddccddcdd46acebce7ef598864b284022063ed82a97e87173926cf48e69cd1656dfdbcd69c1d5dde0e31b2d7a63828fd6fff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b000000000000000017b527062800be5242c6dd288e5fc842d323b880df3045022100e03cd37d0dca988195191175306878832915fac89923ea3e6c4db35e424e8b2f02202fac9bfa81869d2bd6bf4e239a58aee6aab5eb795f06e6a20bb41a9ea692eb72ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b000000000000000017e2ede64fdaadc3535d06d208b820edcb175162a63044022024c14e8b3d656e736890766706713f1224c58a461de599201a2cccb586cad23f02202d6fa8ae3ae24f494037827d1e27511073422496b244a25ce610348f52e7c3ffff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333300c2eb0b000000000000000017424f8c905f2c4533176b88af2cd8cbd16bcb1d873045022100adb464c5e55b27e3575b3c0d721b69fb99f13299cd6b3191d16fa6b298683b5a02200b920190f7b37551c217ff94570c0b3eebf572a64765f7212ec324f9a2034ed5ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b000000000000000017d3fece02925920575ef90eb229aa631707084bd73045022100b632230b766f89e91e24519a93446f7c8a7c6e68e34c7ca00e1ee41b945d78b302207401fd3c6be212dba6763564f5d25865168b8ff54fe1dc52f5d5b12479f3ded4ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333200c2eb0b000000000000000017737f4c47c74919c2aa05befa3a17145334fc0b2330440220142eb40c16bbf50d63ee294a9cd87945e7a942c2c3cf852443be583fba20542e022053050ef63794d52a261e531f16cb3f01a118619fd1c4763bd93928eee7fa036bff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b0000000000000000179d2c9324e662ac5cf922b9f5a0bdf806baf737273045022100c3ba0ffcbfa9db3bcef1595e5836b501de96ce676f0aa33fe885d0721292d93502205cfbf32420b07708340c3ed60ffd29a22cd37b3e55abbfcd2aaa118d48e30347ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b0000000000000000174d48fbcb340a2691556715b1995e51c360adffe73044022019c6fb89c93e746bf5ec3a2eb1bca072da112663427f2db5778d1f9a7e4b7b22022018f48b791669852e13837236d5eddcfc61fc575c8fd15ceb8dc54304b1edb1a2ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b000000000000000017d190f1e81c972becdaa6d8ac66628d1b5a8e82de304402205547a585292262f56a6534c99685e1786b13812773d132476d09c19cad545a5002204af895618f5cf873edfda3f1a20f46ff8eb40ba1027021f5670b112a1a8d4b1eff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b000000000000000017d25825969c0d9d28ffd5bb1b3478901080edb0de3044022026416f54ae43a24634b705374dfe8cc1abace3c3013ba51eb350d0a02bde75440220726b910bf7304342b5eac57cd19c494cb2ef04b3ae0eba0bed266a26b9348c24ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b000000000000000017bbc327b6644dc42d55a8e8f32ffeabac37f742c63045022100f92ba535acd2d4cb2aa14103ad2b2f894cc78167899d16061a85e215f5ec213802206ce4ad145df8663dc16bfa18bc8397daef98c3d2b05f02a1a1630f2ba59157fdff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20343000c2eb0b000000000000000017abe730919c31b471ac0dbf9b6f12bad34416f8bb3045022100f9400dabee7c2749136995628b0813cfab1dd763bd7450717cc0af2bb11810560220683de15f7d713affe5e6de9e3321a6248481246a571ec9f96e648e2f1a0ee24aff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b000000000000000017527ce40209b488f22a14f1ea2880bbe37c15e5963044022005cb3b17865f2ef8580115395aa593abf9b074a70aa2859c17f5fa1f2b5629fd0220512306967c8f5e33eed952bb3d26de6f451b2c3954198e8db1bb5cb045521c50ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b000000000000000017e9beb02e59ebcf1e87f5b2990cd5fc8a523ed152304402200746188cd79a980f63a0011884ea6d34d4af34e533ab0bb18e622bc4b1b1b1b102201de516bd3458ada944d0d46eb2295873e5ab5f929c2fb728fb45ad81a53bbc6fff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b0000000000000000173a8aefd16949d537bbc3fe0a4535920af7501aab3044022010cd7a8ce57ce47221da59de3f75e571fe56e86626d9017ca6b80a0ef3a3508b0220078ee6036fcd468eebadd85b0e0a0a6f8dbb952bdc24feef3ca4decf1e058155ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b000000000000000017fb982d2e09e48f462322061b520f5c4c95567c673045022100b2e14e6c3b68c1d819c8da960c6d47b25bdd25c4856c3e1bd718265b292080c5022040dabf8f95d8e98c962d1733d10b8cfa6749da5b2dd1a521b7778a0306f05d9cff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b000000000000000017a045594b433e0b2c1f50dd607688a182d624d5a8304402206bae61e2254f416d8be818e16ad5eb508f2f5dadd9ecbca1ffe8ae427721974902204be5a5e1e9103898eb8e8922391fdfaa5c9d49c0015d28d2b7c462fb8f0838ccff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b000000000000000017ae968f9f5c4b0427d350ec8502fb3867c52e79903044022009959bb250bab7e1fd893fe7fd841fb17431892df577953bafb31f16ab20babc02205859d9bef6e191aff9c75f799fd7bdae6d8646ecfadd3d8b29a7c13471dfc3e2ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b000000000000000017d0c946c5e6e51a6c879677042422799a80e6e5fd304402204bdcd333bc3b41557b7b805a89f1e8b77fdb6d499736078ccb48c58f6431621e02202b727f6b6708d9459556c02950a350eb75e804bfdce9fcffb67c8cc95a8e532cff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b000000000000000017bdf12bd72e628fa60082963673586e9b96a8725c3045022100ebf7c3d8abefd8fe062fe52d1a226179fe2165f013099b46ba3724c345d51ed202203d8444c63f2d7a0f36bacb1643b33cbd47b02d800d0dd233fbb6e64385779380ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b0000000000000000177cb1f05f837c26723f4a3412fb2353226b245a7f3045022100e7b66efac64def1c8001cfb6b68e2b16f85c0f20684a760bc93aae385126d25702200253492c751f8c704eb042192cd524791ead3b5eda6e9afd0e71c58576127fb2ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b000000000000000017ef886d3c11609a79d95ae6f1ec733c0dcd9a6ebe3045022100f5396a022fccf2e70b2eb3873271e9021dfbd5effd706997f704e24ad722d6f502201343d51f913ecbc9a9ea6be1a97e5531f98107132a9ffe6a0e5b860d4333d039ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b000000000000000017841a086dcdb01b5a3688648e467449ff4ccc71473045022100e9221f75315a65c2b7fadba4dd5f668df15f339061b8e862e926fee8d7990e44022005f3a33f1a680fb9822c5a0ccdaee45a8db4c3f2fabe5b81ccd4c0e93ea8daa6ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313900c2eb0b000000000000000017559f8ef0575e8ade7a3b7040f02f54652e95bc943045022100c21caaecd718c72077f0d09e8ce04293d1ac27473c0545f3e3b1c8d0283b99ab022000c1a1a65dec757dfae0bc41d220a142aeb9ee311f14cbebe6bf977a0cfbe53bff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333800c2eb0b00000000000000001797982b30f6b666ec86b0e2fdc3b08a3b7479cbf0304402202e93971c4bef3ec7a4117177ad3be74b0067dc863d136d14e50e43feb62a6515022044bca36c4e4df2d40bb1f610eaeb0ce814acc0acbeb4c46c876a10d533071619ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333200c2eb0b000000000000000017b406dcd28615b6787189c1c4e9f6e3348037d8b03045022100d09d4720d0517c00672733b12f328a6a2d98d7ab150bdd810c82df4a0e021df7022066aa615c7b9e481ad31c06c77cb6f3c92018c6ed8c120b2cf6e14320679b5738ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b00000000000000001798c09cff1628733ff3baca4b61e838abd4fd17663044022056d79e8035d942eaf1c5de8722904e4ebdf4dc05a72ed6bb21fd16dc6808044d02202ec527272752ce4d75886dd0e39690e6bbe2a652db51addd4279dfd149f4dbe4ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b0000000000000000177a10018a8e0b53ef2ba10a669892a7ed8120e4d43045022100dd67d44ee667c3223ca757fed91fa2cbe1cb9cb42109fabce6eb83dcfd7d8747022045811f62696dc8c6af6554f55d8a9c5f2bd3f80271897c18dfdf03f60349036cff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b0000000000000000177b5daa22d0d47c8aabd77e099011a5c5de5ec0d130440220348a8a248a43b290989893e559fe0a041d77357a62edc4dc990555da00fa817202206aa92a6d3b06695e7600550540204c7b8abedf0b686449a07a4e6a70c1fac395ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b0000000000000000179c89afefd44cae11fa228af7cfe1470208eeef4e304502210099c1981e0e8f968bc115039c025eeb94d371b241a19d98002821f10002c8aeab022046e9aed58e820cf371e194d408d01090bb7b867ac918d47015225ab62157e462ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203200c2eb0b000000000000000017fe49b6f1b2bacbbafe7263bf3f4c66c2332e71bb304402206b86a185dce4e776bb5f9f95d4b5cec4eba62873fe88ccdb864946fbbe60cc7902200a821a5bb1d0f2d983f91d282ce60826b7c616005ff6db0d4dbe3ea11ddf8ee2ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333700c2eb0b000000000000000017bb95af90f1587796c7d844c28d016ecb96cf7b323045022100cd1b94922995ba4e16935f2380234ce7b6da26fcbab29f3e957aeb8ba8e8d87d022071757f37077132b0db0f217b657791ebb971fbaa7ff95e183ba3cb5858d525f4ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333300c2eb0b0000000000000000170e2059d5f792f009d8457f67d29d72ebd5cd8e35304402200a1537addb2f2f2e434a8c6e4ab32acc59f7653226218b7fe9bd5f82f98778b202205c77341d3ffc0e049aac2a476ee978acca2829d111552645797613f3ca616a4bff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333800c2eb0b00000000000000001756cf720576a7f775bd1d4ac4f74bb17e45138740304402206af353161fc543cfab5d90a58dba1438571506cad30a5c78e49cd2f28ebd542002206ab2833769213fa2603ceb14e1bf88a1647dfa4a28e5459fbc47c829adc34026ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b000000000000000017b858992acb67521bcafc16e7d4d1e93005c380443045022100ef53b0b4dee8630494f812ef6056c5a5821eb2478a0cabf55039d35db9ac3838022061845377b9eee8d4c20cf1a99c22f424a9ea21d842bc85cdaa8c4f4ba05b1f35ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b0000000000000000175060e8c493e5772f02b9e88e3973ae1ad35c4b6d3043021f0ef0634992f4b2ad6b038e58d247ef4476a1eda7f2ce48ae48e24a1d3bb54902203a769d1199251a4df6d83d0db299e3bd0c1363c51fa3e9f545dd5c13d730bbd1ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323500c2eb0b000000000000000017229c0d0469156c68ff3bedef21be4a5a652549f630440220435a475d12adb82f40a30520bfde3072e9a8fbf016529faacfde40dea4c7603102202d7d784c34f089ce93af3a902936de4960005d17a9d64f096a87364d4cd98625ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b000000000000000017e7f5bc11a5b2237519bf751220a5968483b3dfc93045022100ce0abaa1e6656daf0cab3652b8f9355633ac16beb264dff7f076c9760b4259d3022070a870ab901efa8fa4e54b9c11fe201716ad922aa73e69cc9b7c88b71acc73eeff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333900c2eb0b000000000000000017f0157c4c48288c93e420b6932ee7c10c119d7d983045022100c33a6b77cd9cd63bc5257537dc90f0506ecee8fd0ad34facfed9132c54b9c17902205546cc10e43e60167964f37881412c1f4e742be6f1218749e63e1bdbd5be5023ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323600c2eb0b0000000000000000177f106853bea88b3b4ca2daaa2e9677ffb019d274304402205d25a0dbb4bd258e2392bf3ac4faedea4e6d24bdbbcdae653641db5eb532cf9e02200a38c7ada6731f470861b929d5f15f9c63a56a1985345b9d86ae005acf13b073ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b0000000000000000172ff8cc458a6815c2c15687aadc6351bd3dabb1993045022100d2aa5946b05215cd1cca45fefe390ec78081406d6b8f13e724cff599f046d6f1022035a2e6ff995bac39bae2dae1232f7ea2d0ba5897b1fe0ffccf1a0c1172d89308ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313500c2eb0b0000000000000000174e4248b0e82e17bce3d97fd49c9b9190e252b62d3045022100e88aaf7ef7fce75a0554d61932375ec058d4f0e35979bc2d3114e8f1c9d08040022051dd6c51e46258ce1b5c6c57e84194325629213b7f9bcabb879d51ac037aa6b5ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333500c2eb0b000000000000000017d320a267170f665d111adaa1960d95f9708e7d073045022100accbc39d4f4e1e863962caf8e6cc548dea20ee1afa9e6e1806e067da9ce4d3d202200a1a38c83468157af05584a85ccfebb9e4c551c2237befbfd54a51fdcc96b168ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b00000000000000001768c6c5f98b69bd4d2f43ccd0d7842d24d0ddf23e3045022100c7c0193c159cbde8a09dead106919732a3b4e30a5cf51617314133454c65e1ef022074b355a40e59075104f694c19d785c2b8ccd3c1ffc0ee17fc8a2c919bbdc3ef2ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20343000c2eb0b00000000000000001730cc52afbe325a52f2bf8983937210c52650a5ed3045022100eaec6482374438ccc03a12bfc06b8ada1c4f2769d1b8451eb643213be07f25f802207bf68341be74bc813eb1ad74a0b56d16e27cb5954a61aa971296f81823d65d6bff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b000000000000000017042b228f903384321ef7e2e38f413a9049052170304502210090998a023faa9149d75cb51f38f98adf38e39cbb5dc15cdfcc0eb5beff9c2eec022034f33fa9ccb43a93965804e455e08adb9f00910063cd5a87ee70ef376a4c5db7ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323100c2eb0b0000000000000000175a821c2ba6fd549322c6a7994ac6fe5a9417f9b930440220494a3190e1fc982d01badc71006b776df163583ae1ac365d7dc4cda0a061373a02201aa1f3bb1d2ba405750c585784caf24f25b83f57fc11e0f675d12d2fe3d666deff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333500c2eb0b00000000000000001759854238ed033c028098256d30c4bd03e090f233304502210080d7de0342c4855c0c1de956e716623341fce1b4856a1c6e00ebba7bdeee4b6d0220172e8ec11914e21ed369fad2615dfa95ce09b3eec8f3a03e453b4050b7baefd6ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017927ac44a3d0a64edebbfe4dafc8510001eff90a530440220257e5ed2aaea59a5f1a4ae74acc59dd1c68a821ecd9eb506ae7fbb01cfe6511f02203eadfccae466fda0800b0cd401284f79016742f94cb6b8e23766e625e7634855ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313800c2eb0b000000000000000017bf515df18a873bf775604cf142690c5c9e2216af304502210089973d46853f64b22a8e5ea082741ce61a5b65ea627c2e7d65c6b68761ea9307022064f1232ac967e636f86ba2a1cb84b898e6ef433fc201b893914ec350ae5424c9ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000179a245b5d5ace31e5432c7cf0e0ef83ddce7143a33045022100d399c822102d6ef40c2e9d4117055604358ef4db80943936c99c3dbe2fe0c9e0022076522ed280a8b48dcf2e1050ada11da7d0ba410cd405cc3575886e524bf74b3bff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333900c2eb0b0000000000000000170ce3d695aa0015271cadb4ae7cabde52e7bf881b3045022100f4160aad6909f8159bb2ba3b677a8d4ccdafd769fb7fb193cf45ef5c24e90b6702206f9c06f30f72624b140d0e79994d655095b9acfc633d65662ef982b52b3554dfff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333700c2eb0b000000000000000017b6f21649c7b1b1b9975312888b2809b028cbb8353045022100f21501ede148ae4b1825747d5a35b2f212392ed213a3de1353cc554cf9fb450002203540a0542bdc74befa5e7737af0a76fd511fcc1d663f3c0ab21e1ca9bdfe69d2ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323200c2eb0b0000000000000000173894e92a8d11945bfb593acdb9e619c22c989d2d3045022100d6ba427e0f702f74d0e7ce778354f5ff0376c775e53991bf9f4057957feb2b7902207ef90dfb96a4c8c5a296ec89589e631913145e9736251a82ea93c48fc765dc3dff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333700c2eb0b00000000000000001779389f9aefdd03d9d3788bb08f1e5d8b6567aa1c3045022100e921d8a535a948010be53a418b14537894e5cd59f4798c28afb3a28b1eef3a6702201933fc9972f945e6a75b98bad43ab4c1d5aa50108ecfb0fc6d4d98692cea74c2ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b0000000000000000171785c644be770b6b42ba6d168e8bd5dbe1067cf63044022069b32ab58125181997f13326bd5bbd8a51c05ce6ca1cb3e05d211aabcdf094c202203ff626f8686e5eaadfbb3f0b0fd3c2d97a27da67df60d449bdfd1a7111691998ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b000000000000000017067c78e18d6221da34c66c5b99c49b6acdf7d6a03044022046e3fe677c86fe992839c0b4b6c8d52ed9b0468d746144f090a3210c3b22839a02200edefed7a6009d614c52d0258f137a9469e1b945cc44a9a966074039037b6050ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203900c2eb0b000000000000000017da2752064521eadb7cac06cc69a82ba14bd78abe3045022100991d550584f937ab579d30b9836d2ddb9d07a53b62088b9ec7d812e831eea3270220130e6d8efe60fec26dd14dc6190c1f68bd37b044c5e48669da7d129671d2fbe9ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333400c2eb0b000000000000000017dce1e04722ba02e42d74bd57303917048a3b9a0c304402203d37d32b85c84245f3cf89d3e3187c61eb28f9140cf07914891da61d58f8268b02203e68eec8d8c2c2e06261e5a39d0a58fafd90c46123f7e09b28c6ba88a489ff24ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b000000000000000017fbe158a34cc27bb228e101f482ed036f96b824743045022100c1fbc4813b8370edb55d74cff95931f9e5d2e44587c362a184e0a60861aaaf94022075e2c4fabcbeeb54f0ac98f3197bb7aa064e5609a4f7004a51b180030d98ec78ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000171e0f108e44028a522525837008ba0dbc307e5a1d3045022100ddb81efcdf3bd77b70c048f3d8cf9752bf1bcef935c73e5f9a92066a4f2c5c18022034a7cd1b0d4a62e88a1d289244462794648d0643015c3ae2fd6c3e92bf9269a8ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313200c2eb0b000000000000000017ba531b66b0cdecdbc3cf271612d42265b1d3a5503044022048e574b2570893304e81957458715508df9160125def85414f5120bb8d5120610220737e6c0775977e19ff310a5480a0a402fc6b6ae6868cdd551d2b30244b15285aff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323700c2eb0b000000000000000017257049419ec6524607a68b33a0a36ca96ac30d5d30440220106fcf7ed726167522846df88446c32ba548a325254710ee229de51b6f1dda6b022054bb708966d6978b949cbc5697d3fa653281d16c7881443377d27307ace5bcccff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323900c2eb0b0000000000000000172c04bc6a74f4c3dd84a9a58123906e30786c68ca3045022100f348c5c1571df94075fe18c72472afff2a8163e6af45cd6bfc4c55bc7af895b302200140edc244687b44fdc245bc0a18edc09352a953f6ce7ff659f8935508027e93ff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313700c2eb0b0000000000000000174222f8f0ed1b116bd939ba2d602e0ece7b3f49b43045022100ec192e32473517250c25ce2dfb79082f34c63017ba2532a6e56406dad64dd8cc02207c5f0d699c9b76987e06fff3ad9788dbab9019653f0ae15555763b4e22bb02b4ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323300c2eb0b0000000000000000171b272cd1e6b978ae9fb815b40329988d2dc113e93044022059870246ff0cf10fd681b29f392c8d046d8344838222f0b53b96f2b436e73c42022070b12ca14c2dee42057a4bb5986461d415502f08abfded6ce85abecdaf851615ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017afb8147376f4e4b1c486e12385d9f5516d32785930440220105f2ab1c8a75d33f29ffd4ceddfd3b175a39cd141a976645faafee06a0708e50220178e2dd98924ed91d1691a3d747fcf8233edc33e98272a24b17b77602dd0fceeff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b0000000000000000171b21d99d4c50d090209cfb717e7e3171445a61e83044022070d12ef771ea3708d2145923d4c0b4c1eb31f8b3719a30cb9fd32566ded28eea02201a744120749b42c39479ad088a462f6f9fec468d85cdc9cf83e5cedc90940832ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333400c2eb0b000000000000000017263931c7543c6fc43bc3312498b241707bca3c043045022100cf63f159f40a6f75ddf5c4b206acce3813d94ab1cdee8c910dede9657d50afe60220724da18645f4ee23be41b697690e55a5e82a2ce1ed3808e104f0902de2c01c67ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b0000000000000000173e1b19bf4d66515f03c6f15e868001ab959b4f403045022100e5936646e020734f85e7601323f4be503f67da4a31f05210210a4d02808af39602205db4b337e1d883b1312bc03ca0195fc77a74f860ceb7867e197d9647f5d26d33ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000170dcd69f016fb9efd5f9ae47d578ed0d04bd58b01304402205e0fe17e03c3666f986dd243bc057dff4b190f14ba1cb7e19804e4c1042deec0022059cf68c76c5b2931b83e9bd3f75cfd75267b8f35aa957cf63cce3b2226665707ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323400c2eb0b00000000000000001705902ff13f07ff4a607ca76390eca20df01539183045022100967f65a4252ef1e2f1f69b754cc8f3b19046af3c7efae86baf81a85031311709022075f0ebbdf75c720696c925bee4f6161bf11a279bf7e9aaaf67234a53dcd614c2ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b0000000000000000175e0496aeecc3ed4be7f98c8679efc4b3d9a398fb3045022100f5bf4a65a49d712c81d8ceb9180a17527852ae393f3fbee6f556f695ea1be8100220332353e697d4916247ec68f40764c5b6411a45d0b3db65d19b699b92e5fa4fb7ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b000000000000000017af608296a6d81f700f792a6ac9da2d170749c40e3044022049637b4877124be3de9ba317970c35fe2f16dce72fe3f5752c1df56b1c14bc1c0220765bd7c7245298676e9943b7443548d723c60def0b7217fa6d991d5866406932ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333000c2eb0b00000000000000001755d63414206f2d2acfb498cb2c2c6f0b9b84b97e3045022100ff478f5c5d86597ebc267e5e819782a5316e79987df57d154046c9c7b742d2e602206a0092b9a8709c3fd1f6168e1cba5bb8e84789140111addd99a3cff0c8dc663dff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313400c2eb0b00000000000000001723f80cdfc6e8737f06a9c02d6ca93594388767f83044022055b7ca7c018bf871e7200ebef17ea826b817305e942b3403ab57a1d2f138b75f02202e8a79588f7802aed93470cce823a5f764715b23ecd84fe531d68019b5792bfbff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313100c2eb0b00000000000000001773938849133e49752e6322fb06520820eb82ab5230440220546f1457a0e381e332b2ae915a4505a86cb15816cd2cb143411a433847c17aed02202e05926f9270e36d9d9371531d68d365de3ed3ccbb7a2331c8a3215a318c451aff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333600c2eb0b0000000000000000173abaff16c478b48ec930bc54dcb2a85767ae61833045022100b9db8445e61c6645aae74c4ee5c536ae0bfe18f63dbc5316085f895db7ae8706022029bbda8d6f0609bdb34eca51a050327a86120733cca1df7eee94f598d0baa7f5ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b000000000000000017236b377b2f235de51e10128a7a5c5cad4c6e1dd130440220186bc71fdfaedc0c31285917f74997cfd66ea4a72e690d480d5f8ef5e7a1d1a20220710c383ea6e8b4f64ed2eaffae33a1f0bafff1fb1c578a1b369dd8151ef3f0bcff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323000c2eb0b000000000000000017b35eba0e5bbe1a15c6e2504779938e94572bc7e1304402204f1f1527f8d037d69ff3de5766b719b4b84f29d5a79ff390877a84bb9856d5da022008656e66808082b8dad2ef6d2168e031f04db664eaa3bed647395b42ffc9f759ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203800c2eb0b0000000000000000179c375cc35993d1b822494819e4e36f0035d2fc9b3045022100eacd0a48fcee232b4592465351612f802e31e0aec809a04aedb1ffc77b67970a02207ddeb454d49fcd5dd03eabcb28fd6a54356ee8ce513ee4eef6c3dad2bcc035d1ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333300c2eb0b000000000000000017db12692f9dc88fe4d380436e4a3824076a4f843f30450221008aaa31bd67638a4896b251f1e34148abcb9834c8324ad2aea68b85747525a98502203021aae408cd18175eb04e45a8bcedc5fffaa6e6effdb1c11a336976be49f240ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203400c2eb0b0000000000000000176d9b88a2a27357f2d7eeae78b386d2edf8df3b84304402200b48815ef066a0eac8a79ec4d0fc4f22b105eff437163af0573683d879b5a93802200bf1f2c2977b4065f5522bb8b917141cb82806446e57593d14dd9d632e0f7013ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313000c2eb0b0000000000000000176358f2ca1a4c272cce0eedb92f3bf8b837fd8cf03044022070c9c7d962d31b5189bf1747aa6e06fd35ae00e0b12efa6899173b3ea5e343180220146328f4a1ed1548a5a95ca8489af16a095cf0095924084f5a508c9e010d9e86ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20333900c2eb0b0000000000000000171b96e2b2a75c0ae6c7fa5626e3d79c20175f65913045022100c8ec04674367b578456e43088752a04b0f59de4bbda197ae38a94a1ad0db39f2022011bedc437d4a025b2eb0a0df29d61b55aa2d4e49a0ab01dd48e050833c153d15ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313300c2eb0b000000000000000017f8c689bc5455e9c7add499f969c3979f6cf1e6c8304502210096e14fce152f9f532f1836d260f535c3316fa8ab5e1bdf1e4c1275fcea5444dd02203be1c81790b12abacd63b89b3fe11cd664a2c9a3519eac0303e3d091dfaac54cff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20323800c2eb0b0000000000000000172fe38431dd11fa9a38bac772feeeb266a13ddb0b3045022100e67098e07447a4f767c9309ba315e89b332cabee4259887111745e83f9cdc4cd0220531cb5f0372caac1ed702394f9722dcf1106f115dc772272fb470db885eb49ebff0117004cf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203600c2eb0b000000000000000017fee784411bbe0538f549eee19cf6593ee1d0378f304402202fd4dd9351912093bf7289fc6a2204cedea60dee16bf6c169f82fd919e94326402207d2f7ffa0cd4e559e39421fec818020859e080d66d4511f6b86af8922c4bbfcfff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000e5472616e73616374696f6e20313600c2eb0b000000000000000017910efbc8d502d7a24a13533a6de93b41c5c74f4c3045022100f1fdf68f3d428be0f9dabd8cf20aabd4bc576ab63e666c339d7d17597f8b023b02203854f0db9240abf17d2f245478ad287d8514c1670fe2604fd7772adc23fae095ff0117004bf0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203500c2eb0b00000000000000001753fdee9530e0d82aa9fcb0d8a378bc0647584765304502210081b45f9b0bdf11c4e0944eef58a960b99ed022643b2f3d18dba890c1f02d4364022050eeca2c2e24440f6f18fec7f019dd27c54f7cb04b152a1a75b324816a859c79ff0117004af0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203100c2eb0b000000000000000017751964a7f75cd482dcc3838024c6dfa7e05f2eb83045022100af03a6ea5d5d9a997465d16580a7e59fe6bc1a4cdf85304f0fd14b76ea83d88e02206be74c7f22acbfb8496bb10c60024dfbe0924de7e26b912735826c8a90908c4e diff --git a/benchmark/fixtures/transaction/deserialized/0.json b/benchmark/fixtures/transaction/deserialized/0.json new file mode 100644 index 0000000000..0f938cc53a --- /dev/null +++ b/benchmark/fixtures/transaction/deserialized/0.json @@ -0,0 +1,15 @@ +{ + "version": 1, + "network": 23, + "type": 0, + "timestamp": 58126413, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "fee": "10000000", + "amount": "200000000", + "vendorFieldHex": "5472616e73616374696f6e2037", + "expiration": 0, + "recipientId": "APyFYXxXtUrvZFnEuwLopfst94GMY5Zkeq", + "signature": "3045022100bac5b7699748a891b39ff5439e16ea1a694e93954b248be6b8082da01e5386310220129eb06a58b9f80d36ea3cdc903e6cc0240bbe1d371339ffe15c87742af1427d", + "vendorField": "Transaction 7", + "id": "00d2025f7914a8e794bdaea404a579840cf71402cef312d2080c7ecd86177e5f" +} diff --git a/benchmark/fixtures/transaction/serialized/0.txt b/benchmark/fixtures/transaction/serialized/0.txt new file mode 100644 index 0000000000..899198688c --- /dev/null +++ b/benchmark/fixtures/transaction/serialized/0.txt @@ -0,0 +1 @@ +ff0117004df0760303d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c35780969800000000000d5472616e73616374696f6e203700c2eb0b00000000000000001759e7dc56557733804418f0ea6fd3b2573a9aabdd3045022100bac5b7699748a891b39ff5439e16ea1a694e93954b248be6b8082da01e5386310220129eb06a58b9f80d36ea3cdc903e6cc0240bbe1d371339ffe15c87742af1427d diff --git a/benchmark/helpers.js b/benchmark/helpers.js new file mode 100644 index 0000000000..c0dbb56700 --- /dev/null +++ b/benchmark/helpers.js @@ -0,0 +1,8 @@ +const { readFileSync } = require("fs"); +const { resolve } = require("path"); + +exports.createBlocks = count => new Array(count).fill(require("./fixtures/block")); + +exports.getFixture = value => readFileSync(resolve(__dirname, `./fixtures/${value}`)).toString().trim(); + +exports.getJSONFixture = value => require(resolve(__dirname, `./fixtures/${value}`)); diff --git a/benchmark/index.js b/benchmark/index.js new file mode 100644 index 0000000000..bf217a53e4 --- /dev/null +++ b/benchmark/index.js @@ -0,0 +1,12 @@ +const { benchmarker } = require('@faustbrian/benchmarker'); + +benchmarker('core', [ + { name: 'Block.serialize (0 transactions)', scenarios: require('./block/serialize') }, + { name: 'Block.serialize (150 transactions)', scenarios: require('./block/serializeFull') }, + + { name: 'Block.deserialize (0 transactions)', scenarios: require('./block/deserialize/0') }, + { name: 'Block.deserialize (150 transactions)', scenarios: require('./block/deserialize/150') }, + + { name: 'Transaction.serialize (Type 0)', scenarios: require('./transaction/serialize/0') }, + { name: 'Transaction.deserialize (Type 0)', scenarios: require('./transaction/deserialize/0') }, +], { hideSummary: true }); diff --git a/benchmark/transaction/deserialize/0.js b/benchmark/transaction/deserialize/0.js new file mode 100644 index 0000000000..5a01d18c2b --- /dev/null +++ b/benchmark/transaction/deserialize/0.js @@ -0,0 +1,9 @@ +const { + deserialize +} = require('./methods') + +const data = require('../../helpers').getFixture('transaction/serialized/0.txt'); + +exports['core'] = () => { + return deserialize(data); +}; diff --git a/benchmark/transaction/deserialize/methods.js b/benchmark/transaction/deserialize/methods.js new file mode 100644 index 0000000000..d01249d1b3 --- /dev/null +++ b/benchmark/transaction/deserialize/methods.js @@ -0,0 +1,7 @@ +const { + models +} = require('@arkecosystem/crypto') + +exports.deserialize = data => { + return models.Transaction.deserialize(data) +} diff --git a/benchmark/transaction/serialize/0.js b/benchmark/transaction/serialize/0.js new file mode 100644 index 0000000000..761e1c1b31 --- /dev/null +++ b/benchmark/transaction/serialize/0.js @@ -0,0 +1,9 @@ +const { + models +} = require('@arkecosystem/crypto') + +const data = require('../../helpers').getJSONFixture('transaction/deserialized/0'); + +exports['core'] = () => { + return models.Transaction.serialize(data); +}; diff --git a/docker/development/docker-compose.yml b/docker/development/docker-compose.yml deleted file mode 100644 index de8fa8f6e6..0000000000 --- a/docker/development/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -# -# For running some services on development without tainting your system -# -version: '2' -services: - - postgres: - image: "postgres:alpine" - container_name: ark-development-postgres - ports: - - '127.0.0.1:5432:5432' - volumes: - - 'postgres:/var/lib/postgresql/data' - environment: - POSTGRES_PASSWORD: password - POSTGRES_DB: ark_development - POSTGRES_USER: ark - -volumes: - postgres: diff --git a/docker/development/purge.sh b/docker/development/purge.sh deleted file mode 100755 index 9849a43d90..0000000000 --- a/docker/development/purge.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env sh - -docker stop ark-development-postgres -docker rm -v ark-development-postgres -docker volume rm development_postgres -docker network rm development_default diff --git a/docker/devnet/Dockerfile b/docker/devnet/Dockerfile deleted file mode 100644 index 46d929718d..0000000000 --- a/docker/devnet/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM node:10 - -WORKDIR /ark-core - -COPY entrypoint.sh / - -RUN apt-get update && \ - apt-get -y install --no-install-recommends \ - build-essential \ - jq \ - iptables \ - python \ - vim && \ - rm -rf /var/lib/apt/lists/* - -EXPOSE 4002 4003 diff --git a/docker/devnet/docker-compose.yml b/docker/devnet/docker-compose.yml deleted file mode 100644 index fdc00da3bf..0000000000 --- a/docker/devnet/docker-compose.yml +++ /dev/null @@ -1,43 +0,0 @@ -version: '2' -services: - - postgres: - image: "postgres:alpine" - container_name: ark-devnet-postgres - ports: - - '127.0.0.1:5432:5432' - volumes: - - 'postgres:/var/lib/postgresql/data' - environment: - POSTGRES_PASSWORD: password - POSTGRES_DB: ark_devnet - POSTGRES_USER: ark - - ark-core: - build: . - image: ark-core - container_name: ark-devnet-core - ports: - - "4002:4002" - - "4003:4003" - volumes: - - ark-core:/ark-core - tty: true - privileged: true - links: - - postgres - depends_on: - - postgres - command: - - /bin/sh - - -c - - | - /entrypoint.sh - -volumes: - postgres: - ark-core: - driver_opts: - type: none - device: $PWD/../../ - o: bind diff --git a/docker/devnet/entrypoint.sh b/docker/devnet/entrypoint.sh deleted file mode 100755 index f67d2d61e4..0000000000 --- a/docker/devnet/entrypoint.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -sysctl -w net.ipv4.conf.all.route_localnet=1 - -POSTGRES=$(ping -c 1 ark-devnet-postgres | awk -F'[()]' '/PING/{print $2}') -CORE=$(ping -c 1 ark-devnet-core | awk -F'[()]' '/PING/{print $2}') - -iptables -I OUTPUT -t nat -o lo -d localhost -p tcp --dport 5432 -j DNAT --to-destination ${POSTGRES}:5432 -iptables -I POSTROUTING -t nat -p tcp --dport 5432 -d ${POSTGRES} -j SNAT --to ${CORE} - -cd /ark-core -rm -rf node_modules package-lock.json > /dev/null 2>&1 -rm -rf packages/core/node_modules packages/core/package-lock.json 2>&1 -npm --quiet install lerna -g && npm --quiet install -g nodemon -lerna bootstrap - -bash diff --git a/docker/devnet/restore.sh b/docker/devnet/restore.sh deleted file mode 100755 index ef8a357f39..0000000000 --- a/docker/devnet/restore.sh +++ /dev/null @@ -1,8 +0,0 @@ -DOCKER_DB_NAME="$(docker-compose ps -q postgres)" -DB_HOSTNAME=postgres -DB_USER=ark -LOCAL_DUMP_PATH="snapshot.dump" - -docker-compose up -d postgres -docker exec -i "${DOCKER_DB_NAME}" pg_restore -C --clean --no-acl --no-owner -U "${DB_USER}" -d "${DB_HOSTNAME}" < "${LOCAL_DUMP_PATH}" -docker-compose stop postgres diff --git a/docker/mainnet/docker-compose.yml b/docker/mainnet/docker-compose.yml deleted file mode 100644 index 213e11d607..0000000000 --- a/docker/mainnet/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -# -# For running some services on development without tainting your system -# -version: '2' -services: - - postgres: - image: "postgres:alpine" - container_name: ark-mainnet-postgres - ports: - - '127.0.0.1:5432:5432' - volumes: - - 'postgres:/var/lib/postgresql/data' - environment: - POSTGRES_PASSWORD: password - POSTGRES_DB: ark_mainnet - POSTGRES_USER: ark - -volumes: - postgres: diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile new file mode 100644 index 0000000000..bdaca70ffc --- /dev/null +++ b/docker/production/Dockerfile @@ -0,0 +1,18 @@ +FROM node:10-alpine + +WORKDIR /home/node/core + +ADD docker/production/entrypoint.sh /entrypoint.sh + +COPY ./ /home/node/core + +RUN apk add --no-cache --virtual .build-deps make gcc g++ python git \ + && apk add --no-cache bash sudo git openntpd openssl \ + && npm i pm2 -g --loglevel notice \ + && yarn setup \ + && yarn cache clean \ + && apk del .build-deps \ + && echo 'node ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +USER node +ENTRYPOINT ["bash", "-c", "/entrypoint.sh \"$@\"", "--"] diff --git a/docker/LICENSE b/docker/production/LICENSE similarity index 100% rename from docker/LICENSE rename to docker/production/LICENSE diff --git a/docker/production/devnet/devnet.env b/docker/production/devnet/devnet.env new file mode 100644 index 0000000000..786aa41cb4 --- /dev/null +++ b/docker/production/devnet/devnet.env @@ -0,0 +1,20 @@ +#MODE must be one of: relay or forger +#relay: start a relay node only +#forger: start relay and forger +MODE=relay +NETWORK=devnet +#Core variables +CORE_DB_HOST=postgres-devnet +CORE_DB_USERNAME=node +CORE_DB_PASSWORD=password +CORE_DB_DATABASE=core_devnet +CORE_P2P_HOST=0.0.0.0 +CORE_P2P_PORT=4002 +CORE_API_HOST=0.0.0.0 +CORE_API_PORT=4003 +CORE_WEBHOOKS_HOST=0.0.0.0 +CORE_WEBHOOKS_PORT=4004 +CORE_GRAPHQL_HOST=0.0.0.0 +CORE_GRAPHQL_PORT=4005 +CORE_JSON_RPC_HOST=0.0.0.0 +CORE_JSON_RPC_PORT=8080 diff --git a/docker/production/devnet/docker-compose-build.yml b/docker/production/devnet/docker-compose-build.yml new file mode 100644 index 0000000000..f3fba47443 --- /dev/null +++ b/docker/production/devnet/docker-compose-build.yml @@ -0,0 +1,54 @@ +version: '2' +services: + postgres: + image: "postgres:alpine" + container_name: postgres-devnet + restart: always + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + networks: + - core + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_devnet + POSTGRES_USER: node + + core: + build: + context: ../../../ + dockerfile: docker/production/Dockerfile + image: core + container_name: core-devnet + restart: always + ports: + - "4002:4002" + - "4003:4003" + - "127.0.0.1:4004:4004" + - "127.0.0.1:4005:4005" + - "127.0.0.1:8080:8080" + cap_add: + - SYS_NICE + - SYS_RESOURCE + - SYS_TIME + volumes: + - ~/.config/ark-core:/home/node/.config/ark-core + - ~/.local/share/ark-core:/home/node/.local/share/ark-core + - ~/.local/state/ark-core:/home/node/.local/state/ark-core + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - ./enc:/run/secrets + networks: + - core + env_file: ./devnet.env + tty: true + links: + - postgres + depends_on: + - postgres +volumes: + postgres: + core: +networks: + core: diff --git a/docker/production/devnet/docker-compose.yml b/docker/production/devnet/docker-compose.yml new file mode 100644 index 0000000000..04d68c4122 --- /dev/null +++ b/docker/production/devnet/docker-compose.yml @@ -0,0 +1,51 @@ +version: '2' +services: + postgres: + image: "postgres:alpine" + container_name: postgres-devnet + restart: always + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + networks: + - core + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_devnet + POSTGRES_USER: node + + core: + image: arkecosystem/core:devnet + container_name: core-devnet + restart: always + ports: + - "4002:4002" + - "4003:4003" + - "127.0.0.1:4004:4004" + - "127.0.0.1:4005:4005" + - "127.0.0.1:8080:8080" + cap_add: + - SYS_NICE + - SYS_RESOURCE + - SYS_TIME + volumes: + - ~/.config/ark-core:/home/node/.config/ark-core + - ~/.local/share/ark-core:/home/node/.local/share/ark-core + - ~/.local/state/ark-core:/home/node/.local/state/ark-core + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - ./enc:/run/secrets + networks: + - core + env_file: ./devnet.env + tty: true + links: + - postgres + depends_on: + - postgres +volumes: + postgres: + core: +networks: + core: diff --git a/docker/production/devnet/enc.sh b/docker/production/devnet/enc.sh new file mode 100755 index 0000000000..e510b293d4 --- /dev/null +++ b/docker/production/devnet/enc.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +########################################################## +# # +# This script encrypts your forging secret and password. # +# # +########################################################## + +type openssl >/dev/null 2>&1 || { echo >&2 "OpenSSL missing. Please install and run the script again."; exit 1; } + +yellow=$(tput setaf 3) +green=$(tput setaf 2) +lila=$(tput setaf 4) +bold=$(tput bold) +reset=$(tput sgr0) + +warning () +{ + echo " ${yellow}==>${reset}${bold} $1${reset}" +} + +success () +{ + echo " ${green}==>${reset}${bold} $1${reset}" +} + +read -sp "Please enter your delegate secret: " inputSecret +echo + +while true; do + read -sp "Please enter your password: " inputPass + echo + read -sp "Please enter password again: " inputPassA + echo + [ "${inputPass}" = "${inputPassA}" ] && break + echo "Password do not match! Please try again." +done + +SECRET="${inputSecret}" +BIP38="${inputPass}" + +rm -rf enc > /dev/null 2>&1 +mkdir enc; cd enc + +warning "Encrypting ..." + +openssl genrsa -out secret.key 2048 +openssl rsa -in secret.key -out secret.pub -outform PEM -pubout +echo "${SECRET}" | openssl rsautl -encrypt -inkey secret.pub -pubin -out secret.dat + +openssl genrsa -out bip.key 2048 +openssl rsa -in bip.key -out bip.pub -outform PEM -pubout +echo "${BIP38}" | openssl rsautl -encrypt -inkey bip.pub -pubin -out bip.dat + +success "Done! Created folder $(echo "${lila}enc${reset}") with all certificates and keys inside." +success "You are now ready to run your docker $(echo "${yellow}forger")." + diff --git a/docker/devnet/purge_all.sh b/docker/production/devnet/purge_all.sh similarity index 100% rename from docker/devnet/purge_all.sh rename to docker/production/devnet/purge_all.sh diff --git a/docker/production/entrypoint.sh b/docker/production/entrypoint.sh new file mode 100755 index 0000000000..86d7ff9593 --- /dev/null +++ b/docker/production/entrypoint.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +sudo /usr/sbin/ntpd -s + +sudo rm -rf /home/node/.config/ark-core/* +sudo rm -rf /home/node/.local/state/ark-core/* +sudo chown node:node -R /home/node +cp -r /home/node/core/packages/core/src/config/$NETWORK /home/node/.config/ark-core/$NETWORK +cd /home/node/core/packages/core +chmod +x ./dist/index.js + +CONFIG=/home/node/.config/ark-core/$NETWORK +SECRET=`openssl rsautl -decrypt -inkey /run/secrets/secret.key -in /run/secrets/secret.dat` +CORE_FORGER_PASSWORD=`openssl rsautl -decrypt -inkey /run/secrets/bip.key -in /run/secrets/bip.dat` + +#startup functions + +config_plain () +{ + ./dist/index.js forger-plain --config $CONFIG --secret "$SECRET" +} + +config_bip () +{ + ./dist/index.js forger-bip38 --config $CONFIG --network $NETWORK --secret "$SECRET" --password "$CORE_FORGER_PASSWORD" +} + +start_relay () +{ + pm2 --name 'ark-core' --no-daemon start ./dist/index.js -- relay --config $CONFIG --network $NETWORK +} + +start_forger () +{ + pm2 --name 'ark-core' --no-daemon start ./dist/index.js -- start --config $CONFIG --network $NETWORK +} + +start_bip () +{ + pm2 --name 'ark-core' --no-daemon start ./dist/index.js -- start --config $CONFIG --network $NETWORK +} + +#configure +if [ -n "$SECRET" ] && [ -n "$CORE_FORGER_PASSWORD" ]; then + config_bip +elif [ "$MODE" = "forger" ] && [ -z "$SECRET" ] && [ -z "$CORE_FORGER_PASSWORD" ]; then + echo "set SECRET and/or CORE_FORGER_PASWORD if you want to run a forger" + exit +elif [ -n "$CORE_FORGER_PASSWORD" ]; then + config_plain +fi + +#relay +if [[ "$MODE" = "relay" ]]; then + start_relay +fi + +#forging +if [ "$MODE" = "forger" ] && [ -n "$SECRET" ] && [ -n "$CORE_FORGER_PASSWORD" ]; then + export CORE_FORGER_BIP38=$(grep bip38 /home/node/.config/ark-core/$NETWORK/delegates.json | awk '{print $2}' | tr -d '"') && export CORE_FORGER_PASSWORD && start_bip && sudo rm -rf /run/secrets/* +elif [ "$MODE" = "forger" ] && [ -z "$SECRET" ] && [ -z "$CORE_FORGER_PASSWORD" ]; then + echo "set SECRET and/or CORE_FORGER_PASWORD if you want to run a forger" + exit +elif [ "$MODE" = "forger" ] && [ -n "$SECRET" ] && [ -z "$CORE_FORGER_PASSWORD" ]; then + start_forger +fi + diff --git a/docker/production/mainnet/docker-compose-build.yml b/docker/production/mainnet/docker-compose-build.yml new file mode 100644 index 0000000000..a184308630 --- /dev/null +++ b/docker/production/mainnet/docker-compose-build.yml @@ -0,0 +1,54 @@ +version: '2' +services: + postgres: + image: "postgres:alpine" + container_name: postgres-mainnet + restart: always + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + networks: + - core + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_mainnet + POSTGRES_USER: node + + core: + build: + context: ../../../ + dockerfile: docker/production/Dockerfile + image: core + container_name: core-mainnet + restart: always + ports: + - "4001:4001" + - "4003:4003" + - "127.0.0.1:4004:4004" + - "127.0.0.1:4005:4005" + - "127.0.0.1:8080:8080" + cap_add: + - SYS_NICE + - SYS_RESOURCE + - SYS_TIME + volumes: + - ~/.config/ark-core:/home/node/.config/ark-core + - ~/.local/share/ark-core:/home/node/.local/share/ark-core + - ~/.local/state/ark-core:/home/node/.local/state/ark-core + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - ./enc:/run/secrets + networks: + - core + env_file: ./mainnet.env + tty: true + links: + - postgres + depends_on: + - postgres +volumes: + postgres: + core: +networks: + core: diff --git a/docker/production/mainnet/docker-compose.yml b/docker/production/mainnet/docker-compose.yml new file mode 100644 index 0000000000..d308f961e3 --- /dev/null +++ b/docker/production/mainnet/docker-compose.yml @@ -0,0 +1,51 @@ +version: '2' +services: + postgres: + image: "postgres:alpine" + container_name: postgres-mainnet + restart: always + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + networks: + - core + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: core_mainnet + POSTGRES_USER: node + + core: + image: arkecosystem/core + container_name: core-mainnet + restart: always + ports: + - "4001:4001" + - "4003:4003" + - "127.0.0.1:4004:4004" + - "127.0.0.1:4005:4005" + - "127.0.0.1:8080:8080" + cap_add: + - SYS_NICE + - SYS_RESOURCE + - SYS_TIME + volumes: + - ~/.config/ark-core:/home/node/.config/ark-core + - ~/.local/share/ark-core:/home/node/.local/share/ark-core + - ~/.local/state/ark-core:/home/node/.local/state/ark-core + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + - ./enc:/run/secrets + networks: + - core + env_file: ./mainnet.env + tty: true + links: + - postgres + depends_on: + - postgres +volumes: + postgres: + core: +networks: + core: diff --git a/docker/production/mainnet/enc.sh b/docker/production/mainnet/enc.sh new file mode 100755 index 0000000000..e510b293d4 --- /dev/null +++ b/docker/production/mainnet/enc.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +########################################################## +# # +# This script encrypts your forging secret and password. # +# # +########################################################## + +type openssl >/dev/null 2>&1 || { echo >&2 "OpenSSL missing. Please install and run the script again."; exit 1; } + +yellow=$(tput setaf 3) +green=$(tput setaf 2) +lila=$(tput setaf 4) +bold=$(tput bold) +reset=$(tput sgr0) + +warning () +{ + echo " ${yellow}==>${reset}${bold} $1${reset}" +} + +success () +{ + echo " ${green}==>${reset}${bold} $1${reset}" +} + +read -sp "Please enter your delegate secret: " inputSecret +echo + +while true; do + read -sp "Please enter your password: " inputPass + echo + read -sp "Please enter password again: " inputPassA + echo + [ "${inputPass}" = "${inputPassA}" ] && break + echo "Password do not match! Please try again." +done + +SECRET="${inputSecret}" +BIP38="${inputPass}" + +rm -rf enc > /dev/null 2>&1 +mkdir enc; cd enc + +warning "Encrypting ..." + +openssl genrsa -out secret.key 2048 +openssl rsa -in secret.key -out secret.pub -outform PEM -pubout +echo "${SECRET}" | openssl rsautl -encrypt -inkey secret.pub -pubin -out secret.dat + +openssl genrsa -out bip.key 2048 +openssl rsa -in bip.key -out bip.pub -outform PEM -pubout +echo "${BIP38}" | openssl rsautl -encrypt -inkey bip.pub -pubin -out bip.dat + +success "Done! Created folder $(echo "${lila}enc${reset}") with all certificates and keys inside." +success "You are now ready to run your docker $(echo "${yellow}forger")." + diff --git a/docker/production/mainnet/mainnet.env b/docker/production/mainnet/mainnet.env new file mode 100644 index 0000000000..a02e965319 --- /dev/null +++ b/docker/production/mainnet/mainnet.env @@ -0,0 +1,20 @@ +#MODE must be one of: relay or forger +#relay: start a relay node only +#forger: start relay and forger +MODE=relay +NETWORK=mainnet +#Core variables +CORE_DB_HOST=postgres-mainnet +CORE_DB_USERNAME=node +CORE_DB_PASSWORD=password +CORE_DB_DATABASE=core_mainnet +CORE_P2P_HOST=0.0.0.0 +CORE_P2P_PORT=4001 +CORE_API_HOST=0.0.0.0 +CORE_API_PORT=4003 +CORE_WEBHOOKS_HOST=0.0.0.0 +CORE_WEBHOOKS_PORT=4004 +CORE_GRAPHQL_HOST=0.0.0.0 +CORE_GRAPHQL_PORT=4005 +CORE_JSON_RPC_HOST=0.0.0.0 +CORE_JSON_RPC_PORT=8080 diff --git a/docker/production/mainnet/purge_all.sh b/docker/production/mainnet/purge_all.sh new file mode 100755 index 0000000000..7be429bc03 --- /dev/null +++ b/docker/production/mainnet/purge_all.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +docker stop $(docker ps -aq) +docker rm $(docker ps -aq) +docker rmi $(docker images -q) +docker volume prune -f +docker network prune -f diff --git a/docker/production/purge_all.sh b/docker/production/purge_all.sh new file mode 100755 index 0000000000..1fe1329a7d --- /dev/null +++ b/docker/production/purge_all.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +docker stop $(docker ps -aq) +docker rm $(docker ps -aq) +docker rmi $(docker images -q) +docker volume prune -f +docker network prune -f diff --git a/docker/testnet/Dockerfile b/docker/testnet/Dockerfile deleted file mode 100644 index e47f4a08ea..0000000000 --- a/docker/testnet/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM node:10 - -WORKDIR /ark-core - -COPY entrypoint.sh / - -RUN apt-get update && \ - apt-get -y install --no-install-recommends \ - build-essential \ - jq \ - iptables \ - python \ - vim && \ - rm -rf /var/lib/apt/lists/* - -EXPOSE 4000 4003 - diff --git a/docker/testnet/docker-compose.yml b/docker/testnet/docker-compose.yml deleted file mode 100644 index 9d42e85a7c..0000000000 --- a/docker/testnet/docker-compose.yml +++ /dev/null @@ -1,43 +0,0 @@ -version: '2' -services: - - postgres: - image: "postgres:alpine" - container_name: ark-testnet-postgres - ports: - - '127.0.0.1:5432:5432' - volumes: - - 'postgres:/var/lib/postgresql/data' - environment: - POSTGRES_PASSWORD: password - POSTGRES_DB: ark_testnet - POSTGRES_USER: ark - - ark-core: - build: . - image: ark-core - container_name: ark-testnet-core - ports: - - "4000:4000" - - "4003:4003" - volumes: - - ark-core:/ark-core - tty: true - privileged: true - links: - - postgres - depends_on: - - postgres - command: - - /bin/sh - - -c - - | - /entrypoint.sh - -volumes: - postgres: - ark-core: - driver_opts: - type: none - device: $PWD/../../ - o: bind diff --git a/docker/testnet/entrypoint.sh b/docker/testnet/entrypoint.sh deleted file mode 100755 index 0fcd93ea11..0000000000 --- a/docker/testnet/entrypoint.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -sysctl -w net.ipv4.conf.all.route_localnet=1 - -POSTGRES=$(ping -c 1 ark-testnet-postgres | awk -F'[()]' '/PING/{print $2}') -CORE=$(ping -c 1 ark-testnet-core | awk -F'[()]' '/PING/{print $2}') - -iptables -I OUTPUT -t nat -o lo -d localhost -p tcp --dport 5432 -j DNAT --to-destination ${POSTGRES}:5432 -iptables -I POSTROUTING -t nat -p tcp --dport 5432 -d ${POSTGRES} -j SNAT --to ${CORE} - -cd /ark-core -rm -rf node_modules package-lock.json > /dev/null 2>&1 -rm -rf packages/core/node_modules packages/core/package-lock.json 2>&1 -npm --quiet install lerna -g && npm --quiet install -g nodemon -lerna bootstrap - -bash diff --git a/docker/testnet/restore.sh b/docker/testnet/restore.sh deleted file mode 100755 index ef8a357f39..0000000000 --- a/docker/testnet/restore.sh +++ /dev/null @@ -1,8 +0,0 @@ -DOCKER_DB_NAME="$(docker-compose ps -q postgres)" -DB_HOSTNAME=postgres -DB_USER=ark -LOCAL_DUMP_PATH="snapshot.dump" - -docker-compose up -d postgres -docker exec -i "${DOCKER_DB_NAME}" pg_restore -C --clean --no-acl --no-owner -U "${DB_USER}" -d "${DB_HOSTNAME}" < "${LOCAL_DUMP_PATH}" -docker-compose stop postgres diff --git a/greenkeeper.json b/greenkeeper.json index 21f3d27557..fb58a5347c 100644 --- a/greenkeeper.json +++ b/greenkeeper.json @@ -1,39 +1,37 @@ { - "groups": { - "default": { - "packages": [ - "package.json", - "packages/core-api/package.json", - "packages/core-blockchain/package.json", - "packages/core-config/package.json", - "packages/core-container/package.json", - "packages/core-database-postgres/package.json", - "packages/core-database/package.json", - "packages/core-debugger-cli/package.json", - "packages/core-deployer/package.json", - "packages/core-elasticsearch/package.json", - "packages/core-error-tracker-bugsnag/package.json", - "packages/core-error-tracker-sentry/package.json", - "packages/core-event-emitter/package.json", - "packages/core-forger/package.json", - "packages/core-graphql/package.json", - "packages/core-http-utils/package.json", - "packages/core-json-rpc/package.json", - "packages/core-logger-winston/package.json", - "packages/core-logger/package.json", - "packages/core-p2p/package.json", - "packages/core-snapshots-cli/package.json", - "packages/core-snapshots/package.json", - "packages/core-test-utils/package.json", - "packages/core-tester-cli/package.json", - "packages/core-transaction-pool-mem/package.json", - "packages/core-transaction-pool/package.json", - "packages/core-utils/package.json", - "packages/core-vote-report/package.json", - "packages/core-webhooks/package.json", - "packages/core/package.json", - "packages/crypto/package.json" - ] + "groups": { + "default": { + "packages": [ + "package.json", + "packages/core-api/package.json", + "packages/core-blockchain/package.json", + "packages/core-container/package.json", + "packages/core-database-postgres/package.json", + "packages/core-database/package.json", + "packages/core-debugger-cli/package.json", + "packages/core-deployer/package.json", + "packages/core-elasticsearch/package.json", + "packages/core-error-tracker-bugsnag/package.json", + "packages/core-error-tracker-sentry/package.json", + "packages/core-event-emitter/package.json", + "packages/core-forger/package.json", + "packages/core-graphql/package.json", + "packages/core-http-utils/package.json", + "packages/core-json-rpc/package.json", + "packages/core-logger-winston/package.json", + "packages/core-logger/package.json", + "packages/core-p2p/package.json", + "packages/core-snapshots-cli/package.json", + "packages/core-snapshots/package.json", + "packages/core-test-utils/package.json", + "packages/core-tester-cli/package.json", + "packages/core-transaction-pool/package.json", + "packages/core-utils/package.json", + "packages/core-vote-report/package.json", + "packages/core-webhooks/package.json", + "packages/core/package.json", + "packages/crypto/package.json" + ] + } } - } } diff --git a/install.sh b/install.sh new file mode 100644 index 0000000000..c8134c72bd --- /dev/null +++ b/install.sh @@ -0,0 +1,364 @@ +#!/usr/bin/env bash + +# ----------------------------------- +# TYPOGRAPHY +# ----------------------------------- + +red=$(tput setaf 1) +green=$(tput setaf 2) +yellow=$(tput setaf 3) +lila=$(tput setaf 4) +pink=$(tput setaf 5) +blue=$(tput setaf 6) +white=$(tput setaf 7) +black=$(tput setaf 8) + +bg_red=$(tput setab 1) +bg_green=$(tput setab 2) +bg_yellow=$(tput setab 3) +bg_lila=$(tput setab 4) +bg_pink=$(tput setab 5) +bg_blue=$(tput setab 6) +bg_white=$(tput setab 7) +bg_black=$(tput setab 8) + +bold=$(tput bold) +reset=$(tput sgr0) + +# Indicators +heading () +{ + echo " ${lila}==>${reset}${bold} $1${reset}" +} + +success () +{ + echo " ${green}==>${reset}${bold} $1${reset}" +} + +info () +{ + echo " ${blue}==>${reset}${bold} $1${reset}" +} + +warning () +{ + echo " ${yellow}==>${reset}${bold} $1${reset}" +} + +error () +{ + echo " ${red}==>${reset}${bold} $1${reset}" +} + +# Colored Text +text_red () +{ + echo "${red}$1${reset}" +} + +text_green () +{ + echo "${green}$1${reset}" +} + +text_yellow () +{ + echo "${yellow}$1${reset}" +} + +text_lila () +{ + echo "${lila}$1${reset}" +} + +text_pink () +{ + echo "${pink}$1${reset}" +} + +text_blue () +{ + echo "${blue}$1${reset}" +} + +text_white () +{ + echo "${white}$1${reset}" +} + +text_black () +{ + echo "${black}$1${reset}" +} + +# Styles +text_bold () +{ + echo "${bold}" +} + +text_reset () +{ + echo "${reset}" +} + +# Helpers +divider () +{ + text_lila " ===============================================================" +} + +paragraph () +{ + text_white "$1" | fold -w67 | paste -sd'\n' - +} + +# Detect pkg type +DEB=$(which apt-get) +RPM=$(which yum) + +# Detect SystemV / SystemD +SYS=$([[ -L "/sbin/init" ]] && echo 'SystemD' || echo 'SystemV') + +if [[ ! -z $DEB ]]; then + success "Running install for Debian derivate" +elif [[ ! -z $RPM ]]; then + success "Running install for RedHat derivate" +else + heading "Not supported system" + exit 1; +fi + +if [[ $(locale -a | grep ^en_US.UTF-8) ]] || [[ $(locale -a | grep ^en_US.utf8) ]]; then + if ! $(grep -E "(en_US.UTF-8)" "$HOME/.bashrc"); then + # Setting the bashrc locale + echo "export LC_ALL=en_US.UTF-8" >> "$HOME/.bashrc" + echo "export LANG=en_US.UTF-8" >> "$HOME/.bashrc" + echo "export LANGUAGE=en_US.UTF-8" >> "$HOME/.bashrc" + + # Setting the current shell locale + export LC_ALL="en_US.UTF-8" + export LANG="en_US.UTF-8" + export LANGUAGE="en_US.UTF-8" + fi +else + # Install en_US.UTF-8 Locale + if [[ ! -z $DEB ]]; then + sudo locale-gen en_US.UTF-8 + sudo update-locale LANG=en_US.UTF-8 + elif [[ ! -z $RPM ]]; then + sudo localedef -c -i en_US -f UTF-8 en_US.UTF-8 + fi + + # Setting the current shell locale + export LC_ALL="en_US.UTF-8" + export LANG="en_US.UTF-8" + export LANGUAGE="en_US.UTF-8" + + # Setting the bashrc locale + echo "export LC_ALL=en_US.UTF-8" >> "$HOME/.bashrc" + echo "export LANG=en_US.UTF-8" >> "$HOME/.bashrc" + echo "export LANGUAGE=en_US.UTF-8" >> "$HOME/.bashrc" +fi + +heading "Installing system dependencies..." + +if [[ ! -z $DEB ]]; then + sudo apt-get update + sudo apt-get install -y git curl apt-transport-https update-notifier +elif [[ ! -z $RPM ]]; then + sudo yum update -y + sudo yum install git curl epel-release -y +fi + +success "Installed system dependencies!" + +heading "Installing node.js & npm..." + +sudo rm -rf /usr/local/{lib/node{,/.npm,_modules},bin,share/man}/{npm*,node*,man1/node*} +sudo rm -rf ~/{.npm,.forever,.node*,.cache,.nvm} + +if [[ ! -z $DEB ]]; then + sudo wget --quiet -O - https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add - + (echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main" | sudo tee /etc/apt/sources.list.d/nodesource.list) + sudo apt-get update + sudo apt-get install nodejs -y +elif [[ ! -z $RPM ]]; then + sudo yum install gcc-c++ make -y + curl -sL https://rpm.nodesource.com/setup_10.x | sudo -E bash - > /dev/null 2>&1 +fi + +success "Installed node.js & npm!" + +heading "Installing Yarn..." + +if [[ ! -z $DEB ]]; then + curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - + (echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list) + + sudo apt-get update + sudo apt-get install -y yarn +elif [[ ! -z $RPM ]]; then + curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | sudo tee /etc/yum.repos.d/yarn.repo + sudo yum install yarn -y +fi + +success "Installed Yarn!" + +heading "Installing PM2..." + +sudo yarn global add pm2 +pm2 install pm2-logrotate +pm2 set pm2-logrotate:max_size 500M +pm2 set pm2-logrotate:compress true +pm2 set pm2-logrotate:retain 7 + +success "Installed PM2!" + +heading "Installing program dependencies..." + +if [[ ! -z $DEB ]]; then + sudo apt-get install build-essential libcairo2-dev pkg-config libtool autoconf automake python libpq-dev jq -y +elif [[ ! -z $RPM ]]; then + sudo yum groupinstall "Development Tools" -y -q + sudo yum install postgresql-devel jq -y -q +fi + +success "Installed program dependencies!" + +heading "Installing PostgreSQL..." + +if [[ ! -z $DEB ]]; then + sudo apt-get update + sudo apt-get install postgresql postgresql-contrib -y +elif [[ ! -z $RPM ]]; then + sudo yum install postgresql-server postgresql-contrib -y + + if [[ "$SYS" == "SystemV" ]]; then + sudo service postgresql initdb + sudo service postgresql start + else + sudo postgresql-setup initdb + sudo systemctl start postgresql + fi +fi + +success "Installed PostgreSQL!" + +heading "Installing NTP..." + +sudo timedatectl set-ntp off > /dev/null 2>&1 # disable the default systemd timesyncd service + +if [[ ! -z $DEB ]]; then + sudo apt-get install ntp -yyq +elif [[ ! -z $RPM ]]; then + sudo yum install ntp -y -q +fi + +sudo ntpd -gq + +success "Installed NTP!" + +heading "Installing system updates..." + +if [[ ! -z $DEB ]]; then + sudo apt-get update + sudo apt-get upgrade -yqq + sudo apt-get dist-upgrade -yq + sudo apt-get autoremove -yyq + sudo apt-get autoclean -yq +elif [[ ! -z $RPM ]]; then + sudo yum update + sudo yum clean +fi + +success "Installed system updates!" + +heading "Installing Ark Core..." + +cd "$HOME" + +if [ -d "ark-core" ]; then + heading "Removing existing folder..." + rm -rf ark-core +fi + +git clone https://github.com/ArkEcosystem/core.git ~/ark-core +cd ark-core +yarn setup + +success "Installed Ark Core!" + +# setup configuration +read -p "Would you like to configure the core? [y/N]: " choice + +if [[ "$choice" =~ ^(yes|y|Y) ]]; then + info "Which network would you like to configure?" + + validNetworks=("mainnet" "devnet" "testnet") + + select opt in "${validNetworks[@]}"; do + case "$opt" in + "mainnet") + mkdir -p "${HOME}/.config/ark-core/mainnet" + cp -rf "${HOME}/ark-core/packages/core/src/config/mainnet/." "${HOME}/.config/ark-core/mainnet" + break + ;; + "devnet") + mkdir -p "${HOME}/.config/ark-core/devnet" + cp -rf "${HOME}/ark-core/packages/core/src/config/devnet/." "${HOME}/.config/ark-core/devnet" + break + ;; + "testnet") + mkdir -p "${HOME}/.config/ark-core/testnet" + cp -rf "${HOME}/ark-core/packages/core/src/config/testnet/." "${HOME}/.config/ark-core/testnet" + break + ;; + *) + echo "Invalid option $REPLY" + ;; + esac + done +fi + +# setup postgres username, password and database +read -p "Would you like to configure the database? [y/N]: " choice + +if [[ "$choice" =~ ^(yes|y|Y) ]]; then + read -p "Enter the database username: " databaseUsername + read -p "Enter the database password: " databasePassword + read -p "Enter the database name: " databaseName + + userExists=$(sudo -i -u postgres psql -c "SELECT * FROM pg_user WHERE usename = '${databaseUsername}'" | grep -c "1 row") + databaseExists=$(sudo -i -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname = '${databaseName}'") + + if [[ $userExists == 1 ]]; then + read -p "The database user ${databaseUsername} already exists, do you want to overwrite it? [y/N]: " choice + + if [[ "$choice" =~ ^(yes|y|Y) ]]; then + if [[ $databaseExists == 1 ]]; then + sudo -i -u postgres psql -c "ALTER DATABASE ${databaseName} OWNER TO postgres;" + fi + sudo -i -u postgres psql -c "DROP USER ${databaseUsername}" + sudo -i -u postgres psql -c "CREATE USER ${databaseUsername} WITH PASSWORD '${databasePassword}' CREATEDB;" + elif [[ "$choice" =~ ^(no|n|N) ]]; then + continue; + fi + else + sudo -i -u postgres psql -c "CREATE USER ${databaseUsername} WITH PASSWORD '${databasePassword}' CREATEDB;" + fi + + if [[ $databaseExists == 1 ]]; then + read -p "The database ${databaseName} already exists, do you want to overwrite it? [y/N]: " choice + + if [[ "$choice" =~ ^(yes|y|Y) ]]; then + sudo -i -u postgres psql -c "DROP DATABASE ${databaseName};" + sudo -i -u postgres psql -c "CREATE DATABASE ${databaseName} WITH OWNER ${databaseUsername};" + elif [[ "$choice" =~ ^(no|n|N) ]]; then + sudo -i -u postgres psql -c "ALTER DATABASE ${databaseName} OWNER TO ${databaseUsername};" + fi + else + sudo -i -u postgres psql -c "CREATE DATABASE ${databaseName} WITH OWNER ${databaseUsername};" + fi +fi diff --git a/jest-preset.json b/jest-preset.json new file mode 100644 index 0000000000..7396af0c7c --- /dev/null +++ b/jest-preset.json @@ -0,0 +1,15 @@ +{ + "testEnvironment": "node", + "bail": false, + "verbose": true, + "transform": { + "^.+\\.tsx?$": "ts-jest" + }, + "testMatch": ["**/*.test.ts"], + "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"], + "collectCoverage": false, + "coverageDirectory": "/.coverage", + "collectCoverageFrom": ["src/**/*.ts", "!**/node_modules/**"], + "watchman": false, + "setupTestFrameworkScriptFile": "jest-extended" +} diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index febc94cc95..0000000000 --- a/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - bail: false, - verbose: true, - testEnvironment: 'node', - testMatch: ['**/packages/**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - coverageDirectory: '/.coverage', - collectCoverageFrom: ['packages/**/lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/lerna.json b/lerna.json index 6fa44c3962..f06def6804 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,7 @@ { - "lerna": "2.10.0", - "packages": ["packages/*", "plugins/*"], - "npmClient": "yarn", - "useWorkspaces": true, - "version": "independent" + "lerna": "3.5.0", + "npmClient": "yarn", + "packages": ["packages/*", "plugins/*"], + "useWorkspaces": true, + "version": "2.1.0" } diff --git a/package.json b/package.json index e54155c725..52c50538c9 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,91 @@ { - "private": true, - "scripts": { - "bootstrap": "lerna bootstrap", - "clean": "lerna clean", - "commit": "git-cz", - "lint": "lerna run lint", - "prepare": "lerna run prepare", - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:force-exit": "cross-env ARK_ENV=test jest --runInBand --forceExit", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "format": "prettier --write \"./*.{js,json,md}\" \"./packages/**/*.{js,json,md}\"", - "snyk": "./node_modules/.bin/snyk protect" - }, - "devDependencies": { - "@arkecosystem/eslint-config-base": "^0.1.0", - "@babel/core": "^7.1.6", - "@babel/preset-env": "^7.1.6", - "axios": "^0.18.0", - "babel-loader": "^8.0.4", - "body-parser": "^1.18.3", - "codecov": "^3.1.0", - "cross-env": "^5.2.0", - "docdash": "^1.0.0", - "eslint": "^5.9.0", - "eslint-config-airbnb-base": "^13.1.0", - "eslint-config-prettier": "^3.3.0", - "eslint-plugin-import": "^2.14.0", - "eslint-plugin-jest": "^22.1.0", - "eslint-plugin-node": "^8.0.0", - "eslint-plugin-promise": "^4.0.1", - "express": "^4.16.4", - "husky": "^1.2.0", - "jest": "^23.6.0", - "jest-extended": "^0.11.0", - "js-yaml": "^3.12.0", - "lerna": "^3.5.0", - "lint-staged": "^8.1.0", - "npm-check-updates": "^2.15.0", - "prettier": "^1.15.2", - "regenerator-runtime": "^0.13.1", - "request-promise": "^4.2.2", - "rimraf": "^2.6.2", - "snyk": "^1.116.0", - "uuid": "^3.3.2", - "webpack": "^4.26.1", - "webpack-cli": "^3.1.2", - "webpack-merge": "^4.1.4", - "webpack-node-externals": "^1.7.2" - }, - "workspaces": [ - "packages/*", - "plugins/*" - ], - "husky": { - "hooks": { - "pre-commit": "lint-staged && ./scripts/pre-commit.sh" + "private": true, + "name": "core", + "description": "The packages that make up the Ark Core", + "scripts": { + "lerna": "./node_modules/lerna/cli.js", + "setup": "yarn && yarn clean && yarn bootstrap && yarn build", + "bootstrap": "yarn lerna bootstrap", + "clean": "yarn lerna clean --yes", + "build": "yarn lerna run build", + "lint": "yarn lerna run lint", + "format": "yarn lint && yarn prettier", + "prettier": "prettier --write \"./*.{ts,js,json,md}\" \"./packages/**/*.{ts,js,json,md}\"", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "snyk": "./node_modules/.bin/snyk protect", + "upgrade": "cross-env-shell ./scripts/upgrade.sh", + "version": "cross-env-shell ./scripts/version.sh", + "release": "cross-env-shell ./scripts/release.sh", + "updates": "yarn lerna run updates", + "docker": "node ./scripts/docker/generate-docker.js", + "bench": "node benchmark/index.js" + }, + "devDependencies": { + "@babel/core": "^7.2.2", + "@babel/preset-env": "^7.2.0", + "@faustbrian/benchmarker": "^0.1.2", + "@sindresorhus/tsconfig": "^0.1.1", + "@types/babel__core": "^7.0.4", + "@types/body-parser": "^1.17.0", + "@types/express": "^4.16.0", + "@types/jest": "^23.3.10", + "@types/js-yaml": "^3.11.4", + "@types/node": "^10.12.17", + "@types/prettier": "^1.15.2", + "@types/pretty-ms": "^4.0.0", + "@types/request-promise": "^4.1.42", + "@types/rimraf": "^2.0.2", + "@types/uuid": "^3.4.4", + "@types/webpack": "^4.4.23", + "@types/webpack-merge": "^4.1.3", + "@types/webpack-node-externals": "^1.6.3", + "axios": "^0.18.0", + "babel-loader": "^8.0.4", + "body-parser": "^1.18.3", + "codecov": "^3.1.0", + "cross-env": "^5.2.0", + "del-cli": "^1.1.0", + "docdash": "^1.0.1", + "express": "^4.16.4", + "husky": "^1.3.0", + "jest": "^23.6.0", + "jest-extended": "^0.11.0", + "js-yaml": "^3.12.0", + "lerna": "^3.6.0", + "lint-staged": "^8.1.0", + "npm-check-updates": "^2.15.0", + "prettier": "^1.15.3", + "prompts": "^2.0.1", + "regenerator-runtime": "^0.13.1", + "request-promise": "^4.2.2", + "rimraf": "^2.6.2", + "snyk": "^1.118.0", + "ts-jest": "^23.10.5", + "tslint": "^5.12.0", + "tslint-config-prettier": "^1.17.0", + "typedoc": "^0.13.0", + "typescript": "^3.2.2", + "uuid": "^3.3.2", + "webpack": "^4.27.1", + "webpack-cli": "^3.1.2", + "webpack-merge": "^4.1.5", + "webpack-node-externals": "^1.7.2" + }, + "workspaces": [ + "packages/*", + "plugins/*" + ], + "husky": { + "hooks": { + "pre-commit": "lint-staged && cross-env-shell ./scripts/pre-commit.sh" + } + }, + "jest": { + "preset": "./jest-preset.json", + "collectCoverageFrom": [ + "packages/**/src/**/*.ts", + "!**/node_modules/**" + ] } - } } diff --git a/packages/core-api/CHANGELOG.md b/packages/core-api/CHANGELOG.md deleted file mode 100644 index 1e4a71ec9d..0000000000 --- a/packages/core-api/CHANGELOG.md +++ /dev/null @@ -1,105 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.14 - 2018-12-07 - -### Fixed - -- Ensure safe integer range for block height lookups - -## 0.2.13 - 2018-12-06 - -### Fixed - -- Perform second-signature checks in the `canApply` logic of multi-signatures - -## 0.2.12 - 2018-12-05 - -### Changed - -- Increase cache generation timeout and make it configurable - -## 0.2.11 - 2018-12-05 - -### Fixed - -- Take milestones into account for supply calculations - -## 0.2.1 - 2018-12-04 - -### Added - -- Allow block display via height in v2 API - -### Fixed - -- Return the correct total count for `/api/v2/peers` - -## 0.2.0 - 2018-12-03 - -### Added - -- Return forged rewards and fees via v2 API -- Return error feedback for transaction posting via v2 API -- Cache block heights to reduce database load -- Implement database repositories -- Limit the number of transactions per request if posting -- `ownerId` property for transaction searches -- Blockchains endpoint to provide information like supply -- Allow registration of additional plugins -- Run HTTP & HTTPS server at the same time -- Validate transaction payloads -- Implement server side caching via server methods - -### Fixed - -- Ensure order parameters are treated as lower-case and properly formatted -- Handle trailing slashes to avoid v1 issues - -### Changed - -- Use the IANA format for the API vendor in the `Accept` header -- Use the official `hapi-api-version` dependency -- Return ports as integers -- Improved some error messages -- Return broadcast IDs for improved feedback -- Sort peers by latency -- Stricter validation of parameters -- Dropped node.js 9 as minimum requirement in favour of node.js 10 -- Return a `type` and `message` property for transaction errors -- Only allow JSON requests to the API - -### Removed - -- All `redis` integrations and dependencies - -### Fixed - -- Return the delegate list in the v1 format with correct limits -- Add the missing `vendorField` property to transactions -- Broken search in the v2 API for blocks and transactions -- Various search, sort and pagination issues -- Failing search because of unknown parameters -- Properly handle CORS headers -- Race condition that would result in duplicate transactions in the transaction pool -- Fixed the value returned by `unconfirmedBalance` -- Various inconsistencies of string/integer values in the v1 API -- Various inconsistencies of property names in the v1 API -- Various validation schemas -- Added missing `orderBy` property for block transaction sorting -- Crashes caused by bad sorting handling -- Properly return the total forged and total amount of transactions that was forged -- Allow an offset of 0 as default -- Sorting of transactions & wallets - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-api/LICENSE b/packages/core-api/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-api/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-api/README.md b/packages/core-api/README.md index 4f63fccdf8..39b72d5bd4 100644 --- a/packages/core-api/README.md +++ b/packages/core-api/README.md @@ -14,9 +14,10 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-api/__tests__/__support__/setup.js b/packages/core-api/__tests__/__support__/setup.js deleted file mode 100644 index fd3bad69ac..0000000000 --- a/packages/core-api/__tests__/__support__/setup.js +++ /dev/null @@ -1,26 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -const activeDelegates = require('@arkecosystem/core-test-utils/fixtures/testnet/delegates') -const generateRound = require('./utils/generate-round') - -const round = generateRound( - activeDelegates.map(delegate => delegate.publicKey), - 1, -) - -exports.setUp = async () => { - jest.setTimeout(60000) - - await appHelper.setUp({}) - - const connection = app.resolvePlugin('database') - await connection.db.rounds.truncate() - await connection.buildWallets(1) - await connection.saveWallets(true) - await connection.saveRound(round) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-api/__tests__/__support__/setup.ts b/packages/core-api/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..d990a0a17e --- /dev/null +++ b/packages/core-api/__tests__/__support__/setup.ts @@ -0,0 +1,64 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import delay from "delay"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; +import { plugin } from "../../src/plugin"; + +import { delegates } from "../../../core-test-utils/src/fixtures"; +import { generateRound } from "./utils/generate-round"; + +import { queries } from "../../../core-database-postgres/src/queries"; + +const round = generateRound(delegates.map(delegate => delegate.publicKey), 1); + +const options = { + enabled: true, + host: "0.0.0.0", + port: 4003, + whitelist: ["*"], +}; + +async function setUp() { + jest.setTimeout(60000); + + await setUpContainer({ + exclude: [ + "@arkecosystem/core-webhooks", + "@arkecosystem/core-graphql", + "@arkecosystem/core-forger", + "@arkecosystem/core-json-rpc", + "@arkecosystem/core-api", + ], + }); + + const databaseService = app.resolvePlugin("database"); + await databaseService.connection.roundsRepository.truncate(); + await databaseService.buildWallets(1); + await databaseService.saveWallets(true); + await databaseService.saveRound(round); + + await registerWithContainer(plugin, options); + await delay(1000); // give some more time for api server to be up +} + +async function tearDown() { + await app.tearDown(); + + await plugin.deregister(app, options); +} + +async function calculateRanks() { + const databaseService = app.resolvePlugin("database"); + + const rows = await (databaseService.connection as any).query.manyOrNone(queries.spv.delegatesRanks); + + rows.forEach((delegate, i) => { + const wallet = databaseService.walletManager.findByPublicKey(delegate.publicKey); + wallet.missedBlocks = +delegate.missedBlocks; + (wallet as any).rate = i + 1; + + databaseService.walletManager.reindex(wallet); + }); +} + +export { calculateRanks, setUp, tearDown }; diff --git a/packages/core-api/__tests__/__support__/utils/generate-round.js b/packages/core-api/__tests__/__support__/utils/generate-round.js deleted file mode 100644 index 69eb78bb8b..0000000000 --- a/packages/core-api/__tests__/__support__/utils/generate-round.js +++ /dev/null @@ -1,7 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') - -module.exports = (delegates, round) => delegates.map(delegate => ({ - round, - publicKey: delegate, - voteBalance: bignumify('245098000000000'), -})) diff --git a/packages/core-api/__tests__/__support__/utils/generate-round.ts b/packages/core-api/__tests__/__support__/utils/generate-round.ts new file mode 100644 index 0000000000..588e26d944 --- /dev/null +++ b/packages/core-api/__tests__/__support__/utils/generate-round.ts @@ -0,0 +1,9 @@ +import { bignumify } from "@arkecosystem/core-utils"; + +export function generateRound(delegates, round) { + return delegates.map(delegate => ({ + round, + publicKey: delegate, + voteBalance: bignumify("245098000000000"), + })); +} diff --git a/packages/core-api/__tests__/repositories/transactions.test.js b/packages/core-api/__tests__/repositories/transactions.test.js deleted file mode 100644 index f4ee83fc5d..0000000000 --- a/packages/core-api/__tests__/repositories/transactions.test.js +++ /dev/null @@ -1,160 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const { crypto } = require('@arkecosystem/crypto') -const app = require('../__support__/setup') - -let genesisBlock -let genesisTransaction -let repository - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') - genesisTransaction = genesisBlock.transactions[0] -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(async () => { - repository = require('../../lib/repositories/transactions') -}) - -describe('Transaction Repository', () => { - describe('search', () => { - const expectSearch = async (params, expected) => { - // await connection.saveBlock(genesisBlock) - - const transactions = await repository.search(params) - expect(transactions).toBeObject() - - expect(transactions.count).toBeNumber() - - expect(transactions.rows).toBeArray() - expect(transactions.rows).not.toBeEmpty() - transactions.rows.forEach(transaction => { - expect(transaction).toContainKeys([ - 'id', - 'version', - 'sequence', - 'timestamp', - 'type', - 'amount', - 'fee', - 'serialized', - 'blockId', - 'senderPublicKey', - 'vendorFieldHex', - 'block', - ]) - }) - - expect(transactions.count).toBe(expected) - } - - it('should be a function', () => { - expect(repository.search).toBeFunction() - }) - - it('should search transactions by the specified `id`', async () => { - await expectSearch({ id: genesisTransaction.id }, 1) - }) - - it('should search transactions by the specified `blockId`', async () => { - await expectSearch({ blockId: genesisTransaction.blockId }, 153) - }) - - it('should search transactions by the specified `type`', async () => { - await expectSearch({ type: genesisTransaction.type }, 51) - }) - - it('should search transactions by the specified `version`', async () => { - await expectSearch({ version: genesisTransaction.version }, 153) - }) - - it('should search transactions by the specified `senderPublicKey`', async () => { - await expectSearch( - { senderPublicKey: genesisTransaction.senderPublicKey }, - 51, - ) - }) - - it('should search transactions by the specified `senderId`', async () => { - const senderId = crypto.getAddress(genesisTransaction.senderPublicKey, 23) - await expectSearch({ senderId }, 51) - }) - - it('should search transactions by the specified `recipientId`', async () => { - await expectSearch({ recipientId: genesisTransaction.recipientId }, 2) - }) - - it('should search transactions by the specified `timestamp`', async () => { - await expectSearch( - { - timestamp: { - from: genesisTransaction.timestamp, - to: genesisTransaction.timestamp, - }, - }, - 153, - ) - }) - - it('should search transactions by the specified `amount`', async () => { - await expectSearch( - { - amount: { - from: genesisTransaction.amount, - to: genesisTransaction.amount, - }, - }, - 50, - ) - }) - - it('should search transactions by the specified `fee`', async () => { - await expectSearch( - { - fee: { - from: genesisTransaction.fee, - to: genesisTransaction.fee, - }, - }, - 153, - ) - }) - - it('should search transactions by the specified `vendorFieldHex`', async () => { - await expectSearch( - { vendorFieldHex: genesisTransaction.vendorFieldHex }, - 153, - ) - }) - - describe('when there are more than 1 condition', () => { - it('should search transactions that includes all of them (AND)', async () => { - await expectSearch( - { recipientId: genesisTransaction.recipientId, type: 3 }, - 1, - ) - }) - }) - - describe('when no results', () => { - it('should not return them', async () => { - // await connection.saveBlock(genesisBlock) - - const transactions = await repository.search({ recipientId: 'dummy' }) - expect(transactions).toBeObject() - - expect(transactions).toHaveProperty('count', 0) - - expect(transactions.rows).toBeArray() - expect(transactions.rows).toBeEmpty() - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/repositories/transactions.test.ts b/packages/core-api/__tests__/repositories/transactions.test.ts new file mode 100644 index 0000000000..cd2685399a --- /dev/null +++ b/packages/core-api/__tests__/repositories/transactions.test.ts @@ -0,0 +1,358 @@ +import "@arkecosystem/core-test-utils"; +import "jest-extended"; + +import { crypto } from "@arkecosystem/crypto"; +import genesisBlock from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; +// noinspection TypeScriptPreferShortImport +import { TransactionsRepository } from "../../dist/repositories/transactions"; +import { setUp, tearDown } from "../__support__/setup"; + +let repository; +let genesisTransaction; + +beforeAll(async () => { + await setUp(); + + repository = new TransactionsRepository(); + + genesisTransaction = genesisBlock.transactions[0]; +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("Transaction Repository", () => { + describe("search", () => { + const expectSearch = async (paramsOrTransactions, count = 1) => { + let transactions; + if (paramsOrTransactions.rows) { + transactions = paramsOrTransactions; + } else { + transactions = await repository.search(paramsOrTransactions); + } + + expect(transactions).toBeObject(); + + expect(transactions.count).toBeNumber(); + // expect(transactions.count).toBe(count); + + expect(transactions.rows).toBeArray(); + if (count > 0) { + expect(transactions.rows).not.toBeEmpty(); + transactions.rows.forEach(transaction => { + expect(transaction).toContainKeys([ + "id", + "version", + "sequence", + "timestamp", + "type", + "amount", + "fee", + "serialized", + "blockId", + "senderPublicKey", + "vendorFieldHex", + "block", + ]); + }); + } + }; + + it("should search transactions by the specified `id`", async () => { + await expectSearch({ id: genesisTransaction.id }); + }); + + it("should search transactions by the specified `blockId`", async () => { + await expectSearch({ blockId: genesisTransaction.blockId }, 153); + }); + + it("should search transactions by the specified `type`", async () => { + await expectSearch({ type: genesisTransaction.type }, 51); + }); + + it("should search transactions by the specified `version`", async () => { + await expectSearch({ version: genesisTransaction.version }, 153); + }); + + it("should search transactions by the specified `senderPublicKey`", async () => { + await expectSearch({ senderPublicKey: genesisTransaction.senderPublicKey }, 51); + }); + + describe("`senderId`", () => { + it("should search transactions by the specified `senderId`", async () => { + const senderPublicKey = genesisTransaction.senderPublicKey; + const senderId = crypto.getAddress(senderPublicKey, 23); + + const transactions = await repository.search({ senderId }); + + await expectSearch(transactions, 51); + + for (const row of transactions.rows) { + expect(row.senderPublicKey).toEqual(senderPublicKey); + } + }); + + describe("when the `senderId` is incorrect", () => { + it("should return no result", async () => { + const senderId = "unknown"; + await expectSearch({ senderId }, 0); + }); + }); + }); + + it("should search transactions by the specified `recipientId`", async () => { + await expectSearch({ recipientId: genesisTransaction.recipientId }, 2); + }); + + describe("when searching by `senderPublicKey` and `recipientId`", () => { + it("should search transactions by sent by `senderPublicKey` to `recipientId`", async () => { + const senderPublicKey = genesisTransaction.senderPublicKey; + const recipientId = genesisBlock.transactions[2].recipientId; + + let transactions = await repository.search({ + recipientId, + senderPublicKey, + }); + + await expectSearch(transactions, 1); + + for (const row of transactions.rows) { + expect(row.senderPublicKey).toEqual(senderPublicKey); + expect(row.recipientId).toEqual(recipientId); + } + + transactions = await repository.search({ + recipientId: "unknown", + senderPublicKey, + }); + + await expectSearch(transactions, 0); + }); + }); + + describe("when searching by `senderId` and `recipientId`", () => { + it("should search transactions by sent by `senderId` to `recipientId`", async () => { + const senderId = crypto.getAddress(genesisTransaction.senderPublicKey, 23); + const recipientId = genesisBlock.transactions[2].recipientId; + + let transactions = await repository.search({ + recipientId, + senderId, + }); + + await expectSearch(transactions, 1); + + for (const row of transactions.rows) { + expect(row.senderPublicKey).toEqual(genesisTransaction.senderPublicKey); + expect(row.recipientId).toEqual(recipientId); + } + + transactions = await repository.search({ + recipientId: "unknown", + senderId, + }); + + await expectSearch(transactions, 0); + }); + }); + + describe("`addresses`", () => { + const addresses = [genesisBlock.transactions[1].recipientId, genesisBlock.transactions[4].recipientId]; + + it("should search transactions by the specified `addresses` (sender and recipient)", async () => { + await expectSearch({ addresses: [addresses[0]] }, 3); + + await expectSearch({ addresses }, 6); + }); + + describe("when `addresses` is empty", () => { + it("should return all transactions", async () => { + await expectSearch({ address: [] }, 153); + }); + }); + + describe("when searching by `addresses` and `senderId`", () => { + it("should search transactions by the `addresses`, but only include those received from `senderId`", async () => { + const senderId = crypto.getAddress(genesisTransaction.senderPublicKey, 23); + + let transactions = await repository.search({ + senderId, + addresses, + }); + + await expectSearch(transactions, 2); + + for (const row of transactions.rows) { + expect(row.senderPublicKey).toEqual(genesisTransaction.senderPublicKey); + } + + transactions = await repository.search({ + senderId: "unknown", + addresses, + }); + + await expectSearch(transactions, 0); + }); + }); + + describe("when searching by `addresses` and `senderPublicKey`", () => { + it("should search transactions by the `addresses`, but only include those received from `senderPublicKey`", async () => { + const { senderPublicKey } = genesisTransaction; + + let transactions = await repository.search({ + senderPublicKey, + addresses, + }); + + await expectSearch(transactions, 2); + + for (const row of transactions.rows) { + expect(row.senderPublicKey).toEqual(senderPublicKey); + } + + transactions = await repository.search({ + senderPublicKey: "unknown", + addresses, + }); + + await expectSearch(transactions, 0); + }); + }); + + describe("when searching by `addresses` and `recipientId`", () => { + it("should search transactions by the `addresses`, but only include those sent to `recipientId`", async () => { + const senderId = crypto.getAddress(genesisTransaction.senderPublicKey, 23); + const recipientId = genesisBlock.transactions[2].recipientId; + + let transactions = await repository.search({ + recipientId, + addresses: [senderId], + }); + + await expectSearch(transactions, 1); + + for (const row of transactions.rows) { + expect(row.recipientId).toEqual(recipientId); + } + + transactions = await repository.search({ + recipientId: "unknown", + addresses, + }); + + await expectSearch(transactions, 0); + }); + }); + + describe("when searching by `addresses`, `senderId` and `recipientId`", () => { + it("should search transactions by `senderId` and `recipientId` only", async () => { + const senderId = crypto.getAddress(genesisTransaction.senderPublicKey, 23); + const params = { + senderId, + recipientId: genesisTransaction.recipientId, + addresses, + }; + const transactions = await repository.search(params); + + await expectSearch(transactions, 1); + + const { rows } = transactions; + expect(rows[0].senderPublicKey).toEqual(genesisTransaction.senderPublicKey); + expect(rows[0].recipientId).toEqual(genesisTransaction.recipientId); + }); + }); + + describe("when searching by `addresses`, `senderPublicKey` and `recipientId`", () => { + it("should search transactions by `senderPublicKey` and `recipientId` only", async () => { + const params = { + senderPublicKey: genesisTransaction.senderPublicKey, + recipientId: genesisTransaction.recipientId, + addresses, + }; + const transactions = await repository.search(params); + + await expectSearch(transactions, 1); + + const { rows } = transactions; + expect(rows[0].senderPublicKey).toEqual(genesisTransaction.senderPublicKey); + expect(rows[0].recipientId).toEqual(genesisTransaction.recipientId); + }); + }); + + describe("when searching by `addresses` and other field`", () => { + it("should search transactions by the specified `addresses` and that field", async () => { + const amount = 245098000000000; + const transactions = await repository.search({ + amount: { from: amount }, + addresses, + }); + + await expectSearch(transactions, 2); + + for (const row of transactions.rows) { + expect(row.amount).toEqual(amount.toString()); + } + }); + }); + }); + + it("should search transactions by the specified `timestamp`", async () => { + await expectSearch( + { + timestamp: { + from: genesisTransaction.timestamp, + to: genesisTransaction.timestamp, + }, + }, + 153, + ); + }); + + it("should search transactions by the specified `amount`", async () => { + await expectSearch( + { + amount: { + from: genesisTransaction.amount, + to: genesisTransaction.amount, + }, + }, + 50, + ); + }); + + it("should search transactions by the specified `fee`", async () => { + await expectSearch( + { + fee: { + from: genesisTransaction.fee, + to: genesisTransaction.fee, + }, + }, + 153, + ); + }); + + it("should search transactions by the specified `vendorFieldHex`", async () => { + await expectSearch({ vendorFieldHex: genesisTransaction.vendorFieldHex }, 153); + }); + + describe("when there are more than 1 condition", () => { + it("should search transactions that includes all of them (AND)", async () => { + await expectSearch({ recipientId: genesisTransaction.recipientId, type: 3 }); + }); + }); + + describe("when no results", () => { + it("should not return them", async () => { + const transactions = await repository.search({ recipientId: "dummy" }); + expect(transactions).toBeObject(); + + expect(transactions).toHaveProperty("count", 0); + + expect(transactions.rows).toBeArray(); + expect(transactions.rows).toBeEmpty(); + }); + }); + }); +}); diff --git a/packages/core-api/__tests__/repositories/utils/build-filter-query.test.ts b/packages/core-api/__tests__/repositories/utils/build-filter-query.test.ts new file mode 100644 index 0000000000..68d10d9046 --- /dev/null +++ b/packages/core-api/__tests__/repositories/utils/build-filter-query.test.ts @@ -0,0 +1,32 @@ +import "jest-extended"; + +import { buildFilterQuery } from "../../../dist/repositories/utils/build-filter-query"; + +describe("Repository utils > buildFilterQuery", () => { + describe("`in` filter", () => { + describe("when the parameters are empty", () => { + it("should generate where conditions", () => { + const params = { a: ["a1", "a2", "a3"] }; + const query = buildFilterQuery(params, { in: [] }); + expect(query).toEqual([]); + }); + }); + + describe("when the parameters are not filterable", () => { + it("should generate where conditions", () => { + const params = { a: ["a1", "a2", "a3"] }; + const query = buildFilterQuery(params, { in: ["NOT"] }); + expect(query).toEqual([]); + }); + }); + + describe("when the parameters are filterable", () => { + it("should generate where conditions", () => { + const values = ["a1", "a2", "a3"]; + const params = { a: values }; + const query = buildFilterQuery(params, { in: ["a"] }); + expect(query).toEqual([{ column: "a", method: "in", value: values }]); + }); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/accounts.test.js b/packages/core-api/__tests__/v1/handlers/accounts.test.js deleted file mode 100644 index 50c2cbc89e..0000000000 --- a/packages/core-api/__tests__/v1/handlers/accounts.test.js +++ /dev/null @@ -1,95 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const address = 'AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo' - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Wallets', () => { - describe('GET api/accounts/getAllAccounts', () => { - it('should return all the wallets', async () => { - const response = await utils.request('GET', 'accounts/getAllAccounts') - expect(response).toBeSuccessfulResponse() - - expect(response.data.accounts).toBeArray() - }) - }) - - describe('GET api/accounts/?address', () => { - it('should return account information', async () => { - const response = await utils.request('GET', 'accounts', { address }) - expect(response).toBeSuccessfulResponse() - - utils.expectWallet(response.data.account) - }) - }) - - describe('GET api/accounts/getBalance?address', () => { - it('should return balance', async () => { - const response = await utils.request('GET', 'accounts/getBalance', { - address, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.balance).toBeString() - expect(response.data.unconfirmedBalance).toBeString() - }) - }) - - describe('GET /accounts/getPublicKey?address', () => { - it('should return public key for address', async () => { - const response = await utils.request('GET', 'accounts/getPublicKey', { - address, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.publicKey).toBeString() - }) - }) - - describe('GET api/accounts/delegates/fee', () => { - it('should return delegate fee of an account', async () => { - const response = await utils.request('GET', 'accounts/delegates/fee') - expect(response).toBeSuccessfulResponse() - - expect(response.data.fee).toBeNumber() - }) - }) - - describe('GET /accounts/delegates?address', () => { - it('should return delegate info the address has voted for', async () => { - const response = await utils.request('GET', 'accounts/delegates', { - address, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.delegates).toBeArray() - expect(response.data.delegates[0].producedblocks).toBeNumber() - }) - }) - - describe('GET api/accounts/top', () => { - it('should return the top wallets', async () => { - const response = await utils.request('GET', 'accounts/top') - expect(response).toBeSuccessfulResponse() - - expect(response.data.accounts).toBeArray() - }) - }) - - describe('GET api/accounts/count', () => { - it('should return the total number of wallets', async () => { - const response = await utils.request('GET', 'accounts/count') - expect(response).toBeSuccessfulResponse() - - expect(response.data.count).toBeNumber() - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/accounts.test.ts b/packages/core-api/__tests__/v1/handlers/accounts.test.ts new file mode 100644 index 0000000000..2643fd1b30 --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/accounts.test.ts @@ -0,0 +1,109 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const address = "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 1.0 - Wallets", () => { + describe("GET api/accounts/getAllAccounts", () => { + it("should return all the wallets", async () => { + const response = await utils.request("GET", "accounts/getAllAccounts"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.accounts).toBeArray(); + }); + }); + + describe("GET api/accounts/?address", () => { + it("should return account information", async () => { + const response = await utils.request("GET", "accounts", { address }); + expect(response).toBeSuccessfulResponse(); + + utils.expectWallet(response.data.account); + }); + + it("should not return an empty wallet", async () => { + // create a cold wallet in memory with the given senderId + const createCold = await utils.request("GET", "transactions", { + senderId: "AbhUUMJBw1dZJiZMxKBhHsdXMMafcMaPNG", + }); + expect(createCold).toBeSuccessfulResponse(); + expect(createCold.data.transactions).toBeEmpty(); + + // attempt to retrieve the cold wallet + const response = await utils.request("GET", "accounts", { address: "AbhUUMJBw1dZJiZMxKBhHsdXMMafcMaPNG" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.error).toBe("Account not found"); + }); + }); + + describe("GET api/accounts/getBalance?address", () => { + it("should return balance", async () => { + const response = await utils.request("GET", "accounts/getBalance", { + address, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.balance).toBeString(); + expect(response.data.unconfirmedBalance).toBeString(); + }); + }); + + describe("GET /accounts/getPublicKey?address", () => { + it("should return public key for address", async () => { + const response = await utils.request("GET", "accounts/getPublicKey", { + address, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.publicKey).toBeString(); + }); + }); + + describe("GET api/accounts/delegates/fee", () => { + it("should return delegate fee of an account", async () => { + const response = await utils.request("GET", "accounts/delegates/fee"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.fee).toBeNumber(); + }); + }); + + describe("GET /accounts/delegates?address", () => { + it("should return delegate info the address has voted for", async () => { + const response = await utils.request("GET", "accounts/delegates", { + address, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.delegates).toBeArray(); + expect(response.data.delegates[0].producedblocks).toBeNumber(); + }); + }); + + describe("GET api/accounts/top", () => { + it("should return the top wallets", async () => { + const response = await utils.request("GET", "accounts/top"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.accounts).toBeArray(); + }); + }); + + describe("GET api/accounts/count", () => { + it("should return the total number of wallets", async () => { + const response = await utils.request("GET", "accounts/count"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.count).toBeNumber(); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/blocks.test.js b/packages/core-api/__tests__/v1/handlers/blocks.test.js deleted file mode 100644 index 2ddb2c8c14..0000000000 --- a/packages/core-api/__tests__/v1/handlers/blocks.test.js +++ /dev/null @@ -1,131 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Blocks', () => { - describe('GET /blocks/get?id', () => { - it('should return blocks based on id', async () => { - const response = await utils.request('GET', 'blocks/get', { - id: genesisBlock.id, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.block).toBeObject() - expect(response.data.block.id).toBeString() - expect(response.data.block.height).toBeNumber() - }) - - it('should return block not found', async () => { - const response = await utils.request('GET', 'blocks/get', { - id: '18777we16674628308671', - }) - utils.expectError(response) - - expect(response.data.error).toContain('not found') - }) - }) - - describe('GET /blocks?limit=XX', () => { - it('should return 1 blocks', async () => { - const response = await utils.request('GET', 'blocks', { limit: 1 }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.blocks).toHaveLength(1) - }) - - it('should return limit error info', async () => { - const response = await utils.request('GET', 'blocks', { limit: 500 }) - utils.expectError(response) - - expect(response.data.success).toBeFalse() - expect(response.data.error).toContain('should be <= 100') - }) - }) - - describe('GET /blocks/getfees', () => { - it('should return matching fees with the config', async () => { - const response = await utils.request('GET', 'blocks/getFees') - expect(response).toBeSuccessfulResponse() - - expect(response.data.fees).toBeObject() - - expect(response.data.fees).toContainKeys([ - 'delegate', - 'secondsignature', - 'delegate', - 'vote', - 'multisignature', - ]) - }) - }) - - describe('GET /blocks/getNethash', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'blocks/getNethash') - expect(response).toBeSuccessfulResponse() - - expect(response.data.nethash).toBeString() - - const container = require('@arkecosystem/core-container') - const config = container.resolvePlugin('config') - - expect(response.data.nethash).toBe(config.network.nethash) - }) - }) - - describe('GET /blocks/getMilestone', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'blocks/getMilestone') - expect(response).toBeSuccessfulResponse() - - expect(response.data.milestone).toBeNumber() - }) - }) - - describe('GET /blocks/getReward', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'blocks/getReward') - expect(response).toBeSuccessfulResponse() - - expect(response.data.reward).toBeNumber() - }) - }) - - describe('GET /blocks/getSupply', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'blocks/getSupply') - expect(response).toBeSuccessfulResponse() - - expect(response.data.supply).toBeNumber() - }) - }) - - describe('GET /blocks/getStatus', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'blocks/getStatus') - expect(response).toBeSuccessfulResponse() - - expect(response.data.epoch).toBeString() - expect(response.data.height).toBeNumber() - expect(response.data.fee).toBeNumber() - expect(response.data.milestone).toBeNumber() - expect(response.data.nethash).toBeString() - expect(response.data.reward).toBeNumber() - expect(response.data.supply).toBeNumber() - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/blocks.test.ts b/packages/core-api/__tests__/v1/handlers/blocks.test.ts new file mode 100644 index 0000000000..c4f39662a3 --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/blocks.test.ts @@ -0,0 +1,126 @@ +import { app } from "@arkecosystem/core-container"; +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 1.0 - Blocks", () => { + describe("GET /blocks/get?id", () => { + it("should return blocks based on id", async () => { + const response = await utils.request("GET", "blocks/get", { + id: genesisBlock.id, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.block).toBeObject(); + expect(response.data.block.id).toBeString(); + expect(response.data.block.height).toBeNumber(); + }); + + it("should return block not found", async () => { + const response = await utils.request("GET", "blocks/get", { + id: "18777we16674628308671", + }); + utils.expectError(response); + + expect(response.data.error).toContain("not found"); + }); + }); + + describe("GET /blocks?limit=XX", () => { + it("should return 1 blocks", async () => { + const response = await utils.request("GET", "blocks", { limit: 1 }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.blocks).toHaveLength(1); + }); + + it("should return limit error info", async () => { + const response = await utils.request("GET", "blocks", { limit: 500 }); + utils.expectError(response); + + expect(response.data.success).toBeFalse(); + expect(response.data.error).toContain("should be <= 100"); + }); + }); + + describe("GET /blocks/getfees", () => { + it("should return matching fees with the config", async () => { + const response = await utils.request("GET", "blocks/getFees"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.fees).toBeObject(); + + expect(response.data.fees).toContainKeys([ + "delegate", + "secondsignature", + "delegate", + "vote", + "multisignature", + ]); + }); + }); + + describe("GET /blocks/getNethash", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "blocks/getNethash"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.nethash).toBeString(); + + const config = app.getConfig(); + + expect(response.data.nethash).toBe(config.get("network.nethash")); + }); + }); + + describe("GET /blocks/getMilestone", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "blocks/getMilestone"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.milestone).toBeNumber(); + }); + }); + + describe("GET /blocks/getReward", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "blocks/getReward"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.reward).toBeNumber(); + }); + }); + + describe("GET /blocks/getSupply", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "blocks/getSupply"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.supply).toBeNumber(); + }); + }); + + describe("GET /blocks/getStatus", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "blocks/getStatus"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.epoch).toBeString(); + expect(response.data.height).toBeNumber(); + expect(response.data.fee).toBeNumber(); + expect(response.data.milestone).toBeNumber(); + expect(response.data.nethash).toBeString(); + expect(response.data.reward).toBeNumber(); + expect(response.data.supply).toBeNumber(); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/delegates.test.js b/packages/core-api/__tests__/v1/handlers/delegates.test.js deleted file mode 100644 index fbad0b5817..0000000000 --- a/packages/core-api/__tests__/v1/handlers/delegates.test.js +++ /dev/null @@ -1,97 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const delegate = { - username: 'genesis_9', - publicKey: - '0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647', -} - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Delegates', () => { - describe('GET /delegates', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'delegates') - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - utils.expectDelegate(response.data.delegates[0]) - }) - }) - - describe('GET /delegates/get', () => { - it('should be ok using a username', async () => { - const response = await utils.request('GET', 'delegates/get', { - username: delegate.username, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - utils.expectDelegate(response.data.delegate, delegate) - }) - - it('should be ok using a publicKey', async () => { - const response = await utils.request('GET', 'delegates/get', { - publicKey: delegate.publicKey, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - utils.expectDelegate(response.data.delegate, delegate) - }) - }) - - describe('GET /delegates/count', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'delegates/count') - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - expect(response.data).toHaveProperty('count') - expect(response.data.count).toBeNumber() - }) - }) - - describe('GET /delegates/search', () => { - it('should be ok searching a username', async () => { - const response = await utils.request('GET', 'delegates/search', { - q: delegate.username, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - utils.expectDelegate(response.data.delegates[0], delegate) - }) - }) - - describe('GET /delegates/voters', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'delegates/voters', { - publicKey: delegate.publicKey, - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - utils.expectWallet(response.data.accounts[0]) - }) - }) - - describe('GET /delegates/fee', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'delegates/fee') - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - expect(response.data).toHaveProperty('fee') - expect(response.data.fee).toBeNumber() - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/delegates.test.ts b/packages/core-api/__tests__/v1/handlers/delegates.test.ts new file mode 100644 index 0000000000..6e6a915a9f --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/delegates.test.ts @@ -0,0 +1,96 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const delegate = { + username: "genesis_9", + publicKey: "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", +}; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 1.0 - Delegates", () => { + describe("GET /delegates", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "delegates"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + utils.expectDelegate(response.data.delegates[0]); + }); + }); + + describe("GET /delegates/get", () => { + it("should be ok using a username", async () => { + const response = await utils.request("GET", "delegates/get", { + username: delegate.username, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + utils.expectDelegate(response.data.delegate, delegate); + }); + + it("should be ok using a publicKey", async () => { + const response = await utils.request("GET", "delegates/get", { + publicKey: delegate.publicKey, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + utils.expectDelegate(response.data.delegate, delegate); + }); + }); + + describe("GET /delegates/count", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "delegates/count"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + expect(response.data).toHaveProperty("count"); + expect(response.data.count).toBeNumber(); + }); + }); + + describe("GET /delegates/search", () => { + it("should be ok searching a username", async () => { + const response = await utils.request("GET", "delegates/search", { + q: delegate.username, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + utils.expectDelegate(response.data.delegates[0], delegate); + }); + }); + + describe("GET /delegates/voters", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "delegates/voters", { + publicKey: delegate.publicKey, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + utils.expectWallet(response.data.accounts[0]); + }); + }); + + describe("GET /delegates/fee", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "delegates/fee"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + expect(response.data).toHaveProperty("fee"); + expect(response.data.fee).toBeNumber(); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/loader.test.js b/packages/core-api/__tests__/v1/handlers/loader.test.js deleted file mode 100644 index f7f9f36bfc..0000000000 --- a/packages/core-api/__tests__/v1/handlers/loader.test.js +++ /dev/null @@ -1,53 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Loader', () => { - describe('GET /loader/status', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'loader/status') - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - expect(response.data).toHaveProperty('loaded') - expect(response.data).toHaveProperty('now') - expect(response.data).toHaveProperty('blocksCount') - }) - }) - - describe('GET /loader/status/sync', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'loader/status/sync') - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - expect(response.data).toHaveProperty('syncing') - expect(response.data).toHaveProperty('blocks') - expect(response.data).toHaveProperty('height') - expect(response.data).toHaveProperty('id') - }) - }) - - describe('GET /loader/autoconfigure', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'loader/autoconfigure') - expect(response).toBeSuccessfulResponse() - - expect(response.data).toBeObject() - expect(response.data.network).toBeObject() - expect(response.data.network).toHaveProperty('nethash') - expect(response.data.network).toHaveProperty('token') - expect(response.data.network).toHaveProperty('symbol') - expect(response.data.network).toHaveProperty('explorer') - expect(response.data.network).toHaveProperty('version') - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/loader.test.ts b/packages/core-api/__tests__/v1/handlers/loader.test.ts new file mode 100644 index 0000000000..f8f79f765b --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/loader.test.ts @@ -0,0 +1,53 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 1.0 - Loader", () => { + describe("GET /loader/status", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "loader/status"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + expect(response.data).toHaveProperty("loaded"); + expect(response.data).toHaveProperty("now"); + expect(response.data).toHaveProperty("blocksCount"); + }); + }); + + describe("GET /loader/status/sync", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "loader/status/sync"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + expect(response.data).toHaveProperty("syncing"); + expect(response.data).toHaveProperty("blocks"); + expect(response.data).toHaveProperty("height"); + expect(response.data).toHaveProperty("id"); + }); + }); + + describe("GET /loader/autoconfigure", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "loader/autoconfigure"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data).toBeObject(); + expect(response.data.network).toBeObject(); + expect(response.data.network).toHaveProperty("nethash"); + expect(response.data.network).toHaveProperty("token"); + expect(response.data.network).toHaveProperty("symbol"); + expect(response.data.network).toHaveProperty("explorer"); + expect(response.data.network).toHaveProperty("version"); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/peers.test.js b/packages/core-api/__tests__/v1/handlers/peers.test.js deleted file mode 100644 index 45e8a8d4ef..0000000000 --- a/packages/core-api/__tests__/v1/handlers/peers.test.js +++ /dev/null @@ -1,92 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const peerIp = '167.114.29.55' -const peerPort = '4002' - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Peers', () => { - describe('GET /peers/version', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'peers/version') - expect(response).toBeSuccessfulResponse() - - expect(response.data.version).toBeString() - }) - }) - - describe('GET /peers', () => { - // NOTE Seems that ark-node replies successfully - // it('should fail using empty parameters', async () => { - // const response = await utils.request('GET', 'peers', { - // state: null, - // os: null, - // shared: null, - // version: null, - // limit: null, - // offset: null, - // orderBy: null - // }) - // debugger - // utils.expectError(response) - // - // expect(response.data.error).toContain('should be string') - // }) - - it('should fail using limit > 100', async () => { - const response = await utils.request('GET', 'peers', { limit: 101 }) - utils.expectError(response) - }) - - it('should fail using invalid parameters', async () => { - const response = await utils.request('GET', 'peers', { - state: 'invalid', - os: 'invalid', - shared: 'invalid', - version: 'invalid', - limit: 'invalid', - offset: 'invalid', - orderBy: 'invalid', - }) - utils.expectError(response) - - expect(response.data.error).not.toBeNull() - }) - }) - - describe('GET /peers/get', () => { - it('should fail using known ip address with no port', async () => { - const response = await utils.request('GET', 'peers/get', { - ip: '127.0.0.1', - }) - utils.expectError(response) - - expect(response.data.error).toBe("should have required property 'port'") - }) - - it('should fail using valid port with no ip address', async () => { - const response = await utils.request('GET', 'peers/get', { port: 4002 }) - utils.expectError(response) - - expect(response.data.error).toBe("should have required property 'ip'") - }) - - it('should fail using unknown ip address and port', async () => { - const response = await utils.request('GET', 'peers/get', { - ip: '99.99.99.99', - port: peerPort, - }) - utils.expectError(response) - - expect(response.data.error).toBe(`Peer 99.99.99.99:${peerPort} not found`) - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/peers.test.ts b/packages/core-api/__tests__/v1/handlers/peers.test.ts new file mode 100644 index 0000000000..4df8286e72 --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/peers.test.ts @@ -0,0 +1,104 @@ +import { app } from "@arkecosystem/core-container"; +import { Peer } from "@arkecosystem/core-p2p/src/peer"; +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const mockAddress = "1.0.0.99"; +const mockPort = 4002; + +beforeAll(async () => { + await setUp(); + + const peerMock = new Peer(mockAddress, mockPort); + peerMock.setStatus("OK"); + + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + monitor.peers[peerMock.ip] = peerMock; +}); + +afterAll(async () => { + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + + await tearDown(); +}); + +describe("API 1.0 - Peers", () => { + describe("GET /peers", () => { + it("should pass using valid parameters", async () => { + const response = await utils.request("GET", "peers", { limit: 50 }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.error).toBeUndefined(); + }); + + it("should fail using limit > 100", async () => { + const response = await utils.request("GET", "peers", { limit: 101 }); + utils.expectError(response); + }); + + it("should fail using invalid parameters", async () => { + const response = await utils.request("GET", "peers", { + state: "invalid", + os: "invalid", + shared: "invalid", + version: "invalid", + limit: "invalid", + offset: "invalid", + orderBy: "invalid", + }); + utils.expectError(response); + + expect(response.data.error).not.toBeNull(); + }); + }); + + describe("GET /peers/get", () => { + it("should pass using valid data", async () => { + const response = await utils.request("GET", "peers/get", { + ip: mockAddress, + port: mockPort, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data).toBeObject(); + expect(response.data.peer.ip).toBe(mockAddress); + expect(response.data.peer.port).toBe(mockPort); + }); + + it("should fail using known ip address with no port", async () => { + const response = await utils.request("GET", "peers/get", { + ip: "127.0.0.1", + }); + utils.expectError(response); + + expect(response.data.error).toBe("should have required property 'port'"); + }); + + it("should fail using valid port with no ip address", async () => { + const response = await utils.request("GET", "peers/get", { port: 4002 }); + utils.expectError(response); + + expect(response.data.error).toBe("should have required property 'ip'"); + }); + + it("should fail using unknown ip address and port", async () => { + const response = await utils.request("GET", "peers/get", { + ip: "99.99.99.99", + port: mockPort, + }); + utils.expectError(response); + + expect(response.data.error).toBe(`Peer 99.99.99.99:${mockPort} not found`); + }); + }); + + describe("GET /peers/version", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "peers/version"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.version).toBeString(); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/signatures.test.js b/packages/core-api/__tests__/v1/handlers/signatures.test.js deleted file mode 100644 index 30fe41c2b9..0000000000 --- a/packages/core-api/__tests__/v1/handlers/signatures.test.js +++ /dev/null @@ -1,22 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Signatures', () => { - describe('GET /signatures/fee', () => { - it('should return second signature value from config', async () => { - const response = await utils.request('GET', 'signatures/fee') - expect(response).toBeSuccessfulResponse() - - expect(response.data.fee).toBeNumber() - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/signatures.test.ts b/packages/core-api/__tests__/v1/handlers/signatures.test.ts new file mode 100644 index 0000000000..4e9b1fd137 --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/signatures.test.ts @@ -0,0 +1,22 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 1.0 - Signatures", () => { + describe("GET /signatures/fee", () => { + it("should return second signature value from config", async () => { + const response = await utils.request("GET", "signatures/fee"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.fee).toBeNumber(); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/handlers/transactions.test.js b/packages/core-api/__tests__/v1/handlers/transactions.test.js deleted file mode 100644 index c4486e6040..0000000000 --- a/packages/core-api/__tests__/v1/handlers/transactions.test.js +++ /dev/null @@ -1,293 +0,0 @@ -/* eslint max-len: "off" */ - -require('@arkecosystem/core-test-utils/lib/matchers') - -const app = require('../../__support__/setup') -const utils = require('../utils') - -const address1 = 'APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn' -const address2 = 'AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri' - -let genesisBlock -let transactionList - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') - transactionList = genesisBlock.transactions -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 1.0 - Transactions', () => { - describe('GET /transactions', () => { - it('should be ok using valid parameters', async () => { - const data = { - blockId: '17184958558311101492', - senderId: address1, - recipientId: address2, - limit: 10, - offset: 0, - orderBy: 'amount:asc', - } - - const response = await utils.request('GET', 'transactions', data) - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - expect(response.data.transactions).not.toBeEmpty() - - response.data.transactions.forEach(transaction => { - expect(transaction).toBeApiTransaction() - }) - }) - - it('should reply with transactions that have any of the values (OR)', async () => { - const data = { - senderId: address1, - recipientId: address2, - } - - const response = await utils.request('GET', 'transactions', data) - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - expect(response.data.transactions).not.toBeEmpty() - - response.data.transactions.forEach(transaction => { - expect(transaction).toBeApiTransaction() - if (transaction.senderId === data.senderId) { - expect(transaction.senderId).toBe(data.senderId) - } else { - expect(transaction.recipientId).toBe(data.recipientId) - } - }) - }) - - it('should be ok filtering by type', async () => { - const type = 3 - - const response = await utils.request('GET', 'transactions', { type }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - expect(response.data.transactions).not.toBeEmpty() - - response.data.transactions.forEach(transaction => { - expect(transaction).toBeApiTransaction() - expect(transaction.type).toBe(type) - }) - }) - - it('should be ok using no params', async () => { - const response = await utils.request('GET', 'transactions') - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - expect(response.data.transactions).not.toBeEmpty() - - response.data.transactions.forEach(transaction => { - expect(transaction).toBeApiTransaction() - }) - }) - - // fixquery - // http://localhost:4003/api/transactions?orderBy=timestamp:desc&offset=0&limit=50&recipientId=ANwZGjK55pe4xSWfnggt324S9XKY3TSwAr&senderId=ANwZGjK55pe4xSWfnggt324S9XKY3TSwAr - - it('should fail using limit > 100', async () => { - const limit = 101 - - const response = await utils.request('GET', 'transactions', { limit }) - utils.expectError(response) - - expect(response.data.error).toBeString() - }) - - it('should be ok ordered by ascending timestamp', async () => { - const response = await utils.request('GET', 'transactions', { - orderBy: 'timestamp:asc', - }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - expect(response.data.transactions).not.toBeEmpty() - - response.data.transactions.forEach(transaction => { - expect(transaction).toBeApiTransaction() - }) - - let flag = 0 - for (let i = 0; i < response.data.transactions.length; i++) { - if (response.data.transactions[i + 1]) { - // await response.data.transactions[i].toHaveProperty('timestamp').which.is.at.most(response.data.transactions[i + 1].timestamp) - expect(response.data.transactions[i]).toHaveProperty('timestamp') - - if (flag === 0) { - // offsetTimestamp = response.data.transactions[i + 1].timestamp - flag = 1 - } - } - } - }) - - it('should be ok using offset == 1', async () => { - const response = await utils.request('GET', 'transactions', { offset: 1 }) - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - expect(response.data.transactions).not.toBeEmpty() - - response.data.transactions.forEach(transaction => { - expect(transaction).toBeApiTransaction() - }) - }) - - it('should fail using offset == "one"', async () => { - const response = await utils.request('GET', 'transactions', { - offset: 'one', - }) - utils.expectError(response) - - expect(response.data.error).toBeString() - }) - - it('should fail using completely invalid fields', async () => { - const response = await utils.request('GET', 'transactions', { - blockId: 'invalid', - senderId: 'invalid', - recipientId: 'invalid', - limit: 'invalid', - offset: 'invalid', - orderBy: 'invalid', - }) - utils.expectError(response) - - expect(response.data.error).toBeString() - }) - - it('should fail using partially invalid fields', async () => { - const response = await utils.request('GET', 'transactions', { - blockId: 'invalid', - senderId: 'invalid', - recipientId: address1, - limit: 'invalid', - offset: 'invalid', - orderBy: 'invalid', - }) - utils.expectError(response) - - expect(response.data.error).toBeString() - }) - }) - - describe('GET /transactions/get?id=', () => { - it('should be ok using valid id', async () => { - const transactionInCheck = transactionList[0] - const response = await utils.request('GET', 'transactions/get', { - id: transactionInCheck.id, - }) - - expect(response).toBeSuccessfulResponse() - - expect(response.data.transaction).toBeApiTransaction() - - expect(response.data.transaction).toHaveProperty( - 'id', - transactionInCheck.id, - ) - expect(response.data.transaction).toHaveProperty( - 'amount', - transactionInCheck.amount, - ) - expect(response.data.transaction).toHaveProperty( - 'fee', - transactionInCheck.fee, - ) - expect(response.data.transaction).toHaveProperty( - 'recipientId', - transactionInCheck.recipientId, - ) - expect(response.data.transaction).toHaveProperty( - 'senderId', - transactionInCheck.senderId, - ) - expect(response.data.transaction).toHaveProperty( - 'type', - transactionInCheck.type, - ) - }) - - it('should fail using invalid id', async () => { - const response = await utils.request('GET', 'transactions/get', { - id: 'invalid', - }) - - utils.expectError(response) - - expect(response.data.error).toBeString() - }) - }) - - describe('GET /transactions/unconfirmed/get?id=', () => { - it('should be ok using valid id', async () => { - const transaction = await utils.createTransaction() - - const response = await utils.request( - 'GET', - 'transactions/unconfirmed/get', - { id: transaction.id }, - ) - expect(response).toBeSuccessfulResponse() - - if (response.data.success && response.data.transaction !== null) { - expect(response.data.transaction).toBeObject() - expect(response.data.transaction).toHaveProperty('id', transaction.id) - expect(response.data.transaction).toHaveProperty( - 'type', - transaction.type, - ) - expect(response.data.transaction).toHaveProperty( - 'amount', - transaction.amount, - ) - expect(response.data.transaction).toHaveProperty('fee', transaction.fee) - expect(response.data.transaction).toHaveProperty( - 'recipientId', - transaction.recipientId, - ) - expect(response.data.transaction).toHaveProperty( - 'senderPublicKey', - transaction.senderPublicKey, - ) - expect(response.data.transaction).toHaveProperty( - 'signature', - transaction.signature, - ) - expect(response.data.transaction).toHaveProperty( - 'timestamp', - transaction.timestamp, - ) - expect(response.data.transaction).toHaveProperty( - 'vendorField', - transaction.vendorField, - ) - } else { - expect(response.data.error).toBeString() - } - }) - }) - - describe('GET /transactions/unconfirmed', () => { - it('should be ok', async () => { - const response = await utils.request('GET', 'transactions/unconfirmed') - expect(response).toBeSuccessfulResponse() - - expect(response.data.transactions).toBeArray() - }) - }) -}) diff --git a/packages/core-api/__tests__/v1/handlers/transactions.test.ts b/packages/core-api/__tests__/v1/handlers/transactions.test.ts new file mode 100644 index 0000000000..795f11a5be --- /dev/null +++ b/packages/core-api/__tests__/v1/handlers/transactions.test.ts @@ -0,0 +1,246 @@ +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const address1 = "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn"; +const address2 = "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri"; + +let transactionList; + +beforeAll(async () => { + await setUp(); + + transactionList = genesisBlock.transactions; +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 1.0 - Transactions", () => { + describe("GET /transactions", () => { + it("should be ok using valid parameters", async () => { + const data = { + blockId: "17184958558311101492", + senderId: address1, + recipientId: address2, + limit: 10, + offset: 0, + orderBy: "amount:asc", + }; + + const response = await utils.request("GET", "transactions", data); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + expect(response.data.transactions).not.toBeEmpty(); + + response.data.transactions.forEach(transaction => { + expect(transaction).toBeApiTransaction(); + }); + }); + + it("should reply with transactions that have any of the values (OR)", async () => { + const data = { + senderId: address1, + recipientId: address2, + }; + + const response = await utils.request("GET", "transactions", data); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + expect(response.data.transactions).not.toBeEmpty(); + + response.data.transactions.forEach(transaction => { + expect(transaction).toBeApiTransaction(); + if (transaction.senderId === data.senderId) { + expect(transaction.senderId).toBe(data.senderId); + } else { + expect(transaction.recipientId).toBe(data.recipientId); + } + }); + }); + + it("should be ok filtering by type", async () => { + const type = 3; + + const response = await utils.request("GET", "transactions", { type }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + expect(response.data.transactions).not.toBeEmpty(); + + response.data.transactions.forEach(transaction => { + expect(transaction).toBeApiTransaction(); + expect(transaction.type).toBe(type); + }); + }); + + it("should be ok using no params", async () => { + const response = await utils.request("GET", "transactions"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + expect(response.data.transactions).not.toBeEmpty(); + + response.data.transactions.forEach(transaction => { + expect(transaction).toBeApiTransaction(); + }); + }); + + // fixquery + // http://localhost:4003/api/transactions?orderBy=timestamp:desc&offset=0&limit=50&recipientId=ANwZGjK55pe4xSWfnggt324S9XKY3TSwAr&senderId=ANwZGjK55pe4xSWfnggt324S9XKY3TSwAr + + it("should fail using limit > 100", async () => { + const limit = 101; + + const response = await utils.request("GET", "transactions", { limit }); + utils.expectError(response); + + expect(response.data.error).toBeString(); + }); + + it("should be ok ordered by ascending timestamp", async () => { + const response = await utils.request("GET", "transactions", { + orderBy: "timestamp:asc", + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + expect(response.data.transactions).not.toBeEmpty(); + + response.data.transactions.forEach(transaction => { + expect(transaction).toBeApiTransaction(); + }); + + let flag = 0; + for (let i = 0; i < response.data.transactions.length; i++) { + if (response.data.transactions[i + 1]) { + // await response.data.transactions[i].toHaveProperty('timestamp').which.is.at.most(response.data.transactions[i + 1].timestamp) + expect(response.data.transactions[i]).toHaveProperty("timestamp"); + + if (flag === 0) { + // offsetTimestamp = response.data.transactions[i + 1].timestamp + flag = 1; + } + } + } + }); + + it("should be ok using offset == 1", async () => { + const response = await utils.request("GET", "transactions", { + offset: 1, + }); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + expect(response.data.transactions).not.toBeEmpty(); + + response.data.transactions.forEach(transaction => { + expect(transaction).toBeApiTransaction(); + }); + }); + + it('should fail using offset == "one"', async () => { + const response = await utils.request("GET", "transactions", { + offset: "one", + }); + utils.expectError(response); + + expect(response.data.error).toBeString(); + }); + + it("should fail using completely invalid fields", async () => { + const response = await utils.request("GET", "transactions", { + blockId: "invalid", + senderId: "invalid", + recipientId: "invalid", + limit: "invalid", + offset: "invalid", + orderBy: "invalid", + }); + utils.expectError(response); + + expect(response.data.error).toBeString(); + }); + + it("should fail using partially invalid fields", async () => { + const response = await utils.request("GET", "transactions", { + blockId: "invalid", + senderId: "invalid", + recipientId: address1, + limit: "invalid", + offset: "invalid", + orderBy: "invalid", + }); + utils.expectError(response); + + expect(response.data.error).toBeString(); + }); + }); + + describe("GET /transactions/get?id=", () => { + it("should be ok using valid id", async () => { + const transactionInCheck = transactionList[0]; + const response = await utils.request("GET", "transactions/get", { + id: transactionInCheck.id, + }); + + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transaction).toBeApiTransaction(); + + expect(response.data.transaction).toHaveProperty("id", transactionInCheck.id); + expect(response.data.transaction).toHaveProperty("amount", transactionInCheck.amount); + expect(response.data.transaction).toHaveProperty("fee", transactionInCheck.fee); + expect(response.data.transaction).toHaveProperty("recipientId", transactionInCheck.recipientId); + expect(response.data.transaction).toHaveProperty("senderId", transactionInCheck.senderId); + expect(response.data.transaction).toHaveProperty("type", transactionInCheck.type); + }); + + it("should fail using invalid id", async () => { + const response = await utils.request("GET", "transactions/get", { + id: "invalid", + }); + + utils.expectError(response); + + expect(response.data.error).toBeString(); + }); + }); + + describe("GET /transactions/unconfirmed/get?id=", () => { + it("should be ok using valid id", async () => { + const transaction = await utils.createTransaction(); + + const response = await utils.request("GET", "transactions/unconfirmed/get", { id: transaction.id }); + expect(response).toBeSuccessfulResponse(); + + if (response.data.success && response.data.transaction !== null) { + expect(response.data.transaction).toBeObject(); + expect(response.data.transaction).toHaveProperty("id", transaction.id); + expect(response.data.transaction).toHaveProperty("type", transaction.type); + expect(response.data.transaction).toHaveProperty("amount", transaction.amount); + expect(response.data.transaction).toHaveProperty("fee", transaction.fee); + expect(response.data.transaction).toHaveProperty("recipientId", transaction.recipientId); + expect(response.data.transaction).toHaveProperty("senderPublicKey", transaction.senderPublicKey); + expect(response.data.transaction).toHaveProperty("signature", transaction.signature); + expect(response.data.transaction).toHaveProperty("timestamp", transaction.timestamp); + expect(response.data.transaction).toHaveProperty("vendorField", transaction.vendorField); + } else { + expect(response.data.error).toBeString(); + } + }); + }); + + describe("GET /transactions/unconfirmed", () => { + it("should be ok", async () => { + const response = await utils.request("GET", "transactions/unconfirmed"); + expect(response).toBeSuccessfulResponse(); + + expect(response.data.transactions).toBeArray(); + }); + }); +}); diff --git a/packages/core-api/__tests__/v1/utils.js b/packages/core-api/__tests__/v1/utils.js deleted file mode 100644 index d27edb7d6f..0000000000 --- a/packages/core-api/__tests__/v1/utils.js +++ /dev/null @@ -1,107 +0,0 @@ -const axios = require('axios') -const { - client, - transactionBuilder, - NetworkManager, -} = require('@arkecosystem/crypto') -const apiHelpers = require('@arkecosystem/core-test-utils/lib/helpers/api') - -class Helpers { - async request(method, path, params = {}) { - const url = `http://localhost:4003/api/${path}` - const headers = { - 'API-Version': 1, - 'Content-Type': 'application/json', - } - - const server = require('@arkecosystem/core-container').resolvePlugin('api') - - return apiHelpers.request(server.http, method, url, headers, params) - } - - expectJson(response) { - expect(response.data).toBeObject() - } - - expectStatus(response, code) { - expect(response.status).toBe(code) - } - - assertVersion(response, version) { - expect(response.headers).toBeObject() - expect(response.headers).toHaveProperty('api-version', version) - } - - expectState(response, state) { - expect(response.data).toHaveProperty('success', state) - } - - expectSuccessful(response) { - this.expectStatus(response, 200) - this.expectJson(response) - this.expectState(response, true) - this.assertVersion(response, 1) - } - - expectError(response) { - this.expectStatus(response, 200) - this.expectJson(response) - this.expectState(response, false) - this.assertVersion(response, 1) - } - - expectDelegate(delegate, expected) { - expect(delegate).toBeObject() - expect(delegate.username).toBeString() - expect(delegate.address).toBeString() - expect(delegate.publicKey).toBeString() - expect(delegate.vote).toBeString() - expect(delegate.rate).toBeNumber() - expect(delegate.missedblocks).toBeNumber() - expect(delegate.producedblocks).toBeNumber() - expect(delegate.approval).toBeNumber() - expect(delegate.productivity).toBeNumber() - - Object.keys(expected || {}).forEach(attr => { - expect(delegate[attr]).toBe(expected[attr]) - }) - } - - expectWallet(response) { - expect(response).toHaveProperty('username') - expect(response).toHaveProperty('address') - expect(response).toHaveProperty('publicKey') - expect(response).toHaveProperty('balance') - } - - async createTransaction() { - client.setConfig(NetworkManager.findByName('testnet')) - - const transaction = transactionBuilder - .transfer() - .amount(1 * 1e8) - .recipientId('AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1') - .vendorField('test') - .sign( - 'prison tobacco acquire stone dignity palace note decade they current lesson robot', - ) - .getStruct() - - await axios.post( - 'http://127.0.0.1:4003/api/v2/transactions', - { - transactions: [transaction], - }, - { - headers: { 'Content-Type': 'application/json' }, - }, - ) - - return transaction - } -} - -/** - * @type {Helpers} - */ -module.exports = new Helpers() diff --git a/packages/core-api/__tests__/v1/utils.ts b/packages/core-api/__tests__/v1/utils.ts new file mode 100644 index 0000000000..aa7511b5fa --- /dev/null +++ b/packages/core-api/__tests__/v1/utils.ts @@ -0,0 +1,100 @@ +import { app } from "@arkecosystem/core-container"; +import { ApiHelpers } from "@arkecosystem/core-test-utils/dist/helpers/api"; +import { client, NetworkManager, transactionBuilder } from "@arkecosystem/crypto"; +import axios from "axios"; +import "jest-extended"; + +class Helpers { + public async request(method, path, params = {}) { + const url = `http://localhost:4003/api/${path}`; + const headers = { + "API-Version": 1, + "Content-Type": "application/json", + }; + + const server = app.resolvePlugin("api"); + + return ApiHelpers.request(server.http, method, url, headers, params); + } + + public expectJson(response) { + expect(response.data).toBeObject(); + } + + public expectStatus(response, code) { + expect(response.status).toBe(code); + } + + public assertVersion(response, version) { + expect(response.headers).toBeObject(); + expect(response.headers).toHaveProperty("api-version", version); + } + + public expectState(response, state) { + expect(response.data).toHaveProperty("success", state); + } + + public expectSuccessful(response) { + this.expectStatus(response, 200); + this.expectJson(response); + this.expectState(response, true); + this.assertVersion(response, 1); + } + + public expectError(response) { + this.expectStatus(response, 200); + this.expectJson(response); + this.expectState(response, false); + this.assertVersion(response, 1); + } + + public expectDelegate(delegate, expected: any = {}) { + expect(delegate).toBeObject(); + expect(delegate.username).toBeString(); + expect(delegate.address).toBeString(); + expect(delegate.publicKey).toBeString(); + expect(delegate.vote).toBeString(); + expect(delegate.rate).toBeNumber(); + expect(delegate.missedblocks).toBeNumber(); + expect(delegate.producedblocks).toBeNumber(); + expect(delegate.approval).toBeNumber(); + expect(delegate.productivity).toBeNumber(); + + Object.keys(expected || {}).forEach(attr => { + expect(delegate[attr]).toBe(expected[attr]); + }); + } + + public expectWallet(response) { + expect(response).toHaveProperty("username"); + expect(response).toHaveProperty("address"); + expect(response).toHaveProperty("publicKey"); + expect(response).toHaveProperty("balance"); + } + + public async createTransaction() { + client.setConfig(NetworkManager.findByName("testnet")); + + const transaction = transactionBuilder + .transfer() + .amount(1 * 1e8) + .recipientId("AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1") + .vendorField("test") + .sign("prison tobacco acquire stone dignity palace note decade they current lesson robot") + .getStruct(); + + await axios.post( + "http://127.0.0.1:4003/api/v2/transactions", + { + transactions: [transaction], + }, + { + headers: { "Content-Type": "application/json" }, + }, + ); + + return transaction; + } +} + +export const utils = new Helpers(); diff --git a/packages/core-api/__tests__/v2/handlers/blocks.test.js b/packages/core-api/__tests__/v2/handlers/blocks.test.js deleted file mode 100644 index 569a708527..0000000000 --- a/packages/core-api/__tests__/v2/handlers/blocks.test.js +++ /dev/null @@ -1,579 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const blockchainHelper = require('@arkecosystem/core-test-utils/lib/helpers/blockchain') -const { Block } = require('@arkecosystem/crypto').models -const blocks2to100 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.2-100') -const app = require('../../__support__/setup') -const utils = require('../utils') - -let genesisBlock -let container - -beforeAll(async () => { - await app.setUp() - await blockchainHelper.resetBlockchain() - container = require('@arkecosystem/core-container') - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Blocks', () => { - describe('GET /blocks', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the "%s" header', (header, request) => { - it('should GET all the blocks', async () => { - const response = await utils[request]('GET', 'blocks') - - expect(response).toBeSuccessfulResponse() - expect(response).toBePaginated() - expect(response.data.data).toBeArray() - - const block = response.data.data[0] - utils.expectBlock(block, { - id: genesisBlock.id, - transactions: genesisBlock.numberOfTransactions, - }) - }) - }) - }) - - describe('GET /blocks?orderBy=height:', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the "%s" header', (header, request) => { - it('should GET all the blocks in descending order', async () => { - const response = await utils[request]('GET', 'blocks?orderBy=height:') - - expect(response).toBeSuccessfulResponse() - expect(response).toBePaginated() - expect(response.data.data).toBeArray() - - const block = response.data.data[0] - utils.expectBlock(block) - }) - }) - }) - - describe('GET /blocks/:id', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a block by the given identifier', async () => { - const response = await utils[request]( - 'GET', - `blocks/${genesisBlock.id}`, - ) - - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - const block = response.data.data - utils.expectBlock(block, { - id: genesisBlock.id, - transactions: genesisBlock.numberOfTransactions, - }) - }) - }) - }) - - describe('GET /blocks/:id/transactions', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the "%s" header', (header, request) => { - it('should GET all the transactions for the given block by id', async () => { - const response = await utils[request]( - 'GET', - `blocks/${genesisBlock.id}/transactions`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - const transaction = response.data.data[0] - utils.expectTransaction(transaction) - expect(transaction.blockId).toBe(genesisBlock.id) - }) - }) - }) - - describe('POST /blocks/search', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified blockId', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified version', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - version: genesisBlock.version, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.version).toBe(genesisBlock.version) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified previousBlock', async () => { - // save a new block so that we can make the request with previousBlock - const block2 = new Block(blocks2to100[0]) - const database = container.resolvePlugin('database') - await database.saveBlock(block2) - - const response = await utils[request]('POST', 'blocks/search', { - id: blocks2to100[0].id, - previousBlock: blocks2to100[0].previousBlock, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(blocks2to100[0].id) - expect(block.previous).toBe(blocks2to100[0].previousBlock) - - await database.deleteBlock(block2) // reset to genesis block - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified payloadHash', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - payloadHash: genesisBlock.payloadHash, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.payload.length).toBe(genesisBlock.payloadLength) - expect(block.payload.hash).toBe(genesisBlock.payloadHash) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified generatorPublicKey', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - generatorPublicKey: genesisBlock.generatorPublicKey, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.generator.publicKey).toBe(genesisBlock.generatorPublicKey) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified blockSignature', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - blockSignature: genesisBlock.blockSignature, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.signature).toBe(genesisBlock.blockSignature) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified timestamp', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - timestamp: { - from: genesisBlock.timestamp, - to: genesisBlock.timestamp, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified height', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - height: { - from: genesisBlock.height, - to: genesisBlock.height, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.height).toBe(genesisBlock.height) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specified height range', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - height: { - from: genesisBlock.height, - to: genesisBlock.height, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.height).toBe(genesisBlock.height) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified numberOfTransactions', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - numberOfTransactions: { - from: genesisBlock.numberOfTransactions, - to: genesisBlock.numberOfTransactions, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.transactions).toBe(genesisBlock.numberOfTransactions) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specified numberOfTransactions range', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - numberOfTransactions: { - from: genesisBlock.numberOfTransactions, - to: genesisBlock.numberOfTransactions, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.transactions).toBe(genesisBlock.numberOfTransactions) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified totalAmount', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - totalAmount: { from: 1 }, - }) - - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specified totalAmount range', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - totalAmount: { from: 1 }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified totalFee', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - totalFee: { from: 0 }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(+block.forged.fee).toBe(genesisBlock.totalFee) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specified totalFee range', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - totalFee: { from: 0 }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(+block.forged.fee).toBe(genesisBlock.totalFee) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified reward', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - reward: { - from: genesisBlock.reward, - to: genesisBlock.reward, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(+block.forged.reward).toBe(genesisBlock.reward) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specified reward range', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - reward: { - from: genesisBlock.reward, - to: genesisBlock.reward, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(+block.forged.reward).toBe(genesisBlock.reward) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the exact specified payloadLength', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - payloadLength: { - from: genesisBlock.payloadLength, - to: genesisBlock.payloadLength, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.payload.length).toBe(genesisBlock.payloadLength) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specified payloadLength range', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - payloadLength: { - from: genesisBlock.payloadLength, - to: genesisBlock.payloadLength, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - expect(block.payload.length).toBe(genesisBlock.payloadLength) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the wrong specified version', async () => { - const response = await utils[request]('POST', 'blocks/search', { - id: genesisBlock.id, - version: 2, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(0) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for blocks with the specific criteria', async () => { - const response = await utils[request]('POST', 'blocks/search', { - generatorPublicKey: genesisBlock.generatorPublicKey, - version: genesisBlock.version, - timestamp: { - from: genesisBlock.timestamp, - to: genesisBlock.timestamp, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const block = response.data.data[0] - utils.expectBlock(block) - expect(block.id).toBe(genesisBlock.id) - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/blocks.test.ts b/packages/core-api/__tests__/v2/handlers/blocks.test.ts new file mode 100644 index 0000000000..9d92841bbd --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/blocks.test.ts @@ -0,0 +1,573 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +import { models } from "@arkecosystem/crypto"; +import genesisBlock from "../../../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { blocks2to100 } from "../../../../core-test-utils/src/fixtures"; +import { resetBlockchain } from "../../../../core-test-utils/src/helpers"; + +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; + +const container = app; +const { Block } = models; + +beforeAll(async () => { + await setUp(); + await resetBlockchain(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 2.0 - Blocks", () => { + describe("GET /blocks", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + 'using the "%s" header', + (header, request) => { + it("should GET all the blocks", async () => { + const response = await utils[request]("GET", "blocks"); + + expect(response).toBeSuccessfulResponse(); + expect(response).toBePaginated(); + expect(response.data.data).toBeArray(); + + const block = response.data.data[0]; + utils.expectBlock(block, { + id: genesisBlock.id, + transactions: genesisBlock.numberOfTransactions, + }); + }); + }, + ); + }); + + describe("GET /blocks?orderBy=height:", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + 'using the "%s" header', + (header, request) => { + it("should GET all the blocks in descending order", async () => { + const response = await utils[request]("GET", "blocks?orderBy=height:"); + + expect(response).toBeSuccessfulResponse(); + expect(response).toBePaginated(); + expect(response.data.data).toBeArray(); + + const block = response.data.data[0]; + utils.expectBlock(block); + }); + }, + ); + }); + + describe("GET /blocks/:id", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a block by the given identifier", async () => { + const response = await utils[request]("GET", `blocks/${genesisBlock.id}`); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + const block = response.data.data; + utils.expectBlock(block, { + id: genesisBlock.id, + transactions: genesisBlock.numberOfTransactions, + }); + }); + }, + ); + }); + + describe("GET /blocks/:id/transactions", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + 'using the "%s" header', + (header, request) => { + it("should GET all the transactions for the given block by id", async () => { + const response = await utils[request]("GET", `blocks/${genesisBlock.id}/transactions`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + const transaction = response.data.data[0]; + utils.expectTransaction(transaction); + expect(transaction.blockId).toBe(genesisBlock.id); + }); + }, + ); + }); + + describe("POST /blocks/search", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified blockId", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified version", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + version: genesisBlock.version, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.version).toBe(genesisBlock.version); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified previousBlock", async () => { + // save a new block so that we can make the request with previousBlock + const block2 = new Block(blocks2to100[0]); + const databaseService = container.resolvePlugin("database"); + await databaseService.saveBlock(block2); + + const response = await utils[request]("POST", "blocks/search", { + id: blocks2to100[0].id, + previousBlock: blocks2to100[0].previousBlock, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(blocks2to100[0].id); + expect(block.previous).toBe(blocks2to100[0].previousBlock); + + await databaseService.deleteBlock(block2); // reset to genesis block + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified payloadHash", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + payloadHash: genesisBlock.payloadHash, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.payload.length).toBe(genesisBlock.payloadLength); + expect(block.payload.hash).toBe(genesisBlock.payloadHash); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified generatorPublicKey", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + generatorPublicKey: genesisBlock.generatorPublicKey, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.generator.publicKey).toBe(genesisBlock.generatorPublicKey); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified blockSignature", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + blockSignature: genesisBlock.blockSignature, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.signature).toBe(genesisBlock.blockSignature); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified timestamp", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + timestamp: { + from: genesisBlock.timestamp, + to: genesisBlock.timestamp, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified height", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + height: { + from: genesisBlock.height, + to: genesisBlock.height, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.height).toBe(genesisBlock.height); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specified height range", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + height: { + from: genesisBlock.height, + to: genesisBlock.height, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.height).toBe(genesisBlock.height); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified numberOfTransactions", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + numberOfTransactions: { + from: genesisBlock.numberOfTransactions, + to: genesisBlock.numberOfTransactions, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.transactions).toBe(genesisBlock.numberOfTransactions); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specified numberOfTransactions range", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + numberOfTransactions: { + from: genesisBlock.numberOfTransactions, + to: genesisBlock.numberOfTransactions, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.transactions).toBe(genesisBlock.numberOfTransactions); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified totalAmount", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + totalAmount: { from: 1 }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specified totalAmount range", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + totalAmount: { from: 1 }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified totalFee", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + totalFee: { from: 0 }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(+block.forged.fee).toBe(genesisBlock.totalFee); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specified totalFee range", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + totalFee: { from: 0 }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(+block.forged.fee).toBe(genesisBlock.totalFee); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified reward", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + reward: { + from: genesisBlock.reward, + to: genesisBlock.reward, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(+block.forged.reward).toBe(genesisBlock.reward); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specified reward range", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + reward: { + from: genesisBlock.reward, + to: genesisBlock.reward, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(+block.forged.reward).toBe(genesisBlock.reward); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the exact specified payloadLength", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + payloadLength: { + from: genesisBlock.payloadLength, + to: genesisBlock.payloadLength, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.payload.length).toBe(genesisBlock.payloadLength); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specified payloadLength range", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + payloadLength: { + from: genesisBlock.payloadLength, + to: genesisBlock.payloadLength, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + expect(block.payload.length).toBe(genesisBlock.payloadLength); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the wrong specified version", async () => { + const response = await utils[request]("POST", "blocks/search", { + id: genesisBlock.id, + version: 2, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(0); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for blocks with the specific criteria", async () => { + const response = await utils[request]("POST", "blocks/search", { + generatorPublicKey: genesisBlock.generatorPublicKey, + version: genesisBlock.version, + timestamp: { + from: genesisBlock.timestamp, + to: genesisBlock.timestamp, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const block = response.data.data[0]; + utils.expectBlock(block); + expect(block.id).toBe(genesisBlock.id); + }); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/handlers/delegates.test.js b/packages/core-api/__tests__/v2/handlers/delegates.test.js deleted file mode 100644 index 082581d75c..0000000000 --- a/packages/core-api/__tests__/v2/handlers/delegates.test.js +++ /dev/null @@ -1,151 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const { Block } = require('@arkecosystem/crypto').models -const blocks2to100 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.2-100') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const delegate = { - username: 'genesis_9', - address: 'AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo', - publicKey: - '0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647', -} - -let container - -beforeAll(async () => { - await app.setUp() - container = require('@arkecosystem/core-container') -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Delegates', () => { - describe('GET /delegates', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the delegates', async () => { - const response = await utils[request]('GET', 'delegates') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectDelegate(response.data.data[0]) - }) - }) - }) - - describe('GET /delegates/:id', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a delegate by the given username', async () => { - const response = await utils[request]( - 'GET', - `delegates/${delegate.username}`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - utils.expectDelegate(response.data.data, delegate) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a delegate by the given address', async () => { - const response = await utils[request]( - 'GET', - `delegates/${delegate.address}`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - utils.expectDelegate(response.data.data, delegate) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a delegate by the given public key', async () => { - const response = await utils[request]( - 'GET', - `delegates/${delegate.publicKey}`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - utils.expectDelegate(response.data.data, delegate) - }) - }) - }) - - describe('POST /delegates/search', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for delegates with a username that matches the given string', async () => { - const response = await utils[request]('POST', 'delegates/search', { - username: delegate.username, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - utils.expectDelegate(response.data.data[0], delegate) - }) - }) - }) - - describe('GET /delegates/:id/blocks', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all blocks for a delegate by the given identifier', async () => { - // save a new block so that we can make the request with generatorPublicKey - const block2 = new Block(blocks2to100[0]) - const database = container.resolvePlugin('database') - await database.saveBlock(block2) - - const response = await utils[request]( - 'GET', - `delegates/${blocks2to100[0].generatorPublicKey}/blocks`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - utils.expectBlock(response.data.data[0]) - - await database.deleteBlock(block2) // reset to genesis block - }) - }) - }) - - describe('GET /delegates/:id/voters', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all voters (wallets) for a delegate by the given identifier', async () => { - const response = await utils[request]( - 'GET', - `delegates/${delegate.publicKey}/voters`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectWallet(response.data.data[0]) - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/delegates.test.ts b/packages/core-api/__tests__/v2/handlers/delegates.test.ts new file mode 100644 index 0000000000..e4a9882365 --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/delegates.test.ts @@ -0,0 +1,189 @@ +import "@arkecosystem/core-test-utils"; +import { calculateRanks, setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +import { blocks2to100 } from "../../../../core-test-utils/src/fixtures/testnet/blocks2to100"; + +import { models } from "@arkecosystem/crypto"; +const { Block } = models; + +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; + +const delegate = { + username: "genesis_9", + address: "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + publicKey: "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", +}; + +beforeAll(async () => { + await setUp(); + await calculateRanks(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 2.0 - Delegates", () => { + describe("GET /delegates", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the delegates", async () => { + const response = await utils[request]("GET", "delegates"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + response.data.data.forEach(utils.expectDelegate); + expect(response.data.data.sort((a, b) => a.rank < b.rank)).toEqual(response.data.data); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the delegates ordered by descending rank", async () => { + const response = await utils[request]("GET", "delegates", { orderBy: "rank:desc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + response.data.data.forEach(utils.expectDelegate); + expect(response.data.data.sort((a, b) => a.rank > b.rank)).toEqual(response.data.data); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the delegates ordered by descending productivity", async () => { + const response = await utils[request]("GET", "delegates", { orderBy: "productivity:desc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + response.data.data.forEach(utils.expectDelegate); + expect( + response.data.data.sort((a, b) => a.production.productivity > b.production.productivity), + ).toEqual(response.data.data); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the delegates ordered by descending approval", async () => { + const response = await utils[request]("GET", "delegates", { orderBy: "approval:desc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + response.data.data.forEach(utils.expectDelegate); + expect(response.data.data.sort((a, b) => a.production.approval > b.production.approval)).toEqual( + response.data.data, + ); + }); + }, + ); + }); + + describe("GET /delegates/:id", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a delegate by the given username", async () => { + const response = await utils[request]("GET", `delegates/${delegate.username}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + utils.expectDelegate(response.data.data, delegate); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a delegate by the given address", async () => { + const response = await utils[request]("GET", `delegates/${delegate.address}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + utils.expectDelegate(response.data.data, delegate); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a delegate by the given public key", async () => { + const response = await utils[request]("GET", `delegates/${delegate.publicKey}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + utils.expectDelegate(response.data.data, delegate); + }); + }, + ); + }); + + describe("POST /delegates/search", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for delegates with a username that matches the given string", async () => { + const response = await utils[request]("POST", "delegates/search", { + username: delegate.username, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + utils.expectDelegate(response.data.data[0], delegate); + }); + }, + ); + }); + + describe("GET /delegates/:id/blocks", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all blocks for a delegate by the given identifier", async () => { + // save a new block so that we can make the request with generatorPublicKey + const block2 = new Block(blocks2to100[0]); + const databaseService = app.resolvePlugin("database"); + await databaseService.saveBlock(block2); + + const response = await utils[request]( + "GET", + `delegates/${blocks2to100[0].generatorPublicKey}/blocks`, + ); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + utils.expectBlock(response.data.data[0]); + + await databaseService.deleteBlock(block2); // reset to genesis block + }); + }, + ); + }); + + describe("GET /delegates/:id/voters", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all voters (wallets) for a delegate by the given identifier", async () => { + const response = await utils[request]("GET", `delegates/${delegate.publicKey}/voters`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + utils.expectWallet(response.data.data[0]); + }); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/handlers/node.test.js b/packages/core-api/__tests__/v2/handlers/node.test.js deleted file mode 100644 index de4833564f..0000000000 --- a/packages/core-api/__tests__/v2/handlers/node.test.js +++ /dev/null @@ -1,67 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Loader', () => { - describe('GET /node/status', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET the node status', async () => { - const response = await utils[request]('GET', 'node/status') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data.synced).toBeBoolean() - expect(response.data.data.now).toBeNumber() - expect(response.data.data.blocksCount).toBeNumber() - }) - }) - }) - - describe('GET /node/syncing', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET the node syncing status', async () => { - const response = await utils[request]('GET', 'node/syncing') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data.syncing).toBeBoolean() - expect(response.data.data.blocks).toBeNumber() - expect(response.data.data.height).toBeNumber() - expect(response.data.data.id).toBeString() - }) - }) - }) - - describe('GET /node/configuration', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET the node configuration', async () => { - const response = await utils[request]('GET', 'node/configuration') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data.nethash).toBeString() - expect(response.data.data.token).toBeString() - expect(response.data.data.symbol).toBeString() - expect(response.data.data.explorer).toBeString() - expect(response.data.data.version).toBeNumber() - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/node.test.ts b/packages/core-api/__tests__/v2/handlers/node.test.ts new file mode 100644 index 0000000000..46a8179561 --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/node.test.ts @@ -0,0 +1,67 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 2.0 - Loader", () => { + describe("GET /node/status", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET the node status", async () => { + const response = await utils[request]("GET", "node/status"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + expect(response.data.data.synced).toBeBoolean(); + expect(response.data.data.now).toBeNumber(); + expect(response.data.data.blocksCount).toBeNumber(); + }); + }, + ); + }); + + describe("GET /node/syncing", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET the node syncing status", async () => { + const response = await utils[request]("GET", "node/syncing"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + expect(response.data.data.syncing).toBeBoolean(); + expect(response.data.data.blocks).toBeNumber(); + expect(response.data.data.height).toBeNumber(); + expect(response.data.data.id).toBeString(); + }); + }, + ); + }); + + describe("GET /node/configuration", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET the node configuration", async () => { + const response = await utils[request]("GET", "node/configuration"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + expect(response.data.data.nethash).toBeString(); + expect(response.data.data.token).toBeString(); + expect(response.data.data.symbol).toBeString(); + expect(response.data.data.explorer).toBeString(); + expect(response.data.data.version).toBeNumber(); + }); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/handlers/peers.test.js b/packages/core-api/__tests__/v2/handlers/peers.test.js deleted file mode 100644 index 4bebbe5d6c..0000000000 --- a/packages/core-api/__tests__/v2/handlers/peers.test.js +++ /dev/null @@ -1,47 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const peers = require('@arkecosystem/core-test-utils/config/testnet/peers.json') -const app = require('../../__support__/setup') -const utils = require('../utils') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Peers', () => { - describe('GET /peers', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the peers', async () => { - const response = await utils[request]('GET', 'peers') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data[0]).toBeObject() - }) - }) - }) - - describe('GET /peers/:ip', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a peer by the given ip', async () => { - const response = await utils[request]( - 'GET', - `peers/${peers.list[0].ip}`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data).toBeObject() - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/peers.test.ts b/packages/core-api/__tests__/v2/handlers/peers.test.ts new file mode 100644 index 0000000000..30c3af41bd --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/peers.test.ts @@ -0,0 +1,57 @@ +import { app } from "@arkecosystem/core-container"; +import { Peer } from "@arkecosystem/core-p2p/src/peer"; +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const mockAddress = "1.0.0.99"; +const mockPort = 4002; + +beforeAll(async () => { + await setUp(); + + const peerMock = new Peer(mockAddress, mockPort); + peerMock.setStatus("OK"); + + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + monitor.peers[peerMock.ip] = peerMock; +}); + +afterAll(async () => { + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + + await tearDown(); +}); + +describe("API 2.0 - Peers", () => { + describe("GET /peers", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (_, request) => { + it("should GET all the peers", async () => { + const response = await utils[request]("GET", "peers"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data[0]).toBeObject(); + }); + }, + ); + }); + + describe("GET /peers/:ip", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (_, request) => { + it("should GET a peer by the given ip", async () => { + const response = await utils[request]("GET", `peers/${mockAddress}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + expect(response.data.data.ip).toBe(mockAddress); + expect(response.data.data.port).toBe(mockPort); + }); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/handlers/transactions.test.js b/packages/core-api/__tests__/v2/handlers/transactions.test.js deleted file mode 100644 index 6918c8e770..0000000000 --- a/packages/core-api/__tests__/v2/handlers/transactions.test.js +++ /dev/null @@ -1,653 +0,0 @@ -/* eslint max-len: "off" */ - -require('@arkecosystem/core-test-utils/lib/matchers') -const generateTransfers = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const generateWallets = require('@arkecosystem/core-test-utils/lib/generators/wallets') -const delegates = require('@arkecosystem/core-test-utils/fixtures/testnet/delegates') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const transferFee = 10000000 - -let genesisBlock -let genesisTransactions - -let transactionId -let blockId -let type -let wrongType -let version -let senderPublicKey -let senderAddress -let recipientAddress -let timestamp -let timestampFrom -let timestampTo -let amount -let amountFrom -let amountTo -let fee -let feeFrom -let feeTo - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') - genesisTransactions = genesisBlock.transactions[0] - - transactionId = genesisTransactions.id - blockId = genesisBlock.id - type = genesisTransactions.type - wrongType = 3 - version = 1 - senderPublicKey = genesisTransactions.senderPublicKey - senderAddress = genesisTransactions.senderId - recipientAddress = genesisTransactions.recipientId - timestamp = genesisTransactions.timestamp - timestampFrom = timestamp - timestampTo = timestamp - amount = genesisTransactions.amount - amountFrom = amount - amountTo = amount - fee = genesisTransactions.fee - feeFrom = fee - feeTo = fee -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Transactions', () => { - describe('GET /transactions', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the transactions', async () => { - const response = await utils[request]('GET', 'transactions') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectTransaction(response.data.data[0]) - }) - }) - }) - - describe('GET /transactions/:id', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a transaction by the given identifier', async () => { - const response = await utils[request]( - 'GET', - `transactions/${transactionId}`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - const transaction = response.data.data - utils.expectTransaction(transaction) - expect(transaction.id).toBe(transactionId) - }) - }) - }) - - describe('GET /transactions/unconfirmed', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the unconfirmed transactions', async () => { - await utils.createTransaction() - - const response = await utils[request]('GET', 'transactions/unconfirmed') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toBeArray() - expect(response.data.data).not.toBeEmpty() - }) - }) - }) - - describe('GET /transactions/unconfirmed/:id', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET an unconfirmed transaction by the given identifier', async () => { - const transaction = await utils.createTransaction() - - const response = await utils[request]( - 'GET', - `transactions/unconfirmed/${transaction.id}`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data).toHaveProperty('id', transaction.id) - }) - }) - }) - - describe('POST /transactions/search', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified transactionId', async () => { - const response = await utils[request]('POST', 'transactions/search', { - id: transactionId, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.id).toBe(transactionId) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified blockId', async () => { - const response = await utils[request]('POST', 'transactions/search', { - blockId, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(100) - expect(response.data.meta.totalCount).toBe(153) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.blockId).toBe(blockId) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified type', async () => { - const response = await utils[request]('POST', 'transactions/search', { - type, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - expect(response.data.data).toHaveLength(51) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.type).toBe(type) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified version', async () => { - const response = await utils[request]('POST', 'transactions/search', { - version, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - expect(response.data.data).toHaveLength(100) - expect(response.data.meta.totalCount).toBe(153) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.version).toBe(version) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified senderPublicKey', async () => { - const response = await utils[request]('POST', 'transactions/search', { - senderPublicKey, - }) - - expect(response).toBeSuccessfulResponse() - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.sender).toBe(senderAddress) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified senderId', async () => { - const response = await utils[request]('POST', 'transactions/search', { - senderId: senderAddress, - }) - - expect(response).toBeSuccessfulResponse() - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.sender).toBe(senderAddress) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified recipientId (Address)', async () => { - const response = await utils[request]('POST', 'transactions/search', { - recipientId: recipientAddress, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - expect(response.data.data).toHaveLength(2) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.recipient).toBe(recipientAddress) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified timestamp', async () => { - const response = await utils[request]('POST', 'transactions/search', { - timestamp: { - from: timestamp, - to: timestamp, - }, - }) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeArray() - expect(data.length).toEqual(100) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.timestamp.epoch).toBe(timestamp) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the specified timestamp range', async () => { - const response = await utils[request]('POST', 'transactions/search', { - timestamp: { - from: timestampFrom, - to: timestampTo, - }, - }) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeArray() - expect(data).toHaveLength(100) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.timestamp.epoch).toBeGreaterThanOrEqual( - timestampFrom, - ) - expect(transaction.timestamp.epoch).toBeLessThanOrEqual(timestampTo) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified amount', async () => { - const response = await utils[request]('POST', 'transactions/search', { - amount: { - from: amount, - to: amount, - }, - }) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeArray() - expect(data).toHaveLength(50) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.amount).toBe(amount) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the specified amount range', async () => { - const response = await utils[request]('POST', 'transactions/search', { - amount: { - from: amountFrom, - to: amountTo, - }, - }) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeArray() - expect(data).toHaveLength(50) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.amount).toBeGreaterThanOrEqual(amountFrom) - expect(transaction.amount).toBeLessThanOrEqual(amountTo) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the exact specified fee', async () => { - const response = await utils[request]('POST', 'transactions/search', { - fee: { - from: fee, - to: fee, - }, - }) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeArray() - expect(data).toHaveLength(100) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.fee).toBe(fee) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the specified fee range', async () => { - const response = await utils[request]('POST', 'transactions/search', { - fee: { - from: feeFrom, - to: feeTo, - }, - }) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeArray() - expect(data).toHaveLength(100) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.fee).toBeGreaterThanOrEqual(feeFrom) - expect(transaction.fee).toBeLessThanOrEqual(feeTo) - } - }) - }) - - // TODO remove the search by id, to be sure that is OK - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it.skip('should POST a search for transactions with the exact specified vendorFieldHex', async () => { - const id = - '0000faa27b422f7648b1a2f634f15c7e5c8e96b84929624fda44abf716bdf784' - const vendorFieldHex = - '64656c65676174653a20766f746572732073686172652e205468616e6b20796f7521207c74782062792061726b2d676f' - - const response = await utils[request]('POST', 'transactions/search', { - id, - vendorFieldHex, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - expect(response.data.data).toHaveLength(1) - - for (const transaction of response.data.data) { - utils.expectTransaction(transaction) - expect(transaction.vendorField).toBe(vendorFieldHex.toString('utf8')) - } - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the wrong specified type', async () => { - const response = await utils[request]('POST', 'transactions/search', { - id: transactionId, - type: wrongType, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - expect(response.data.data).toHaveLength(0) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for transactions with the specific criteria', async () => { - const response = await utils[request]('POST', 'transactions/search', { - senderPublicKey, - type, - timestamp: { - from: timestampFrom, - to: timestampTo, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - utils.expectTransaction(response.data.data[0]) - }) - }) - }) - - describe('POST /transactions', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - const transactions = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[1].address, - 1, - 40, - true, - ) - - it('should POST all the transactions', async () => { - const response = await utils[request]('POST', 'transactions', { - transactions, - }) - expect(response).toBeSuccessfulResponse() - }) - - it('should not POST all the transactions', async () => { - const response = await utils[request]('POST', 'transactions', { - transactions: transactions.concat(transactions), - }) - - expect(response.data.statusCode).toBe(413) - expect(response.data.message).toBe( - 'Received 80 transactions. Only 40 are allowed per request.', - ) - }) - }) - - it('should POST 2 transactions double spending and get only 1 accepted and broadcasted', async () => { - const transactions = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[1].address, - 245098000000000 - 5098000000000, // a bit less than the delegates' balance - 2, - true, - ) - const response = await utils.requestWithAcceptHeader( - 'POST', - 'transactions', - { - transactions, - }, - ) - - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data.accept.length).toBe(1) - expect(response.data.data.accept[0]).toBe(transactions[0].id) - - expect(response.data.data.broadcast.length).toBe(1) - expect(response.data.data.broadcast[0]).toBe(transactions[0].id) - - expect(response.data.data.invalid.length).toBe(1) - expect(response.data.data.invalid[0]).toBe(transactions[1].id) - }) - - it.each([3, 5, 8])( - 'should accept and broadcast %i transactions emptying a wallet', - async txNumber => { - const sender = delegates[txNumber] // use txNumber so that we use a different delegate for each test case - const receivers = generateWallets('testnet', 2) - const amountPlusFee = Math.floor(sender.balance / txNumber) - const lastAmountPlusFee = - sender.balance - (txNumber - 1) * amountPlusFee - - const transactions = generateTransfers( - 'testnet', - sender.secret, - receivers[0].address, - amountPlusFee - transferFee, - txNumber - 1, - true, - ) - const lastTransaction = generateTransfers( - 'testnet', - sender.secret, - receivers[1].address, - lastAmountPlusFee - transferFee, - 1, - true, - ) - // we change the receiver in lastTransaction to prevent having 2 exact same transactions with same id (if not, could be same as transactions[0]) - - const allTransactions = transactions.concat(lastTransaction) - - const response = await utils.requestWithAcceptHeader( - 'POST', - 'transactions', - { - transactions: allTransactions, - }, - ) - - expect(response).toBeSuccessfulResponse() - - expect(response.data.data.accept.sort()).toEqual( - allTransactions.map(transaction => transaction.id).sort(), - ) - expect(response.data.data.broadcast.sort()).toEqual( - allTransactions.map(transaction => transaction.id).sort(), - ) - expect(response.data.data.invalid.length).toBe(0) - }, - ) - - it.each([3, 5, 8])( - 'should not accept the last of %i transactions emptying a wallet when the last one is 1 arktoshi too much', - async txNumber => { - const sender = delegates[txNumber + 1] // use txNumber + 1 so that we don't use the same delegates as the above test - const receivers = generateWallets('testnet', 2) - const amountPlusFee = Math.floor(sender.balance / txNumber) - const lastAmountPlusFee = - sender.balance - (txNumber - 1) * amountPlusFee + 1 - - const transactions = generateTransfers( - 'testnet', - sender.secret, - receivers[0].address, - amountPlusFee - transferFee, - txNumber - 1, - true, - ) - const lastTransaction = generateTransfers( - 'testnet', - sender.secret, - receivers[1].address, - lastAmountPlusFee - transferFee, - 1, - true, - ) - // we change the receiver in lastTransaction to prevent having 2 exact same transactions with same id (if not, could be same as transactions[0]) - - const allTransactions = transactions.concat(lastTransaction) - - const response = await utils.requestWithAcceptHeader( - 'POST', - 'transactions', - { - transactions: allTransactions, - }, - ) - - expect(response).toBeSuccessfulResponse() - - expect(response.data.data.accept.sort()).toEqual( - transactions.map(transaction => transaction.id).sort(), - ) - expect(response.data.data.broadcast.sort()).toEqual( - transactions.map(transaction => transaction.id).sort(), - ) - expect(response.data.data.invalid).toEqual( - lastTransaction.map(transaction => transaction.id), - ) - }, - ) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/transactions.test.ts b/packages/core-api/__tests__/v2/handlers/transactions.test.ts new file mode 100644 index 0000000000..1c02ef1e4f --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/transactions.test.ts @@ -0,0 +1,647 @@ +import "@arkecosystem/core-test-utils"; +import { constants } from "@arkecosystem/crypto"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +import genesisBlock from "../../../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { delegates } from "../../../../core-test-utils/src/fixtures/testnet/delegates"; +import { generateTransfers } from "../../../../core-test-utils/src/generators/transactions/transfer"; +import { generateWallets } from "../../../../core-test-utils/src/generators/wallets"; + +const transferFee = 10000000; + +let genesisTransaction; +let genesisTransactions; + +let transactionId; +let blockId; +let type; +let wrongType; +let version; +let senderPublicKey; +let senderAddress; +let recipientAddress; +let timestamp; +let timestampFrom; +let timestampTo; +let amount; +let amountFrom; +let amountTo; +let fee; +let feeFrom; +let feeTo; + +beforeAll(async () => { + await setUp(); + + genesisTransactions = genesisBlock.transactions; + genesisTransaction = genesisTransactions[0]; + + transactionId = genesisTransaction.id; + blockId = genesisBlock.id; + type = genesisTransaction.type; + wrongType = 3; + version = 1; + senderPublicKey = genesisTransaction.senderPublicKey; + senderAddress = genesisTransaction.senderId; + recipientAddress = genesisTransaction.recipientId; + timestamp = genesisTransaction.timestamp; + timestampFrom = timestamp; + timestampTo = timestamp; + amount = genesisTransaction.amount; + amountFrom = amount; + amountTo = amount; + fee = genesisTransaction.fee; + feeFrom = fee; + feeTo = fee; +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 2.0 - Transactions", () => { + describe("GET /transactions", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the transactions", async () => { + const response = await utils[request]("GET", "transactions"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + utils.expectTransaction(response.data.data[0]); + }); + }, + ); + }); + + describe("GET /transactions/:id", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a transaction by the given identifier", async () => { + const response = await utils[request]("GET", `transactions/${transactionId}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + const transaction = response.data.data; + utils.expectTransaction(transaction); + expect(transaction.id).toBe(transactionId); + }); + }, + ); + }); + + describe("GET /transactions/unconfirmed", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the unconfirmed transactions", async () => { + await utils.createTransaction(); + + const response = await utils[request]("GET", "transactions/unconfirmed"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).not.toBeEmpty(); + }); + }, + ); + }); + + describe("GET /transactions/unconfirmed/:id", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET an unconfirmed transaction by the given identifier", async () => { + const transaction = await utils.createTransaction(); + + const response = await utils[request]("GET", `transactions/unconfirmed/${transaction.id}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + expect(response.data.data).toHaveProperty("id", transaction.id); + }); + }, + ); + }); + + describe("GET /transactions/types", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET transaction types", async () => { + const response = await utils[request]("GET", "transactions/types"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + expect(response.data.data).toEqual({ + Transfer: 0, + SecondSignature: 1, + DelegateRegistration: 2, + Vote: 3, + MultiSignature: 4, + Ipfs: 5, + TimelockTransfer: 6, + MultiPayment: 7, + DelegateResignation: 8, + }); + }); + }, + ); + }); + + describe("POST /transactions/search", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified transactionId", async () => { + const response = await utils[request]("POST", "transactions/search", { + id: transactionId, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(1); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.id).toBe(transactionId); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified blockId", async () => { + const response = await utils[request]("POST", "transactions/search", { + blockId, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(100); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.blockId).toBe(blockId); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified type", async () => { + const response = await utils[request]("POST", "transactions/search", { + type, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(51); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.type).toBe(type); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified version", async () => { + const response = await utils[request]("POST", "transactions/search", { + version, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(100); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.version).toBe(version); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified senderPublicKey", async () => { + const response = await utils[request]("POST", "transactions/search", { + senderPublicKey, + }); + + expect(response).toBeSuccessfulResponse(); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.sender).toBe(senderAddress); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified senderId", async () => { + const response = await utils[request]("POST", "transactions/search", { + senderId: senderAddress, + }); + + expect(response).toBeSuccessfulResponse(); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.sender).toBe(senderAddress); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified recipientId (Address)", async () => { + const response = await utils[request]("POST", "transactions/search", { + recipientId: recipientAddress, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(2); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.recipient).toBe(recipientAddress); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the any of the specified addresses", async () => { + const response = await utils[request]("POST", "transactions/search", { + addresses: [genesisTransactions[3].recipientId, genesisTransactions[8].recipientId], + }); + + expect(response).toBeSuccessfulResponse(); + + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(6); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified timestamp", async () => { + const response = await utils[request]("POST", "transactions/search", { + timestamp: { + from: timestamp, + to: timestamp, + }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(100); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.timestamp.epoch).toBe(timestamp); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the specified timestamp range", async () => { + const response = await utils[request]("POST", "transactions/search", { + timestamp: { + from: timestampFrom, + to: timestampTo, + }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(100); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.timestamp.epoch).toBeGreaterThanOrEqual(timestampFrom); + expect(transaction.timestamp.epoch).toBeLessThanOrEqual(timestampTo); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified amount", async () => { + const response = await utils[request]("POST", "transactions/search", { + amount: { + from: amount, + to: amount, + }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(50); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.amount).toBe(amount); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the specified amount range", async () => { + const response = await utils[request]("POST", "transactions/search", { + amount: { + from: amountFrom, + to: amountTo, + }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(50); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.amount).toBeGreaterThanOrEqual(amountFrom); + expect(transaction.amount).toBeLessThanOrEqual(amountTo); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified fee", async () => { + const response = await utils[request]("POST", "transactions/search", { + fee: { + from: fee, + to: fee, + }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(100); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.fee).toBe(fee); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the specified fee range", async () => { + const response = await utils[request]("POST", "transactions/search", { + fee: { + from: feeFrom, + to: feeTo, + }, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(100); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(transaction.fee).toBeGreaterThanOrEqual(feeFrom); + expect(transaction.fee).toBeLessThanOrEqual(feeTo); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the exact specified vendorFieldHex", async () => { + const dummyTransaction = await utils.createTransaction(); + const hexify = (value: string) => Buffer.from(value, "utf8").toString("hex"); + + const vendorFieldHex = hexify(dummyTransaction.vendorField); + const response = await utils[request]("POST", "transactions/search", { + vendorFieldHex, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + // TODO: the response is sometimes empty. Racy test? + // expect(response.data.data).toHaveLength(1); + + for (const transaction of response.data.data) { + utils.expectTransaction(transaction); + expect(hexify(transaction.vendorField)).toBe(vendorFieldHex); + } + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the wrong specified type", async () => { + const response = await utils[request]("POST", "transactions/search", { + id: transactionId, + type: wrongType, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(0); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for transactions with the specific criteria", async () => { + const response = await utils[request]("POST", "transactions/search", { + senderPublicKey, + type, + timestamp: { + from: timestampFrom, + to: timestampTo, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + utils.expectTransaction(response.data.data[0]); + }); + }, + ); + }); + + describe("POST /transactions", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + const transactions = generateTransfers( + "testnet", + delegates[0].secret, + delegates[1].address, + 1, + 40, + true, + ); + + it("should POST all the transactions", async () => { + const response = await utils[request]("POST", "transactions", { + transactions, + }); + expect(response).toBeSuccessfulResponse(); + }); + + it("should not POST all the transactions", async () => { + const response = await utils[request]("POST", "transactions", { + transactions: transactions.concat(transactions), + }); + + expect(response.data.statusCode).toBe(400); + expect(response.data.message).toBe( + 'child "transactions" fails because ["transactions" must contain less than or equal to 40 items]', + ); + }); + }, + ); + + it("should POST 2 transactions double spending and get only 1 accepted and broadcasted", async () => { + const transactions = generateTransfers( + "testnet", + delegates[0].secret, + delegates[1].address, + 245098000000000 - 5098000000000, // a bit less than the delegates' balance + 2, + true, + ); + const response = await utils.requestWithAcceptHeader("POST", "transactions", { + transactions, + }); + + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + expect(response.data.data.accept).toHaveLength(1); + expect(response.data.data.accept[0]).toBe(transactions[0].id); + + expect(response.data.data.broadcast).toHaveLength(1); + expect(response.data.data.broadcast[0]).toBe(transactions[0].id); + + expect(response.data.data.invalid).toHaveLength(1); + expect(response.data.data.invalid[0]).toBe(transactions[1].id); + }); + + it.each([3, 5, 8])("should accept and broadcast %i transactions emptying a wallet", async txNumber => { + const sender = delegates[txNumber]; // use txNumber so that we use a different delegate for each test case + const receivers = generateWallets("testnet", 2); + const amountPlusFee = Math.floor(sender.balance / txNumber); + const lastAmountPlusFee = sender.balance - (txNumber - 1) * amountPlusFee; + + const transactions = generateTransfers( + "testnet", + sender.secret, + receivers[0].address, + amountPlusFee - transferFee, + txNumber - 1, + true, + ); + const lastTransaction = generateTransfers( + "testnet", + sender.secret, + receivers[1].address, + lastAmountPlusFee - transferFee, + 1, + true, + ); + // we change the receiver in lastTransaction to prevent having 2 exact same transactions with same id (if not, could be same as transactions[0]) + + const allTransactions = transactions.concat(lastTransaction); + + const response = await utils.requestWithAcceptHeader("POST", "transactions", { + transactions: allTransactions, + }); + + expect(response).toBeSuccessfulResponse(); + + expect(response.data.data.accept.sort()).toEqual(allTransactions.map(transaction => transaction.id).sort()); + expect(response.data.data.broadcast.sort()).toEqual( + allTransactions.map(transaction => transaction.id).sort(), + ); + expect(response.data.data.invalid).toHaveLength(0); + }); + + it.each([3, 5, 8])( + "should not accept the last of %i transactions emptying a wallet when the last one is 1 arktoshi too much", + async txNumber => { + const sender = delegates[txNumber + 1]; // use txNumber + 1 so that we don't use the same delegates as the above test + const receivers = generateWallets("testnet", 2); + const amountPlusFee = Math.floor(sender.balance / txNumber); + const lastAmountPlusFee = sender.balance - (txNumber - 1) * amountPlusFee + 1; + + const transactions = generateTransfers( + "testnet", + sender.secret, + receivers[0].address, + amountPlusFee - transferFee, + txNumber - 1, + true, + ); + const lastTransaction = generateTransfers( + "testnet", + sender.secret, + receivers[1].address, + lastAmountPlusFee - transferFee, + 1, + true, + ); + // we change the receiver in lastTransaction to prevent having 2 exact same transactions with same id (if not, could be same as transactions[0]) + + const allTransactions = transactions.concat(lastTransaction); + + const response = await utils.requestWithAcceptHeader("POST", "transactions", { + transactions: allTransactions, + }); + + expect(response).toBeSuccessfulResponse(); + + expect(response.data.data.accept.sort()).toEqual( + transactions.map(transaction => transaction.id).sort(), + ); + expect(response.data.data.broadcast.sort()).toEqual( + transactions.map(transaction => transaction.id).sort(), + ); + expect(response.data.data.invalid).toEqual(lastTransaction.map(transaction => transaction.id)); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/handlers/votes.test.js b/packages/core-api/__tests__/v2/handlers/votes.test.js deleted file mode 100644 index 70de25f462..0000000000 --- a/packages/core-api/__tests__/v2/handlers/votes.test.js +++ /dev/null @@ -1,48 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const voteId = 'ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3' - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Votes', () => { - describe('GET /votes', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the votes', async () => { - const response = await utils[request]('GET', 'votes') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - utils.expectPaginator(response) - - expect(response.data.data[0]).toBeObject() - expect(response.data.meta.count).toBeNumber() - }) - }) - }) - - describe('GET /votes/:id', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a vote by the given identifier', async () => { - const response = await utils[request]('GET', `votes/${voteId}`) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - expect(response.data.data).toBeObject() - expect(response.data.data.id).toBe(voteId) - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/votes.test.ts b/packages/core-api/__tests__/v2/handlers/votes.test.ts new file mode 100644 index 0000000000..bb1d378593 --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/votes.test.ts @@ -0,0 +1,48 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const voteId = "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 2.0 - Votes", () => { + describe("GET /votes", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the votes", async () => { + const response = await utils[request]("GET", "votes"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + utils.expectPaginator(response); + + expect(response.data.data[0]).toBeObject(); + expect(response.data.meta.count).toBeNumber(); + }); + }, + ); + }); + + describe("GET /votes/:id", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a vote by the given identifier", async () => { + const response = await utils[request]("GET", `votes/${voteId}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + expect(response.data.data).toBeObject(); + expect(response.data.data.id).toBe(voteId); + }); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/handlers/wallets.test.js b/packages/core-api/__tests__/v2/handlers/wallets.test.js deleted file mode 100644 index 9af90bc5f1..0000000000 --- a/packages/core-api/__tests__/v2/handlers/wallets.test.js +++ /dev/null @@ -1,357 +0,0 @@ -/* eslint max-len: "off" */ - -require('@arkecosystem/core-test-utils/lib/matchers') -const app = require('../../__support__/setup') -const utils = require('../utils') - -const username = 'genesis_9' -const address = 'AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo' -const publicKey = - '0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647' -const balance = 245098000000000 - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API 2.0 - Wallets', () => { - describe('GET /wallets', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the wallets', async () => { - const response = await utils[request]('GET', 'wallets') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectWallet(response.data.data[0]) - }) - }) - }) - - describe('GET /wallets/top', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the top wallets', async () => { - const response = await utils[request]('GET', 'wallets/top') - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectWallet(response.data.data[0]) - }) - }) - }) - - describe('GET /wallets/:id', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET a wallet by the given identifier', async () => { - const response = await utils[request]('GET', `wallets/${address}`) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeObject() - - const wallet = response.data.data - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - }) - }) - - describe('when requesting an unknown address', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should return ResourceNotFound error', async () => { - try { - await utils[request]('GET', 'wallets/dummy') - } catch (error) { - expect(error.response.status).toEqual(404) - } - }) - }) - }) - }) - - describe('GET /wallets/:id/transactions', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the transactions for the given wallet by id', async () => { - const response = await utils[request]( - 'GET', - `wallets/${address}/transactions`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectTransaction(response.data.data[0]) - }) - }) - }) - - describe('GET /wallets/:id/transactions/sent', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the send transactions for the given wallet by id', async () => { - const response = await utils[request]( - 'GET', - `wallets/${address}/transactions/sent`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - const transaction = response.data.data[0] - utils.expectTransaction(transaction) - expect(transaction.sender).toBe(address) - }) - }) - }) - - describe('GET /wallets/:id/transactions/received', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the received transactions for the given wallet by id', async () => { - const response = await utils[request]( - 'GET', - `wallets/${address}/transactions/received`, - ) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - utils.expectTransaction(response.data.data[0]) - }) - }) - }) - - describe('GET /wallets/:id/votes', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should GET all the votes for the given wallet by id', async () => { - const response = await utils[request]('GET', `wallets/${address}/votes`) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data[0]).toBeObject() - }) - }) - }) - - describe('POST /wallets/search', () => { - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the exact specified address', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the exact specified publicKey', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - publicKey, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - expect(wallet.publicKey).toBe(publicKey) - }) - }) - - // describe.each([ - // ['API-Version', 'request'], - // ['Accept', 'requestWithAcceptHeader'] - // ])('using the %s header', (header, request) => { - // it('should POST a search for wallets with the exact specified secondPublicKey', async () => { - // const response = await utils[request]('POST', 'wallets/search', { address: addressSecondPassphrase, secondPublicKey }) - // expect(response).toBeSuccessfulResponse() - // expect(response.data.data).toBeArray() - - // expect(response.data.data).toHaveLength(1) - - // const wallet = response.data.data[0] - // utils.expectWallet(wallet) - // expect(wallet.address).toBe(addressSecondPassphrase) - // }) - // }) - - // describe.each([ - // ['API-Version', 'request'], - // ['Accept', 'requestWithAcceptHeader'] - // ])('using the %s header', (header, request) => { - // it('should POST a search for wallets with the exact specified vote', async () => { - // const response = await utils[request]('POST', 'wallets/search', { address: address, vote }) - // expect(response).toBeSuccessfulResponse() - // expect(response.data.data).toBeArray() - - // expect(response.data.data).toHaveLength(1) - - // const wallet = response.data.data[0] - // utils.expectWallet(wallet) - // expect(wallet.address).toBe(address) - // }) - // }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the exact specified username', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - username, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the exact specified balance', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - balance: { - from: balance, - to: balance, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - expect(wallet.balance).toBe(balance) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the specified balance range', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - balance: { - from: balance - 1000, - to: balance + 1000, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - expect(wallet.balance).toBe(balance) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the exact specified voteBalance', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - voteBalance: { - from: balance, - to: balance, - }, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the wrong specified username', async () => { - const response = await utils[request]('POST', 'wallets/search', { - address, - username: 'dummy', - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(0) - }) - }) - - describe.each([ - ['API-Version', 'request'], - ['Accept', 'requestWithAcceptHeader'], - ])('using the %s header', (header, request) => { - it('should POST a search for wallets with the specific criteria', async () => { - const response = await utils[request]('POST', 'wallets/search', { - publicKey, - username, - }) - expect(response).toBeSuccessfulResponse() - expect(response.data.data).toBeArray() - - expect(response.data.data).toHaveLength(1) - - const wallet = response.data.data[0] - utils.expectWallet(wallet) - expect(wallet.address).toBe(address) - }) - }) - }) -}) diff --git a/packages/core-api/__tests__/v2/handlers/wallets.test.ts b/packages/core-api/__tests__/v2/handlers/wallets.test.ts new file mode 100644 index 0000000000..0a20e72838 --- /dev/null +++ b/packages/core-api/__tests__/v2/handlers/wallets.test.ts @@ -0,0 +1,362 @@ +import "@arkecosystem/core-test-utils"; +import { setUp, tearDown } from "../../__support__/setup"; +import { utils } from "../utils"; + +const username = "genesis_9"; +const address = "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo"; +const publicKey = "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647"; +const balance = 245098000000000; +const address2 = "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API 2.0 - Wallets", () => { + describe("GET /wallets", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the wallets", async () => { + const response = await utils[request]("GET", "wallets"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + utils.expectWallet(response.data.data[0]); + }); + }, + ); + }); + + describe("GET /wallets/top", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the top wallets", async () => { + const response = await utils[request]("GET", "wallets/top"); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + utils.expectWallet(response.data.data[0]); + }); + }, + ); + }); + + describe("GET /wallets/:id", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET a wallet by the given identifier", async () => { + const response = await utils[request]("GET", `wallets/${address}`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeObject(); + + const wallet = response.data.data; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + }); + }, + ); + + describe("when requesting an unknown address", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should return ResourceNotFound error", async () => { + try { + await utils[request]("GET", "wallets/dummy"); + } catch (error) { + expect(error.response.status).toEqual(404); + } + }); + }, + ); + }); + }); + + describe("GET /wallets/:id/transactions", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the transactions for the given wallet by id", async () => { + const response = await utils[request]("GET", `wallets/${address}/transactions`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + utils.expectTransaction(response.data.data[0]); + }); + }, + ); + }); + + describe("GET /wallets/:id/transactions/sent", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the send transactions for the given wallet by id", async () => { + const response = await utils[request]("GET", `wallets/${address}/transactions/sent`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + const transaction = response.data.data[0]; + utils.expectTransaction(transaction); + expect(transaction.sender).toBe(address); + }); + }, + ); + }); + + describe("GET /wallets/:id/transactions/received", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the received transactions for the given wallet by id", async () => { + const response = await utils[request]("GET", `wallets/${address}/transactions/received`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + utils.expectTransaction(response.data.data[0]); + }); + }, + ); + }); + + describe("GET /wallets/:id/votes", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should GET all the votes for the given wallet by id", async () => { + const response = await utils[request]("GET", `wallets/${address}/votes`); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data[0]).toBeObject(); + }); + }, + ); + }); + + describe("POST /wallets/search", () => { + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the exact specified address", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + }); + + it("should POST a search for wallets with the any of the specified addresses", async () => { + const response = await utils[request]("POST", "wallets/search", { + addresses: [address, address2], + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + expect(response.data.data).toHaveLength(2); + + for (const wallet of response.data.data) { + utils.expectWallet(wallet); + } + + const addresses = response.data.data.map(wallet => wallet.address).sort(); + expect(addresses).toEqual([address, address2]); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the exact specified publicKey", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + publicKey, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + expect(wallet.publicKey).toBe(publicKey); + }); + }, + ); + + // describe.each([ + // ['API-Version', 'request'], + // ['Accept', 'requestWithAcceptHeader'] + // ])('using the %s header', (header, request) => { + // it('should POST a search for wallets with the exact specified secondPublicKey', async () => { + // const response = await utils[request]('POST', 'wallets/search', { address: addressSecondPassphrase, secondPublicKey }) + // expect(response).toBeSuccessfulResponse() + // expect(response.data.data).toBeArray() + + // expect(response.data.data).toHaveLength(1) + + // const wallet = response.data.data[0] + // utils.expectWallet(wallet) + // expect(wallet.address).toBe(addressSecondPassphrase) + // }) + // }) + + // describe.each([ + // ['API-Version', 'request'], + // ['Accept', 'requestWithAcceptHeader'] + // ])('using the %s header', (header, request) => { + // it('should POST a search for wallets with the exact specified vote', async () => { + // const response = await utils[request]('POST', 'wallets/search', { address: address, vote }) + // expect(response).toBeSuccessfulResponse() + // expect(response.data.data).toBeArray() + + // expect(response.data.data).toHaveLength(1) + + // const wallet = response.data.data[0] + // utils.expectWallet(wallet) + // expect(wallet.address).toBe(address) + // }) + // }) + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the exact specified username", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + username, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the exact specified balance", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + balance: { + from: balance, + to: balance, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + expect(wallet.balance).toBe(balance); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the specified balance range", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + balance: { + from: balance - 1000, + to: balance + 1000, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + expect(wallet.balance).toBe(balance); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the exact specified voteBalance", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + voteBalance: { + from: balance, + to: balance, + }, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the wrong specified username", async () => { + const response = await utils[request]("POST", "wallets/search", { + address, + username: "dummy", + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(0); + }); + }, + ); + + describe.each([["API-Version", "request"], ["Accept", "requestWithAcceptHeader"]])( + "using the %s header", + (header, request) => { + it("should POST a search for wallets with the specific criteria", async () => { + const response = await utils[request]("POST", "wallets/search", { + publicKey, + username, + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(1); + + const wallet = response.data.data[0]; + utils.expectWallet(wallet); + expect(wallet.address).toBe(address); + }); + }, + ); + }); +}); diff --git a/packages/core-api/__tests__/v2/utils.js b/packages/core-api/__tests__/v2/utils.js deleted file mode 100644 index 71076a0823..0000000000 --- a/packages/core-api/__tests__/v2/utils.js +++ /dev/null @@ -1,182 +0,0 @@ -const axios = require('axios') -const { - client, - transactionBuilder, - NetworkManager, -} = require('@arkecosystem/crypto') -const apiHelpers = require('@arkecosystem/core-test-utils/lib/helpers/api') - -class Helpers { - async request(method, path, params = {}) { - const url = `http://localhost:4003/api/${path}` - const headers = { - 'API-Version': 2, - 'Content-Type': 'application/json', - } - - const server = require('@arkecosystem/core-container').resolvePlugin('api') - - return apiHelpers.request(server.http, method, url, headers, params) - } - - async requestWithAcceptHeader(method, path, params = {}) { - const url = `http://localhost:4003/api/${path}` - const headers = { - Accept: 'application/vnd.ark.core-api.v2+json', - 'Content-Type': 'application/json', - } - - const server = require('@arkecosystem/core-container').resolvePlugin('api') - - return apiHelpers.request(server.http, method, url, headers, params) - } - - expectJson(response) { - expect(response.data).toBeObject() - } - - expectStatus(response, code) { - expect(response.status).toBe(code) - } - - assertVersion(response, version) { - expect(response.headers).toBeObject() - expect(response.headers).toHaveProperty('api-version', version) - } - - expectResource(response) { - expect(response.data.data).toBeObject() - } - - expectCollection(response) { - expect(Array.isArray(response.data.data)).toBe(true) - } - - expectPaginator(response, firstPage = true) { - expect(response.data.meta).toBeObject() - expect(response.data.meta).toHaveProperty('count') - expect(response.data.meta).toHaveProperty('pageCount') - expect(response.data.meta).toHaveProperty('totalCount') - expect(response.data.meta).toHaveProperty('next') - expect(response.data.meta).toHaveProperty('previous') - expect(response.data.meta).toHaveProperty('self') - expect(response.data.meta).toHaveProperty('first') - expect(response.data.meta).toHaveProperty('last') - } - - expectSuccessful(response, statusCode = 200) { - this.expectStatus(response, statusCode) - this.expectJson(response) - this.assertVersion(response, 2) - } - - expectError(response, statusCode = 404) { - this.expectStatus(response, statusCode) - this.expectJson(response) - expect(response.data.statusCode).toBeNumber() - expect(response.data.error).toBeString() - expect(response.data.message).toBeString() - } - - expectTransaction(transaction) { - expect(transaction).toBeObject() - expect(transaction).toHaveProperty('id') - expect(transaction).toHaveProperty('blockId') - expect(transaction).toHaveProperty('type') - expect(transaction).toHaveProperty('amount') - expect(transaction).toHaveProperty('fee') - expect(transaction).toHaveProperty('sender') - - if ([1, 2].indexOf(transaction.type) === -1) { - expect(transaction.recipient).toBeString() - } - - expect(transaction.signature).toBeString() - expect(transaction.confirmations).toBeNumber() - } - - expectBlock(block, expected) { - expect(block).toBeObject() - expect(block.id).toBeString() - expect(block.version).toBeNumber() - expect(block.height).toBeNumber() - expect(block).toHaveProperty('previous') // `null` or String - expect(block).toHaveProperty('forged') - expect(block.forged.reward).toBeNumber() - expect(block.forged.fee).toBeNumber() - expect(block.forged.total).toBeNumber() - expect(block.forged.amount).toBeNumber() - expect(block).toHaveProperty('payload') - expect(block.payload.length).toBeNumber() - expect(block.payload.hash).toBeString() - expect(block).toHaveProperty('generator') - expect(block.generator.publicKey).toBeString() - expect(block.signature).toBeString() - expect(block.transactions).toBeNumber() - - Object.keys(expected || {}).forEach(attr => { - expect(block[attr]).toEqual(expected[attr]) - }) - } - - expectDelegate(delegate, expected) { - expect(delegate).toBeObject() - expect(delegate.username).toBeString() - expect(delegate.address).toBeString() - expect(delegate.publicKey).toBeString() - expect(delegate.votes).toBeNumber() - expect(delegate.rank).toBeNumber() - expect(delegate.blocks).toBeObject() - expect(delegate.blocks.missed).toBeNumber() - expect(delegate.blocks.produced).toBeNumber() - expect(delegate.production).toBeObject() - expect(delegate.production.approval).toBeNumber() - expect(delegate.production.productivity).toBeNumber() - expect(delegate.forged.fees).toBeNumber() - expect(delegate.forged.rewards).toBeNumber() - expect(delegate.forged.total).toBeNumber() - - Object.keys(expected || {}).forEach(attr => { - expect(delegate[attr]).toBe(expected[attr]) - }) - } - - expectWallet(wallet) { - expect(wallet).toBeObject() - expect(wallet).toHaveProperty('address') - expect(wallet).toHaveProperty('publicKey') - expect(wallet).toHaveProperty('balance') - expect(wallet).toHaveProperty('isDelegate') - } - - async createTransaction() { - client.setConfig(NetworkManager.findByName('testnet')) - - const transaction = transactionBuilder - .transfer() - .amount(1 * 1e8) - .recipientId('AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1') - .vendorField('test') - .sign( - 'prison tobacco acquire stone dignity palace note decade they current lesson robot', - ) - .getStruct() - - await axios.post( - 'http://127.0.0.1:4003/api/v2/transactions', - { - transactions: [transaction], - }, - { - headers: { 'Content-Type': 'application/json' }, - }, - ) - - return transaction - } -} - -/** - * @type {Helpers} - */ -module.exports = new Helpers() diff --git a/packages/core-api/__tests__/v2/utils.ts b/packages/core-api/__tests__/v2/utils.ts new file mode 100644 index 0000000000..afb7d99c91 --- /dev/null +++ b/packages/core-api/__tests__/v2/utils.ts @@ -0,0 +1,176 @@ +import { app } from "@arkecosystem/core-container"; +import { client, NetworkManager, transactionBuilder } from "@arkecosystem/crypto"; +import axios from "axios"; +import "jest-extended"; +import { ApiHelpers } from "../../../core-test-utils/src/helpers/api"; + +class Helpers { + public async request(method, path, params = {}) { + const url = `http://localhost:4003/api/${path}`; + const headers = { + "API-Version": 2, + "Content-Type": "application/json", + }; + + const server = app.resolvePlugin("api"); + + return ApiHelpers.request(server.http, method, url, headers, params); + } + + public async requestWithAcceptHeader(method, path, params = {}) { + const url = `http://localhost:4003/api/${path}`; + const headers = { + Accept: "application/vnd.core-api.v2+json", + "Content-Type": "application/json", + }; + + const server = app.resolvePlugin("api"); + + return ApiHelpers.request(server.http, method, url, headers, params); + } + + public expectJson(response) { + expect(response.data).toBeObject(); + } + + public expectStatus(response, code) { + expect(response.status).toBe(code); + } + + public assertVersion(response, version) { + expect(response.headers).toBeObject(); + expect(response.headers).toHaveProperty("api-version", version); + } + + public expectResource(response) { + expect(response.data.data).toBeObject(); + } + + public expectCollection(response) { + expect(Array.isArray(response.data.data)).toBe(true); + } + + public expectPaginator(response, firstPage = true) { + expect(response.data.meta).toBeObject(); + expect(response.data.meta).toHaveProperty("count"); + expect(response.data.meta).toHaveProperty("pageCount"); + expect(response.data.meta).toHaveProperty("totalCount"); + expect(response.data.meta).toHaveProperty("next"); + expect(response.data.meta).toHaveProperty("previous"); + expect(response.data.meta).toHaveProperty("self"); + expect(response.data.meta).toHaveProperty("first"); + expect(response.data.meta).toHaveProperty("last"); + } + + public expectSuccessful(response, statusCode = 200) { + this.expectStatus(response, statusCode); + this.expectJson(response); + this.assertVersion(response, 2); + } + + public expectError(response, statusCode = 404) { + this.expectStatus(response, statusCode); + this.expectJson(response); + expect(response.data.statusCode).toBeNumber(); + expect(response.data.error).toBeString(); + expect(response.data.message).toBeString(); + } + + public expectTransaction(transaction) { + expect(transaction).toBeObject(); + expect(transaction).toHaveProperty("id"); + expect(transaction).toHaveProperty("blockId"); + expect(transaction).toHaveProperty("type"); + expect(transaction).toHaveProperty("amount"); + expect(transaction).toHaveProperty("fee"); + expect(transaction).toHaveProperty("sender"); + + if ([1, 2].indexOf(transaction.type) === -1) { + expect(transaction.recipient).toBeString(); + } + + expect(transaction.signature).toBeString(); + expect(transaction.confirmations).toBeNumber(); + } + + public expectBlock(block, expected: any = {}) { + expect(block).toBeObject(); + expect(block.id).toBeString(); + expect(block.version).toBeNumber(); + expect(block.height).toBeNumber(); + expect(block).toHaveProperty("previous"); // `null` or String + expect(block).toHaveProperty("forged"); + expect(block.forged.reward).toBeNumber(); + expect(block.forged.fee).toBeNumber(); + expect(block.forged.total).toBeNumber(); + expect(block.forged.amount).toBeNumber(); + expect(block).toHaveProperty("payload"); + expect(block.payload.length).toBeNumber(); + expect(block.payload.hash).toBeString(); + expect(block).toHaveProperty("generator"); + expect(block.generator.publicKey).toBeString(); + expect(block.signature).toBeString(); + expect(block.transactions).toBeNumber(); + + Object.keys(expected || {}).forEach(attr => { + expect(block[attr]).toEqual(expected[attr]); + }); + } + + public expectDelegate(delegate, expected: any = {}) { + expect(delegate).toBeObject(); + expect(delegate.username).toBeString(); + expect(delegate.address).toBeString(); + expect(delegate.publicKey).toBeString(); + expect(delegate.votes).toBeNumber(); + expect(delegate.rank).toBeNumber(); + expect(delegate.blocks).toBeObject(); + expect(delegate.blocks.missed).toBeNumber(); + expect(delegate.blocks.produced).toBeNumber(); + expect(delegate.production).toBeObject(); + expect(delegate.production.approval).toBeNumber(); + expect(delegate.production.productivity).toBeNumber(); + expect(delegate.forged.fees).toBeNumber(); + expect(delegate.forged.rewards).toBeNumber(); + expect(delegate.forged.total).toBeNumber(); + + Object.keys(expected || {}).forEach(attr => { + expect(delegate[attr]).toBe(expected[attr]); + }); + } + + public expectWallet(wallet) { + expect(wallet).toBeObject(); + expect(wallet).toHaveProperty("address"); + expect(wallet).toHaveProperty("publicKey"); + expect(wallet).toHaveProperty("balance"); + expect(wallet).toHaveProperty("isDelegate"); + expect(wallet).toHaveProperty("vote"); + } + + public async createTransaction() { + client.setConfig(NetworkManager.findByName("testnet")); + + const transaction = transactionBuilder + .transfer() + .amount(1 * 1e8) + .recipientId("AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1") + .vendorField("test") + .sign("prison tobacco acquire stone dignity palace note decade they current lesson robot") + .getStruct(); + + await axios.post( + "http://127.0.0.1:4003/api/v2/transactions", + { + transactions: [transaction], + }, + { + headers: { "Content-Type": "application/json" }, + }, + ); + + return transaction; + } +} + +export const utils = new Helpers(); diff --git a/packages/core-api/jest.config.js b/packages/core-api/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-api/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-api/lib/defaults.js b/packages/core-api/lib/defaults.js deleted file mode 100644 index 0e201f6d40..0000000000 --- a/packages/core-api/lib/defaults.js +++ /dev/null @@ -1,83 +0,0 @@ -const path = require('path') - -module.exports = { - enabled: false, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4003, - cache: { - /** - * How many seconds the server will try to complete the request and cache the result. - * - * Defaults to 8 seconds, set it to false if you do not care about the timeout. - * - * Setting it to false can result in requests never being completed, which is usually - * caused by low-spec servers that are unable to handle the heavy load that results - * out of SQL queries on the blocks and transactions tables. - * - * If you experience issues with the cache timeout, which is indicated by a 503 status codes, - * you should consider upgrading your hardware or tweak your PostgreSQL settings. - */ - generateTimeout: process.env.ARK_API_CACHE_TIMEOUT || 8000, - }, - // @see https://hapijs.com/api#-serveroptionstls - ssl: { - enabled: process.env.ARK_API_SSL, - host: process.env.ARK_API_SSL_HOST || '0.0.0.0', - port: process.env.ARK_API_SSL_PORT || 8443, - key: process.env.ARK_API_SSL_KEY, - cert: process.env.ARK_API_SSL_CERT, - }, - // @see https://github.com/p-meier/hapi-api-version - versions: { - validVersions: [1, 2], - defaultVersion: 1, - basePath: '/api/', - vendorName: 'ark.core-api', - }, - // @see https://github.com/wraithgar/hapi-rate-limit - rateLimit: { - enabled: !process.env.ARK_API_RATE_LIMIT, - pathLimit: false, - userLimit: process.env.ARK_API_RATE_LIMIT_USER_LIMIT || 300, - userCache: { - expiresIn: process.env.ARK_API_RATE_LIMIT_USER_EXPIRES || 60000, - }, - ipWhitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - // @see https://github.com/fknop/hapi-pagination - pagination: { - limit: 100, - include: [ - '/api/v2/blocks', - '/api/v2/blocks/{id}/transactions', - '/api/v2/blocks/search', - '/api/v2/delegates', - '/api/v2/delegates/{id}/blocks', - '/api/v2/delegates/{id}/voters', - '/api/v2/delegates/search', - '/api/v2/peers', - '/api/v2/transactions', - '/api/v2/transactions/search', - '/api/v2/transactions/unconfirmed', - '/api/v2/votes', - '/api/v2/wallets', - '/api/v2/wallets/top', - '/api/v2/wallets/{id}/transactions', - '/api/v2/wallets/{id}/transactions/received', - '/api/v2/wallets/{id}/transactions/sent', - '/api/v2/wallets/{id}/votes', - '/api/v2/wallets/search', - ], - }, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - plugins: [ - { - plugin: path.resolve(__dirname, './versions/1'), - routes: { prefix: '/api/v1' }, - }, - { - plugin: path.resolve(__dirname, './versions/2'), - routes: { prefix: '/api/v2' }, - }, - ], -} diff --git a/packages/core-api/lib/index.js b/packages/core-api/lib/index.js deleted file mode 100644 index 811bf3ad83..0000000000 --- a/packages/core-api/lib/index.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'api', - async register(container, options) { - if (!options.enabled) { - container - .resolvePlugin('logger') - .info('Public API is disabled :grey_exclamation:') - - return - } - - return require('./server')(options) - }, - async deregister(container, options) { - if (options.enabled) { - const servers = Object.entries(container.resolvePlugin('api')) - - for (const [type, server] of servers) { - container.resolvePlugin('logger').info(`Stopping Public ${type} API`) - - return server.stop() - } - } - }, -} diff --git a/packages/core-api/lib/plugins/caster.js b/packages/core-api/lib/plugins/caster.js deleted file mode 100644 index 6d5db8529e..0000000000 --- a/packages/core-api/lib/plugins/caster.js +++ /dev/null @@ -1,78 +0,0 @@ -/* eslint-disable */ - -const { bignumify } = require('@arkecosystem/core-utils') - -/** - * Check if the given value is a boolean. - * @param {*} value - * @return {Boolean} - */ -function isBoolean(value) { - try { - return value.toLowerCase() === 'true' || value.toLowerCase() === 'false' - } catch (e) { - return false - } -} - -/** - * Check if the given value is a number. - * @param {*} value - * @return {Boolean} - */ -function isNumber(value) { - return !isNaN(value) -} - -/** - * @TODO - Review this module later on in the development. - * - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.ext({ - type: 'onPreHandler', - method: (request, h) => { - const query = request.query - - Object.keys(query).map((key, index) => { - // Special fields that should always be a "string" - if (key === 'id' || key === 'blockId' || key === 'previousBlock') { - query[key] = query[key] - } - // Booleans - else if (isBoolean(query[key])) { - query[key] = query[key].toLowerCase() === 'true' - } - // Integers - making sure "BigNumbers" are kept as strings - else if (isNumber(query[key])) { - query[key] = - query[key] == Number(query[key]) - ? Number(query[key]) - : bignumify(query[key]).toString() - } - // Strings - else { - query[key] = query[key] - } - }) - - request.query = query - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'core-caster', - version: '0.1.0', - register, -} diff --git a/packages/core-api/lib/plugins/endpoint-version.js b/packages/core-api/lib/plugins/endpoint-version.js deleted file mode 100644 index 83f5415841..0000000000 --- a/packages/core-api/lib/plugins/endpoint-version.js +++ /dev/null @@ -1,39 +0,0 @@ -const Boom = require('boom') - -const versionRegex = /^\/api\/v([0-9])\// - -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.ext({ - type: 'onRequest', - async method(request, h) { - const match = versionRegex.exec(request.path) - if (match && match.length === 2) { - const apiVersion = parseInt(match[1]) - if (options.validVersions.includes(apiVersion)) { - request.pre.apiVersion = apiVersion - } else { - return Boom.badRequest( - `Invalid api-version! Valid values: ${options.validVersions.join()}`, - ) - } - } - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -module.exports = { - name: 'endpoint-version', - version: '0.1.0', - register, -} diff --git a/packages/core-api/lib/plugins/set-headers.js b/packages/core-api/lib/plugins/set-headers.js deleted file mode 100644 index a0415af0be..0000000000 --- a/packages/core-api/lib/plugins/set-headers.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.ext({ - type: 'onPreResponse', - async method(request, h) { - const response = request.response - if (response.isBoom && response.data) { - // Deleting the property beforehand makes it appear last in the - // response body. - delete response.output.payload.error - response.output.payload.error = response.data - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'set-headers', - version: '0.1.0', - register, -} diff --git a/packages/core-api/lib/plugins/validation/formats/address.js b/packages/core-api/lib/plugins/validation/formats/address.js deleted file mode 100644 index f2423b6560..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/address.js +++ /dev/null @@ -1,20 +0,0 @@ -const bs58check = require('bs58check') -const config = require('@arkecosystem/core-container').resolvePlugin('config') - -/** - * Register the "address" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('address', { - type: 'string', - validate: value => { - try { - return bs58check.decode(value)[0] === config.network.pubKeyHash - } catch (e) { - return false - } - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/csv.js b/packages/core-api/lib/plugins/validation/formats/csv.js deleted file mode 100644 index 4c5fcb66cf..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/csv.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Register the "csv" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('csv', { - type: 'string', - validate: value => { - try { - const a = value.split(',') - - return a.length > 0 && a.length <= 1000 - } catch (e) { - return false - } - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/hex.js b/packages/core-api/lib/plugins/validation/formats/hex.js deleted file mode 100644 index 2fc0f03575..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/hex.js +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Register the "hex" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('hex', { - type: 'string', - validate: value => { - try { - Buffer.from(value, 'hex') - - return true - } catch (e) { - return false - } - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/ip.js b/packages/core-api/lib/plugins/validation/formats/ip.js deleted file mode 100644 index a9833fea0c..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/ip.js +++ /dev/null @@ -1,13 +0,0 @@ -const ip = require('ip') - -/** - * Register the "ip" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('ip', { - type: 'string', - validate: value => ip.isV4Format(value) || ip.isV6Format(value), - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/parsedInt.js b/packages/core-api/lib/plugins/validation/formats/parsedInt.js deleted file mode 100644 index 8c0db25163..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/parsedInt.js +++ /dev/null @@ -1,25 +0,0 @@ -/* eslint no-restricted-globals: "off" */ - -/** - * Register the "parsedInt" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('parsedInt', { - type: 'string', - validate: value => { - if ( - isNaN(value) || - parseInt(value) !== value || - isNaN(parseInt(value, 10)) - ) { - return false - } - - value = parseInt(value) - - return true - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/publicKey.js b/packages/core-api/lib/plugins/validation/formats/publicKey.js deleted file mode 100644 index 5a732a71cc..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/publicKey.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Register the "publicKey" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('publicKey', { - type: 'string', - validate: value => { - try { - return Buffer.from(value, 'hex').length === 33 - } catch (e) { - return false - } - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/signature.js b/packages/core-api/lib/plugins/validation/formats/signature.js deleted file mode 100644 index 80080ef1b3..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/signature.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Register the "signature" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('signature', { - type: 'string', - validate: value => { - try { - return Buffer.from(value, 'hex').length < 73 - } catch (e) { - return false - } - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/formats/vendorField.js b/packages/core-api/lib/plugins/validation/formats/vendorField.js deleted file mode 100644 index d78f5c2279..0000000000 --- a/packages/core-api/lib/plugins/validation/formats/vendorField.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Register the "vendorField" validation rule. - * @param {AJV} ajv - * @return {void} - */ -module.exports = ajv => { - ajv.addFormat('vendorField', { - type: 'string', - validate: value => { - try { - return Buffer.from(value).length < 65 - } catch (e) { - return false - } - }, - }) -} diff --git a/packages/core-api/lib/plugins/validation/index.js b/packages/core-api/lib/plugins/validation/index.js deleted file mode 100644 index f9a302ce7b..0000000000 --- a/packages/core-api/lib/plugins/validation/index.js +++ /dev/null @@ -1,99 +0,0 @@ -const PLUGIN_NAME = 'hapi-ajv' - -const fs = require('fs') -const path = require('path') -const Boom = require('boom') -const AJV = require('ajv') - -const ajv = new AJV() - -/** - * Validate the given data using AJV. - * @param {Object} schema - * @param {Object} data - * @return {(Boolean|Object)} - */ -function validate(schema, data) { - return ajv.validate(schema, data) ? null : ajv.errors -} - -/** - * Create an error response for hapi.js. - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @param {Array} errors - * @return {Hapi.Response} - */ -function createErrorResponse(request, h, errors) { - if (request.pre.apiVersion === 1) { - return h - .response({ - path: errors[0].dataPath, - error: errors[0].message, - success: false, - }) - .takeover() - } - return Boom.badData(errors) -} - -/** - * Register all custom validation formats - * @return {void} - */ -function registerCustomFormats() { - const directory = path.resolve(__dirname, 'formats') - - fs.readdirSync(directory).forEach(file => { - if (file.indexOf('.js') !== -1) { - require(`${directory}/${file}`)(ajv) - } - }) -} - -/** - * The register method uses by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - registerCustomFormats() - - server.ext({ - type: 'onPreHandler', - method: (request, h) => { - const config = request.route.settings.plugins[PLUGIN_NAME] || {} - - let errors - - if (config.payloadSchema) { - errors = validate(config.payloadSchema, request.payload) - - if (errors) { - return createErrorResponse(request, h, errors) - } - } - - if (config.querySchema) { - errors = validate(config.querySchema, request.query) - - if (errors) { - return createErrorResponse(request, h, errors) - } - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: PLUGIN_NAME, - version: '0.1.0', - register, -} diff --git a/packages/core-api/lib/repositories/blocks.js b/packages/core-api/lib/repositories/blocks.js deleted file mode 100644 index 82052aa393..0000000000 --- a/packages/core-api/lib/repositories/blocks.js +++ /dev/null @@ -1,159 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') - -const buildFilterQuery = require('./utils/filter-query') -const Repository = require('./repository') - -class BlocksRepository extends Repository { - /** - * Get all blocks for the given parameters. - * @param {Object} parameters - * @return {Object} - */ - async findAll(parameters = {}) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - const applyConditions = queries => { - const conditions = Object.entries(this._formatConditions(parameters)) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first[0]].equals(first[1])) - - for (const condition of conditions) { - item.and(this.query[condition[0]].equals(condition[1])) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - return this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - } - - /** - * Get all blocks for the given generator. - * @param {String} generatorPublicKey - * @param {Object} paginator - * @return {Object} - */ - async findAllByGenerator(generatorPublicKey, paginator) { - return this.findAll({ ...{ generatorPublicKey }, ...paginator }) - } - - /** - * Get a block. - * @param {Number} value - * @return {Object} - */ - async findById(value) { - const query = this.query - .select() - .from(this.query) - .where(this.query.id.equals(value)) - - // ensure that the value is not greater than 2147483647 (psql max int size) - const height = +value - if (height <= 2147483647) { - query.or(this.query.height.equals(height)) - } - - return this._find(query) - } - - /** - * Get the last block for the given generator. - * TODO is this right? - * @param {String} generatorPublicKey - * @return {Object} - */ - async findLastByPublicKey(generatorPublicKey) { - const query = this.query - .select(this.query.id, this.query.timestamp) - .from(this.query) - .where(this.query.generator_public_key.equals(generatorPublicKey)) - .order(this.query.height.desc) - - return this._find(query) - } - - /** - * Search all blocks. - * @param {Object} parameters - * @return {Object} - */ - async search(parameters) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - const applyConditions = queries => { - const conditions = buildFilterQuery(this._formatConditions(parameters), { - exact: [ - 'id', - 'version', - 'previous_block', - 'payload_hash', - 'generator_public_key', - 'block_signature', - ], - between: [ - 'timestamp', - 'height', - 'number_of_transactions', - 'total_amount', - 'total_fee', - 'reward', - 'payload_length', - ], - }) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first.column][first.method](first.value)) - - for (const condition of conditions) { - item.and( - this.query[condition.column][condition.method](condition.value), - ) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - return this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - } - - getModel() { - return database.models.block - } - - __orderBy(parameters) { - if (!parameters.orderBy) return ['height', 'desc'] - - const orderBy = parameters.orderBy.split(':').map(p => p.toLowerCase()) - if (orderBy.length !== 2 || ['desc', 'asc'].includes(orderBy[1]) !== true) { - return ['height', 'desc'] - } - - return orderBy - } -} - -module.exports = new BlocksRepository() diff --git a/packages/core-api/lib/repositories/index.js b/packages/core-api/lib/repositories/index.js deleted file mode 100644 index 5724a9e9b4..0000000000 --- a/packages/core-api/lib/repositories/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - blocks: require('./blocks'), - transactions: require('./transactions'), -} diff --git a/packages/core-api/lib/repositories/repository.js b/packages/core-api/lib/repositories/repository.js deleted file mode 100644 index 59af86586c..0000000000 --- a/packages/core-api/lib/repositories/repository.js +++ /dev/null @@ -1,82 +0,0 @@ -const snakeCase = require('lodash/snakeCase') -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') - -module.exports = class Repository { - constructor() { - this.cache = database.getCache() - this.model = this.getModel() - this.query = this.model.query() - - this.__mapColumns() - } - - async _find(query) { - return database.query.oneOrNone(query.toQuery()) - } - - async _findMany(query) { - return database.query.manyOrNone(query.toQuery()) - } - - async _findManyWithCount( - selectQuery, - countQuery, - { limit, offset, orderBy }, - ) { - const { count } = await this._find(countQuery) - - if (this.columns.includes(orderBy[0])) { - selectQuery.order(this.query[snakeCase(orderBy[0])][orderBy[1]]) - } - - selectQuery.offset(offset).limit(limit) - - return { - rows: await this._findMany(selectQuery), - count: +count, - } - } - - _makeCountQuery() { - return this.query.select('count(*) AS count').from(this.query) - } - - _makeEstimateQuery() { - return this.query - .select('count(*) AS count') - .from(`${this.model.getTable()} TABLESAMPLE SYSTEM (100)`) - } - - _formatConditions(parameters) { - const columns = this.model.getColumnSet().columns.map(column => ({ - name: column.name, - prop: column.prop || column.name, - })) - - return Object.keys(parameters) - .filter(arg => this.columns.includes(arg)) - .reduce((items, item) => { - const column = columns.find( - value => value.name === item || value.prop === item, - ) - - column ? (items[column.name] = parameters[item]) : delete items[item] - - return items - }, {}) - } - - __mapColumns() { - this.columns = [] - - for (const column of this.model.getColumnSet().columns) { - this.columns.push(column.name) - - if (column.prop) { - this.columns.push(column.prop) - } - } - } -} diff --git a/packages/core-api/lib/repositories/transactions.js b/packages/core-api/lib/repositories/transactions.js deleted file mode 100644 index 74137f7f0b..0000000000 --- a/packages/core-api/lib/repositories/transactions.js +++ /dev/null @@ -1,457 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') - -const dayjs = require('dayjs-ext') -const { slots } = require('@arkecosystem/crypto') -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants -const buildFilterQuery = require('./utils/filter-query') -const Repository = require('./repository') - -class TransactionsRepository extends Repository { - /** - * Get all transactions. - * @param {Object} params - * @return {Object} - */ - async findAll(parameters = {}) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - if (parameters.senderId) { - const senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId) - - if (!senderPublicKey) { - return { rows: [], count: 0 } - } - - parameters.senderPublicKey = senderPublicKey - } - - const applyConditions = queries => { - const conditions = Object.entries(this._formatConditions(parameters)) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first[0]].equals(first[1])) - - for (const condition of conditions) { - item.and(this.query[condition[0]].equals(condition[1])) - } - } - } - - for (const item of queries) { - if (parameters.ownerId) { - const owner = database.walletManager.findByAddress(parameters.ownerId) - - item.and(this.query.sender_public_key.equals(owner.publicKey)) - item.or(this.query.recipient_id.equals(owner.address)) - } - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - /** - * Get all transactions (LEGACY, for V1 only). - * @param {Object} params - * @return {Object} - */ - async findAllLegacy(parameters = {}) { - const selectQuery = this.query - .select(this.query.block_id, this.query.serialized, this.query.timestamp) - .from(this.query) - const countQuery = this._makeEstimateQuery() - - if (parameters.senderId) { - parameters.senderPublicKey = this.__publicKeyFromSenderId( - parameters.senderId, - ) - } - - const applyConditions = queries => { - const conditions = Object.entries(this._formatConditions(parameters)) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first[0]].equals(first[1])) - - for (const [key, value] of conditions) { - item.or(this.query[key].equals(value)) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - /** - * Get all transactions for the given Wallet object. - * @param {Wallet} wallet - * @param {Object} parameters - * @return {Object} - */ - async findAllByWallet(wallet, parameters = {}) { - const selectQuery = this.query - .select(this.query.block_id, this.query.serialized, this.query.timestamp) - .from(this.query) - const countQuery = this._makeEstimateQuery() - - const applyConditions = queries => { - for (const item of queries) { - item - .where(this.query.sender_public_key.equals(wallet.publicKey)) - .or(this.query.recipient_id.equals(wallet.address)) - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - /** - * Get all transactions for the given sender public key. - * @param {String} senderPublicKey - * @param {Object} parameters - * @return {Object} - */ - async findAllBySender(senderPublicKey, parameters = {}) { - return this.findAll({ ...{ senderPublicKey }, ...parameters }) - } - - /** - * Get all transactions for the given recipient address. - * @param {String} recipientId - * @param {Object} parameters - * @return {Object} - */ - async findAllByRecipient(recipientId, parameters = {}) { - return this.findAll({ ...{ recipientId }, ...parameters }) - } - - /** - * Get all vote transactions for the given sender public key. - * TODO rename to findAllVotesBySender or not? - * @param {String} senderPublicKey - * @param {Object} parameters - * @return {Object} - */ - async allVotesBySender(senderPublicKey, parameters = {}) { - return this.findAll({ - ...{ senderPublicKey, type: TRANSACTION_TYPES.VOTE }, - ...parameters, - }) - } - - /** - * Get all transactions for the given block. - * @param {Number} blockId - * @param {Object} parameters - * @return {Object} - */ - async findAllByBlock(blockId, parameters = {}) { - return this.findAll({ ...{ blockId }, ...parameters }) - } - - /** - * Get all transactions for the given type. - * @param {Number} type - * @param {Object} parameters - * @return {Object} - */ - async findAllByType(type, parameters = {}) { - return this.findAll({ ...{ type }, ...parameters }) - } - - /** - * Get a transaction. - * @param {Number} id - * @return {Object} - */ - async findById(id) { - const query = this.query - .select(this.query.block_id, this.query.serialized, this.query.timestamp) - .from(this.query) - .where(this.query.id.equals(id)) - - const transaction = await this._find(query) - - return this.__mapBlocksToTransactions(transaction) - } - - /** - * Get a transactions for the given type and id. - * @param {Number} type - * @param {Number} id - * @return {Object} - */ - async findByTypeAndId(type, id) { - const query = this.query - .select(this.query.block_id, this.query.serialized, this.query.timestamp) - .from(this.query) - .where(this.query.id.equals(id).and(this.query.type.equals(type))) - - const transaction = await this._find(query) - - return this.__mapBlocksToTransactions(transaction) - } - - /** - * Get transactions for the given ids. - * @param {Array} ids - * @return {Object} - */ - async findByIds(ids) { - const query = this.query - .select(this.query.block_id, this.query.serialized, this.query.timestamp) - .from(this.query) - .where(this.query.id.in(ids)) - - return this._findMany(query) - } - - /** - * Get all transactions that have a vendor field. - * @return {Object} - */ - async findWithVendorField() { - const query = this.query - .select(this.query.block_id, this.query.serialized, this.query.timestamp) - .from(this.query) - .where(this.query.vendor_field_hex.isNotNull()) - - const transactions = await this._findMany(query) - - return this.__mapBlocksToTransactions(transactions) - } - - /** - * Calculates min, max and average fee statistics based on transactions table - * @return {Object} - */ - async getFeeStatistics() { - const query = this.query - .select( - this.query.type, - this.query.fee.min('minFee'), - this.query.fee.max('maxFee'), - this.query.fee.avg('avgFee'), - this.query.timestamp.max('timestamp'), - ) - .from(this.query) - .where( - this.query.timestamp.gte(slots.getTime(dayjs().subtract(30, 'days'))), - ) - .group(this.query.type) - .order('"timestamp" DESC') - - return this._findMany(query) - } - - /** - * Search all transactions. - * - * @param {Object} params - * @return {Object} - */ - async search(parameters) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - if (parameters.senderId) { - const senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId) - - if (senderPublicKey) { - parameters.senderPublicKey = senderPublicKey - } - } - - const applyConditions = queries => { - const conditions = buildFilterQuery(this._formatConditions(parameters), { - exact: [ - 'id', - 'block_id', - 'type', - 'version', - 'sender_public_key', - 'recipient_id', - ], - between: ['timestamp', 'amount', 'fee'], - wildcard: ['vendor_field_hex'], - }) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first.column][first.method](first.value)) - - for (const condition of conditions) { - item.and( - this.query[condition.column][condition.method](condition.value), - ) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit || 100, - offset: parameters.offset || 0, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - getModel() { - return database.models.transaction - } - - /** - * [__mapBlocksToTransactions description] - * @param {Array|Object} data - * @return {Object} - */ - async __mapBlocksToTransactions(data) { - const blockQuery = database.models.block.query() - - // Array... - if (Array.isArray(data)) { - // 1. get heights from cache - const missingFromCache = [] - - for (let i = 0; i < data.length; i++) { - const cachedBlock = this.__getBlockCache(data[i].blockId) - - if (cachedBlock) { - data[i].block = cachedBlock - } else { - missingFromCache.push({ - index: i, - blockId: data[i].blockId, - }) - } - } - - // 2. get missing heights from database - if (missingFromCache.length) { - const query = blockQuery - .select(blockQuery.id, blockQuery.height) - .from(blockQuery) - .where(blockQuery.id.in(missingFromCache.map(d => d.blockId))) - .group(blockQuery.id) - - const blocks = await this._findMany(query) - - for (const missing of missingFromCache) { - const block = blocks.find(item => item.id === missing.blockId) - if (block) { - data[missing.index].block = block - this.__setBlockCache(block) - } - } - } - - return data - } - - // Object... - if (data) { - const cachedBlock = this.__getBlockCache(data.blockId) - - if (cachedBlock) { - data.block = cachedBlock - } else { - const query = blockQuery - .select(blockQuery.id, blockQuery.height) - .from(blockQuery) - .where(blockQuery.id.equals(data.blockId)) - - data.block = await this._find(query) - - this.__setBlockCache(data.block) - } - } - - return data - } - - /** - * Tries to retrieve the height of the block from the cache - * @param {String} blockId - * @return {Object|null} - */ - __getBlockCache(blockId) { - const height = this.cache.get(`heights:${blockId}`) - - return height ? { height, id: blockId } : null - } - - /** - * Stores the height of the block on the cache - * @param {Object} block - * @param {String} block.id - * @param {Number} block.height - */ - __setBlockCache({ id, height }) { - this.cache.set(`heights:${id}`, height) - } - - /** - * Retrieves the publicKey of the address from the WalletManager in-memory data - * @param {String} senderId - * @return {String} - */ - __publicKeyFromSenderId(senderId) { - return database.walletManager.findByAddress(senderId).publicKey - } - - __orderBy(parameters) { - return parameters.orderBy - ? parameters.orderBy.split(':').map(p => p.toLowerCase()) - : ['timestamp', 'desc'] - } -} - -module.exports = new TransactionsRepository() diff --git a/packages/core-api/lib/repositories/utils/filter-query.js b/packages/core-api/lib/repositories/utils/filter-query.js deleted file mode 100644 index 0e2614354b..0000000000 --- a/packages/core-api/lib/repositories/utils/filter-query.js +++ /dev/null @@ -1,79 +0,0 @@ -/* eslint no-prototype-builtins: "off" */ - -/** - * Create a "where" object for a sql query. - * @param {Object} parameters - * @param {Object} filters - * @return {Object} - */ -module.exports = (parameters, filters) => { - const where = [] - - if (filters.hasOwnProperty('exact')) { - for (const elem of filters.exact) { - if (typeof parameters[elem] !== 'undefined') { - where.push({ - column: elem, - method: 'equals', - value: parameters[elem], - }) - } - } - } - - if (filters.hasOwnProperty('between')) { - for (const elem of filters.between) { - if (!parameters[elem]) { - continue - } - - if ( - !parameters[elem].hasOwnProperty('from') && - !parameters[elem].hasOwnProperty('to') - ) { - where.push({ - column: elem, - method: 'equals', - value: parameters[elem], - }) - } - - if ( - parameters[elem].hasOwnProperty('from') || - parameters[elem].hasOwnProperty('to') - ) { - where[elem] = {} - - if (parameters[elem].hasOwnProperty('from')) { - where.push({ - column: elem, - method: 'gte', - value: parameters[elem].from, - }) - } - - if (parameters[elem].hasOwnProperty('to')) { - where.push({ - column: elem, - method: 'lte', - value: parameters[elem].to, - }) - } - } - } - } - - if (filters.hasOwnProperty('wildcard')) { - for (const elem of filters.wildcard) { - if (parameters[elem]) { - where.push({ - column: elem, - method: 'like', - value: `%${parameters[elem]}%`, - }) - } - } - } - - return where -} diff --git a/packages/core-api/lib/server.js b/packages/core-api/lib/server.js deleted file mode 100644 index 81f8088683..0000000000 --- a/packages/core-api/lib/server.js +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const { - createServer, - createSecureServer, - mountServer, - plugins, -} = require('@arkecosystem/core-http-utils') - -/** - * Create a new hapi.js server. - * @param {Object} config - * @return {Hapi.Server} - */ -module.exports = async config => { - const options = { - host: config.host, - port: config.port, - routes: { - cors: { - additionalHeaders: ['api-version'], - }, - validate: { - async failAction(request, h, err) { - throw err - }, - }, - }, - } - - const servers = { http: await createServer(options) } - - if (config.ssl.enabled) { - servers.https = await createSecureServer(options, null, config.ssl) - } - - for (const [type, server] of Object.entries(servers)) { - // TODO: enable after mainnet migration - // await server.register({ plugin: plugins.contentType }) - - await server.register({ - plugin: plugins.corsHeaders, - }) - - await server.register({ - plugin: plugins.transactionPayload, - options: { - routes: [ - { - method: 'POST', - path: '/api/v2/transactions', - }, - ], - }, - }) - - await server.register({ - plugin: plugins.whitelist, - options: { - whitelist: config.whitelist, - name: 'Public API', - }, - }) - - await server.register({ - plugin: require('./plugins/set-headers'), - }) - - await server.register({ - plugin: require('hapi-api-version'), - options: config.versions, - }) - - await server.register({ - plugin: require('./plugins/endpoint-version'), - options: { validVersions: config.versions.validVersions }, - }) - - await server.register({ - plugin: require('./plugins/caster'), - }) - - await server.register({ - plugin: require('./plugins/validation'), - }) - - await server.register({ - plugin: require('hapi-rate-limit'), - options: config.rateLimit, - }) - - await server.register({ - plugin: require('hapi-pagination'), - options: { - meta: { - baseUri: '', - }, - query: { - limit: { - default: config.pagination.limit, - }, - }, - results: { - name: 'data', - }, - routes: { - include: config.pagination.include, - exclude: ['*'], - }, - }, - }) - - for (const plugin of config.plugins) { - if (typeof plugin.plugin === 'string') { - plugin.plugin = require(plugin.plugin) - } - - await server.register(plugin) - } - - await mountServer(`Public ${type} API`, server) - } - - return servers -} diff --git a/packages/core-api/lib/utils/generate-cache-key.js b/packages/core-api/lib/utils/generate-cache-key.js deleted file mode 100644 index 9c72414078..0000000000 --- a/packages/core-api/lib/utils/generate-cache-key.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = value => - require('crypto') - .createHash('sha256') - .update(JSON.stringify(value)) - .digest('hex') diff --git a/packages/core-api/lib/utils/transformer.js b/packages/core-api/lib/utils/transformer.js deleted file mode 100644 index af05e063c4..0000000000 --- a/packages/core-api/lib/utils/transformer.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint max-len: "off" */ - -const path = require('path') - -/** - * Transform the given data to a resource. - * @param {Hapi.Request} request - * @param {Object} data - * @param {Object} transformer - * @return {Object} - */ -const transformResource = (request, data, transformer) => - require(path.resolve( - __dirname, - `../versions/${request.pre.apiVersion}/transformers/${transformer}`, - ))(data) - -/** - * Transform the given data to a collection. - * @param {Hapi.Request} request - * @param {Object} data - * @param {Object} transformer - * @return {Object} - */ -const transformCollection = (request, data, transformer) => - data.map(d => transformResource(request, d, transformer)) - -/** - * @type {Object} - */ -module.exports = { - transformResource, - transformCollection, -} diff --git a/packages/core-api/lib/versions/1/handlers/accounts.js b/packages/core-api/lib/versions/1/handlers/accounts.js deleted file mode 100644 index e3806f558d..0000000000 --- a/packages/core-api/lib/versions/1/handlers/accounts.js +++ /dev/null @@ -1,193 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const database = app.resolvePlugin('database') -const blockchain = app.resolvePlugin('blockchain') - -const utils = require('../utils') -const schema = require('../schemas/accounts') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.accounts.index(request) - - return utils.respondWithCache(data, h) - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.accounts.show(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getAccount, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.balance = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.accounts.balance(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getBalance, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.publicKey = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.accounts.publicKey(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getPublicKey, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.fee = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - fee: config.getConstants(blockchain.getLastBlock().data.height).fees - .staticFees.delegateRegistration, - }) - }, -} - -/** - * @type {Object} - */ -exports.delegates = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const account = await database.wallets.findById(request.query.address) - - if (!account) { - return utils.respondWith('Address not found.', true) - } - - if (!account.vote) { - return utils.respondWith( - `Address ${request.query.address} hasn't voted yet.`, - true, - ) - } - - const delegate = await database.delegates.findById(account.vote) - - return utils.respondWith({ - delegates: [utils.toResource(request, delegate, 'delegate')], - }) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getDelegates, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.top = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - let accounts = database.wallets.top(utils.paginate(request)) - - accounts = accounts.rows.map(account => ({ - address: account.address, - balance: `${account.balance}`, - publicKey: account.publicKey, - })) - - return utils.respondWith({ accounts }) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.top, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.count = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const { count } = await database.wallets.findAll() - - return utils.respondWith({ count }) - }, -} diff --git a/packages/core-api/lib/versions/1/handlers/blocks.js b/packages/core-api/lib/versions/1/handlers/blocks.js deleted file mode 100644 index e8f64c1ec1..0000000000 --- a/packages/core-api/lib/versions/1/handlers/blocks.js +++ /dev/null @@ -1,216 +0,0 @@ -const app = require('@arkecosystem/core-container') -const { supplyCalculator } = require('@arkecosystem/core-utils') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') - -const utils = require('../utils') -const schema = require('../schemas/blocks') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.blocks.index(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getBlocks, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.blocks.show(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getBlock, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.epoch = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - epoch: config.getConstants(blockchain.getLastBlock().data.height).epoch, - }) - }, -} - -/** - * @type {Object} - */ -exports.height = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const block = blockchain.getLastBlock() - - return utils.respondWith({ height: block.data.height, id: block.data.id }) - }, -} - -/** - * @type {Object} - */ -exports.nethash = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ nethash: config.network.nethash }) - }, -} - -/** - * @type {Object} - */ -exports.fee = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - fee: config.getConstants(blockchain.getLastBlock().data.height).fees - .staticFees.transfer, - }) - }, -} - -/** - * @type {Object} - */ -exports.fees = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const fees = config.getConstants(blockchain.getLastBlock().data.height).fees - .staticFees - - return utils.respondWith({ - fees: { - send: fees.transfer, - vote: fees.vote, - secondsignature: fees.secondSignature, - delegate: fees.delegateRegistration, - multisignature: fees.multiSignature, - }, - }) - }, -} - -/** - * @type {Object} - */ -exports.milestone = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - milestone: Math.floor(blockchain.getLastBlock().data.height / 3000000), - }) - }, -} - -/** - * @type {Object} - */ -exports.reward = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - reward: config.getConstants(blockchain.getLastBlock().data.height).reward, - }) - }, -} - -/** - * @type {Object} - */ -exports.supply = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = blockchain.getLastBlock() - return utils.respondWith({ - supply: supplyCalculator.calculate(lastBlock.data.height), - }) - }, -} - -/** - * @type {Object} - */ -exports.status = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = blockchain.getLastBlock() - const constants = config.getConstants(lastBlock.data.height) - - return utils.respondWith({ - epoch: constants.epoch, - height: lastBlock.data.height, - fee: constants.fees.staticFees.transfer, - milestone: Math.floor(lastBlock.data.height / 3000000), - nethash: config.network.nethash, - reward: constants.reward, - supply: supplyCalculator.calculate(lastBlock.data.height), - }) - }, -} diff --git a/packages/core-api/lib/versions/1/handlers/delegates.js b/packages/core-api/lib/versions/1/handlers/delegates.js deleted file mode 100644 index 87cc25d3ee..0000000000 --- a/packages/core-api/lib/versions/1/handlers/delegates.js +++ /dev/null @@ -1,201 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const database = app.resolvePlugin('database') -const blockchain = app.resolvePlugin('blockchain') -const { slots } = require('@arkecosystem/crypto') - -const utils = require('../utils') -const schema = require('../schemas/delegates') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.delegates.index(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getDelegates, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.delegates.show(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getDelegate, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.count = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.delegates.count(request) - - return utils.respondWithCache(data, h) - }, -} - -/** - * @type {Object} - */ -exports.search = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.delegates.search(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.search, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.voters = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.delegates.voters(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getVoters, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.fee = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - fee: config.getConstants(blockchain.getLastBlock().data.height).fees - .staticFees.delegateRegistration, - }) - }, -} - -/** - * @type {Object} - */ -exports.forged = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const wallet = database.walletManager.findByPublicKey( - request.query.generatorPublicKey, - ) - - return utils.respondWith({ - fees: Number(wallet.forgedFees), - rewards: Number(wallet.forgedRewards), - forged: Number(wallet.forgedFees) + Number(wallet.forgedRewards), - }) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getForgedByAccount, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.nextForgers = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const lastBlock = blockchain.getLastBlock() - const limit = request.query.limit || 10 - - const delegatesCount = config.getConstants(lastBlock).activeDelegates - const currentSlot = slots.getSlotNumber(lastBlock.data.timestamp) - - let activeDelegates = await database.getActiveDelegates( - lastBlock.data.height, - ) - activeDelegates = activeDelegates.map(delegate => delegate.publicKey) - - const nextForgers = [] - for (let i = 1; i <= delegatesCount && i <= limit; i++) { - const delegate = activeDelegates[(currentSlot + i) % delegatesCount] - - if (delegate) { - nextForgers.push(delegate) - } - } - - return utils.respondWith({ - currentBlock: lastBlock.data.height, - currentSlot, - delegates: nextForgers, - }) - }, -} diff --git a/packages/core-api/lib/versions/1/handlers/loader.js b/packages/core-api/lib/versions/1/handlers/loader.js deleted file mode 100644 index 693991de95..0000000000 --- a/packages/core-api/lib/versions/1/handlers/loader.js +++ /dev/null @@ -1,80 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') -const utils = require('../utils') -const { transactions } = require('../../../repositories') - -/** - * @type {Object} - */ -exports.status = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = blockchain.getLastBlock() - - return utils.respondWith({ - loaded: blockchain.isSynced(), - now: lastBlock ? lastBlock.data.height : 0, - blocksCount: - blockchain.p2p.getNetworkHeight() - lastBlock - ? lastBlock.data.height - : 0, - }) - }, -} - -/** - * @type {Object} - */ -exports.syncing = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = blockchain.getLastBlock() - - return utils.respondWith({ - syncing: !blockchain.isSynced(), - blocks: blockchain.p2p.getNetworkHeight() - lastBlock.data.height, - height: lastBlock.data.height, - id: lastBlock.data.id, - }) - }, -} - -/** - * @type {Object} - */ -exports.autoconfigure = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const feeStatisticsData = await transactions.getFeeStatistics() - - return utils.respondWith({ - network: { - nethash: config.network.nethash, - token: config.network.client.token, - symbol: config.network.client.symbol, - explorer: config.network.client.explorer, - version: config.network.pubKeyHash, - ports: utils.toResource(request, config, 'ports'), - feeStatistics: utils.toCollection( - request, - feeStatisticsData, - 'fee-statistics', - ), - }, - }) - }, -} diff --git a/packages/core-api/lib/versions/1/handlers/peers.js b/packages/core-api/lib/versions/1/handlers/peers.js deleted file mode 100644 index 27c95c6378..0000000000 --- a/packages/core-api/lib/versions/1/handlers/peers.js +++ /dev/null @@ -1,127 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const p2p = app.resolvePlugin('p2p') - -const utils = require('../utils') -const schema = require('../schemas/peers') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const allPeers = await p2p.getPeers() - - if (!allPeers) { - return utils.respondWith('No peers found', true) - } - - let peers = allPeers - .map(peer => { - // just use 'OK' status for API instead of p2p http status codes - peer.status = peer.status === 200 ? 'OK' : peer.status - return peer - }) - .sort((a, b) => a.delay - b.delay) - peers = request.query.os - ? allPeers.filter(peer => peer.os === request.query.os) - : peers - peers = request.query.status - ? allPeers.filter(peer => peer.status === request.query.status) - : peers - peers = request.query.port - ? allPeers.filter(peer => peer.port === request.query.port) - : peers - peers = request.query.version - ? allPeers.filter(peer => peer.version === request.query.version) - : peers - peers = peers.slice(0, request.query.limit || 100) - - if (request.query.orderBy) { - const order = request.query.orderBy.split(':') - if (['port', 'status', 'os', 'version'].includes(order[0])) { - peers = - order[1].toUpperCase() === 'ASC' - ? peers.sort((a, b) => a[order[0]] - b[order[0]]) - : peers.sort((a, b) => a[order[0]] + b[order[0]]) - } - } - - return utils.respondWith({ - peers: utils.toCollection( - request, - peers.map(peer => peer.toBroadcastInfo()), - 'peer', - ), - }) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getPeers, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const peers = await p2p.getPeers() - - if (!peers) { - return utils.respondWith('No peers found', true) - } - - const peer = peers.find( - elem => - elem.ip === request.query.ip && +elem.port === +request.query.port, - ) - - if (!peer) { - return utils.respondWith( - `Peer ${request.query.ip}:${request.query.port} not found`, - true, - ) - } - - return utils.respondWith({ - peer: utils.toResource(request, peer.toBroadcastInfo(), 'peer'), - }) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getPeer, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.version = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - version: app.getVersion(), - }) - }, -} diff --git a/packages/core-api/lib/versions/1/handlers/signatures.js b/packages/core-api/lib/versions/1/handlers/signatures.js deleted file mode 100644 index 5a32cea6b0..0000000000 --- a/packages/core-api/lib/versions/1/handlers/signatures.js +++ /dev/null @@ -1,23 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') - -const utils = require('../utils') - -/** - * @type {Object} - */ -exports.fee = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return utils.respondWith({ - fee: config.getConstants(blockchain.getLastBlock().data.height).fees - .staticFees.secondSignature, - }) - }, -} diff --git a/packages/core-api/lib/versions/1/handlers/transactions.js b/packages/core-api/lib/versions/1/handlers/transactions.js deleted file mode 100644 index 9ac43c1d87..0000000000 --- a/packages/core-api/lib/versions/1/handlers/transactions.js +++ /dev/null @@ -1,106 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const transactionPool = app.resolvePlugin('transactionPool') - -const utils = require('../utils') -const schema = require('../schemas/transactions') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.transactions.index(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getTransactions, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v1.transactions.show(request) - - return utils.respondWithCache(data, h) - }, - config: { - plugins: { - 'hapi-ajv': { - querySchema: schema.getTransaction, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.unconfirmed = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const pagination = utils.paginate(request) - - let transactions = transactionPool.getTransactions( - pagination.offset, - pagination.limit, - ) - transactions = transactions.map(transaction => ({ - serialized: transaction, - })) - - return utils.respondWith({ - transactions: utils.toCollection(request, transactions, 'transaction'), - }) - }, -} - -/** - * @type {Object} - */ -exports.showUnconfirmed = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const transaction = transactionPool.getTransaction(request.query.id) - - if (!transaction) { - return utils.respondWith('Transaction not found', true) - } - - return utils.respondWith({ - transaction: utils.toResource( - request, - { - serialized: transaction.serialized, - }, - 'transaction', - ), - }) - }, -} diff --git a/packages/core-api/lib/versions/1/index.js b/packages/core-api/lib/versions/1/index.js deleted file mode 100644 index 45ef7237a5..0000000000 --- a/packages/core-api/lib/versions/1/index.js +++ /dev/null @@ -1,100 +0,0 @@ -const blocks = require('./handlers/blocks') -const delegates = require('./handlers/delegates') -const loader = require('./handlers/loader') -const peers = require('./handlers/peers') -const signatures = require('./handlers/signatures') -const transactions = require('./handlers/transactions') -const accounts = require('./handlers/accounts') - -const registerAccountMethods = require('./methods/accounts') -const registerBlockMethods = require('./methods/blocks') -const registerDelegateMethods = require('./methods/delegates') -const registerTransactionMethods = require('./methods/transactions') - -/** - * Register the v1 routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - registerAccountMethods(server) - registerBlockMethods(server) - registerDelegateMethods(server) - registerTransactionMethods(server) - - server.route([ - { method: 'GET', path: '/accounts/getAllAccounts', ...accounts.index }, - { method: 'GET', path: '/accounts', ...accounts.show }, - { method: 'GET', path: '/accounts/getBalance', ...accounts.balance }, - { method: 'GET', path: '/accounts/getPublicKey', ...accounts.publicKey }, - { method: 'GET', path: '/accounts/delegates/fee', ...accounts.fee }, - { method: 'GET', path: '/accounts/delegates', ...accounts.delegates }, - { method: 'GET', path: '/accounts/top', ...accounts.top }, - { method: 'GET', path: '/accounts/count', ...accounts.count }, - - { method: 'GET', path: '/blocks', ...blocks.index }, - { method: 'GET', path: '/blocks/get', ...blocks.show }, - { method: 'GET', path: '/blocks/getEpoch', ...blocks.epoch }, - { method: 'GET', path: '/blocks/getHeight', ...blocks.height }, - { method: 'GET', path: '/blocks/getheight', ...blocks.height }, // desktop wallet inconsistency - { method: 'GET', path: '/blocks/getNethash', ...blocks.nethash }, - { method: 'GET', path: '/blocks/getFee', ...blocks.fee }, - { method: 'GET', path: '/blocks/getFees', ...blocks.fees }, - { method: 'GET', path: '/blocks/getfees', ...blocks.fees }, // desktop wallet inconsistency - { method: 'GET', path: '/blocks/getMilestone', ...blocks.milestone }, - { method: 'GET', path: '/blocks/getReward', ...blocks.reward }, - { method: 'GET', path: '/blocks/getSupply', ...blocks.supply }, - { method: 'GET', path: '/blocks/getStatus', ...blocks.status }, - - { method: 'GET', path: '/delegates', ...delegates.index }, - { method: 'GET', path: '/delegates/get', ...delegates.show }, - { method: 'GET', path: '/delegates/count', ...delegates.count }, - { method: 'GET', path: '/delegates/search', ...delegates.search }, - { method: 'GET', path: '/delegates/voters', ...delegates.voters }, - { method: 'GET', path: '/delegates/fee', ...delegates.fee }, - { - method: 'GET', - path: '/delegates/forging/getForgedByAccount', - ...delegates.forged, - }, - { - method: 'GET', - path: '/delegates/getNextForgers', - ...delegates.nextForgers, - }, - - { method: 'GET', path: '/loader/status', ...loader.status }, - { method: 'GET', path: '/loader/status/sync', ...loader.syncing }, - { method: 'GET', path: '/loader/autoconfigure', ...loader.autoconfigure }, - - { method: 'GET', path: '/peers', ...peers.index }, - { method: 'GET', path: '/peers/get', ...peers.show }, - { method: 'GET', path: '/peers/version', ...peers.version }, - - { method: 'GET', path: '/signatures/fee', ...signatures.fee }, - - { method: 'GET', path: '/transactions', ...transactions.index }, - { method: 'GET', path: '/transactions/get', ...transactions.show }, - { - method: 'GET', - path: '/transactions/unconfirmed', - ...transactions.unconfirmed, - }, - { - method: 'GET', - path: '/transactions/unconfirmed/get', - ...transactions.showUnconfirmed, - }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark Public API - v1', - version: '0.1.0', - register, -} diff --git a/packages/core-api/lib/versions/1/methods/accounts.js b/packages/core-api/lib/versions/1/methods/accounts.js deleted file mode 100644 index a3533391d7..0000000000 --- a/packages/core-api/lib/versions/1/methods/accounts.js +++ /dev/null @@ -1,98 +0,0 @@ -const app = require('@arkecosystem/core-container') -const generateCacheKey = require('../../../utils/generate-cache-key') -const utils = require('../utils') - -const database = app.resolvePlugin('database') - -const index = async request => { - const { rows } = await database.wallets.findAll({ - ...request.query, - ...utils.paginate(request), - }) - - return utils.respondWith({ - accounts: utils.toCollection(request, rows, 'account'), - }) -} - -const show = async request => { - const account = await database.wallets.findById(request.query.address) - - if (!account) { - return utils.respondWith('Account not found', true) - } - - return utils.respondWith({ - account: utils.toResource(request, account, 'account'), - }) -} - -const balance = async request => { - const account = await database.wallets.findById(request.query.address) - - if (!account) { - return utils.respondWith({ balance: '0', unconfirmedBalance: '0' }) - } - - return utils.respondWith({ - balance: account ? `${account.balance}` : '0', - unconfirmedBalance: account ? `${account.balance}` : '0', - }) -} - -const publicKey = async request => { - const account = await database.wallets.findById(request.query.address) - - if (!account) { - return utils.respondWith('Account not found', true) - } - - return utils.respondWith({ publicKey: account.publicKey }) -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v1.accounts.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v1.accounts.show', show, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ address: request.query.address }), - }) - - server.method('v1.accounts.balance', balance, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ address: request.query.address }), - }) - - server.method('v1.accounts.publicKey', publicKey, { - cache: { - expiresIn: 600 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ address: request.query.address }), - }) -} diff --git a/packages/core-api/lib/versions/1/methods/blocks.js b/packages/core-api/lib/versions/1/methods/blocks.js deleted file mode 100644 index 3a3a12a477..0000000000 --- a/packages/core-api/lib/versions/1/methods/blocks.js +++ /dev/null @@ -1,60 +0,0 @@ -const generateCacheKey = require('../../../utils/generate-cache-key') -const { blocks: blocksRepository } = require('../../../repositories') -const utils = require('../utils') - -const index = async request => { - const { count, rows } = await blocksRepository.findAll({ - ...request.query, - ...utils.paginate(request), - }) - - if (!rows) { - return utils.respondWith('No blocks found', true) - } - - return utils.respondWith({ - blocks: utils.toCollection(request, rows, 'block'), - count, - }) -} - -const show = async request => { - const block = await blocksRepository.findById(request.query.id) - - if (!block) { - return utils.respondWith( - `Block with id ${request.query.id} not found`, - true, - ) - } - - return utils.respondWith({ - block: utils.toResource(request, block, 'block'), - }) -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v1.blocks.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v1.blocks.show', show, { - cache: { - expiresIn: 600 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.query.id }), - }) -} diff --git a/packages/core-api/lib/versions/1/methods/delegates.js b/packages/core-api/lib/versions/1/methods/delegates.js deleted file mode 100644 index 89ff3b62b7..0000000000 --- a/packages/core-api/lib/versions/1/methods/delegates.js +++ /dev/null @@ -1,134 +0,0 @@ -const app = require('@arkecosystem/core-container') -const generateCacheKey = require('../../../utils/generate-cache-key') -const utils = require('../utils') - -const database = app.resolvePlugin('database') - -const index = async request => { - const { count, rows } = await database.delegates.paginate({ - ...request.query, - ...{ - offset: request.query.offset || 0, - limit: request.query.limit || 51, - }, - }) - - return utils.respondWith({ - delegates: utils.toCollection(request, rows, 'delegate'), - totalCount: count, - }) -} - -const show = async request => { - if (!request.query.publicKey && !request.query.username) { - return utils.respondWith('Delegate not found', true) - } - - const delegate = await database.delegates.findById( - request.query.publicKey || request.query.username, - ) - - if (!delegate) { - return utils.respondWith('Delegate not found', true) - } - - return utils.respondWith({ - delegate: utils.toResource(request, delegate, 'delegate'), - }) -} - -const count = async request => { - const delegate = await database.delegates.findAll() - - return utils.respondWith({ count: delegate.count }) -} - -const search = async request => { - const { rows } = await database.delegates.search({ - ...{ username: request.query.q }, - ...utils.paginate(request), - }) - - return utils.respondWith({ - delegates: utils.toCollection(request, rows, 'delegate'), - }) -} - -const voters = async request => { - const delegate = await database.delegates.findById(request.query.publicKey) - - if (!delegate) { - return utils.respondWith({ - accounts: [], - }) - } - - const accounts = await database.wallets.findAllByVote(delegate.publicKey) - - return utils.respondWith({ - accounts: utils.toCollection(request, accounts.rows, 'voter'), - }) -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v1.delegates.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...{ - offset: request.query.offset || 0, - limit: request.query.limit || 51, - }, - }), - }) - - server.method('v1.delegates.show', show, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - id: request.query.publicKey || request.query.username, - }), - }) - - server.method('v1.delegates.count', count, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ time: +new Date() }), - }) - - server.method('v1.delegates.search', search, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...{ username: request.query.q }, - ...utils.paginate(request), - }), - }) - - server.method('v1.delegates.voters', voters, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.query.publicKey }), - }) -} diff --git a/packages/core-api/lib/versions/1/methods/transactions.js b/packages/core-api/lib/versions/1/methods/transactions.js deleted file mode 100644 index 6a160100f5..0000000000 --- a/packages/core-api/lib/versions/1/methods/transactions.js +++ /dev/null @@ -1,59 +0,0 @@ -const generateCacheKey = require('../../../utils/generate-cache-key') -const { - transactions: transactionsRepository, -} = require('../../../repositories') -const utils = require('../utils') - -const index = async request => { - const { count, rows } = await transactionsRepository.findAllLegacy({ - ...request.query, - ...utils.paginate(request), - }) - - if (!rows) { - return utils.respondWith('No transactions found', true) - } - - return utils.respondWith({ - transactions: utils.toCollection(request, rows, 'transaction'), - count, - }) -} - -const show = async request => { - const result = await transactionsRepository.findById(request.query.id) - - if (!result) { - return utils.respondWith('No transactions found', true) - } - - return utils.respondWith({ - transaction: utils.toResource(request, result, 'transaction'), - }) -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v1.transactions.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v1.transactions.show', show, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.query.id }), - }) -} diff --git a/packages/core-api/lib/versions/1/schemas/accounts.js b/packages/core-api/lib/versions/1/schemas/accounts.js deleted file mode 100755 index 3e64a77fc0..0000000000 --- a/packages/core-api/lib/versions/1/schemas/accounts.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * The AJV schema for the account endpoints. - * @type {Object} - */ -module.exports = { - getBalance: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - format: 'address', - }, - }, - required: ['address'], - }, - getPublicKey: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - format: 'address', - }, - }, - required: ['address'], - }, - generatePublicKey: { - type: 'object', - properties: { - secret: { - type: 'string', - minLength: 1, - }, - }, - required: ['secret'], - }, - getDelegates: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - format: 'address', - }, - }, - required: ['address'], - }, - getAccount: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - format: 'address', - }, - }, - required: ['address'], - }, - top: { - type: 'object', - properties: { - limit: { - type: 'integer', - minimum: 0, - maximum: 100, - }, - offset: { - type: 'integer', - minimum: 0, - }, - }, - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/blocks.js b/packages/core-api/lib/versions/1/schemas/blocks.js deleted file mode 100755 index 699cc691d4..0000000000 --- a/packages/core-api/lib/versions/1/schemas/blocks.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * The AJV schema for the block endpoints. - * @type {Object} - */ -module.exports = { - getBlock: { - type: 'object', - properties: { - id: { - type: 'string', - minLength: 1, - }, - }, - required: ['id'], - }, - getBlocks: { - type: 'object', - properties: { - limit: { - type: 'integer', - minimum: 0, - maximum: 100, - }, - orderBy: { - type: 'string', - }, - offset: { - type: 'integer', - minimum: 0, - }, - generatorPublicKey: { - type: 'string', - format: 'publicKey', - }, - totalAmount: { - type: 'integer', - minimum: 0, - }, - totalFee: { - type: 'integer', - minimum: 0, - }, - reward: { - type: 'integer', - minimum: 0, - }, - previousBlock: { - type: 'string', - }, - height: { - type: 'integer', - }, - }, - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/delegates.js b/packages/core-api/lib/versions/1/schemas/delegates.js deleted file mode 100755 index 7ce2953c4d..0000000000 --- a/packages/core-api/lib/versions/1/schemas/delegates.js +++ /dev/null @@ -1,88 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const lastBlock = app.resolvePlugin('blockchain').getLastBlock() - -/** - * The AJV schema for the delegate endpoints. - * @type {Object} - */ -module.exports = { - forgingStatus: { - type: 'object', - properties: { - publicKey: { - type: 'string', - format: 'publicKey', - }, - }, - required: ['publicKey'], - }, - getDelegate: { - type: 'object', - properties: { - publicKey: { - type: 'string', - }, - username: { - type: 'string', - }, - }, - }, - search: { - type: 'object', - properties: { - q: { - type: 'string', - minLength: 1, - maxLength: 20, - }, - limit: { - type: 'integer', - minimum: 1, - maximum: 100, - }, - }, - required: ['q'], - }, - getVoters: { - type: 'object', - properties: { - publicKey: { - type: 'string', - format: 'publicKey', - }, - }, - required: ['publicKey'], - }, - getDelegates: { - type: 'object', - properties: { - orderBy: { - type: 'string', - }, - limit: { - type: 'integer', - minimum: 1, - maximum: lastBlock - ? app - .resolvePlugin('config') - .getConstants(lastBlock.data.height).activeDelegates - : 51, - }, - offset: { - type: 'integer', - minimum: 0, - }, - }, - }, - getForgedByAccount: { - type: 'object', - properties: { - generatorPublicKey: { - type: 'string', - format: 'publicKey', - }, - }, - required: ['generatorPublicKey'], - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/loader.js b/packages/core-api/lib/versions/1/schemas/loader.js deleted file mode 100755 index 72e4a5ab05..0000000000 --- a/packages/core-api/lib/versions/1/schemas/loader.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * The AJV schema for the loader endpoints. - * @type {Object} - */ -module.exports = { - loadSignatures: { - type: 'object', - properties: { - signatures: { - type: 'array', - uniqueItems: true, - }, - }, - required: ['signatures'], - }, - loadUnconfirmedTransactions: { - type: 'object', - properties: { - transactions: { - type: 'array', - uniqueItems: true, - }, - }, - required: ['transactions'], - }, - getNetwork: { - peers: { - type: 'object', - properties: { - peers: { - type: 'array', - uniqueItems: true, - }, - }, - required: ['peers'], - }, - peer: { - type: 'object', - properties: { - ip: { - type: 'string', - format: 'ip', - }, - port: { - type: 'integer', - minimum: 1, - maximum: 65535, - }, - state: { - type: 'integer', - minimum: 0, - maximum: 3, - }, - os: { - type: 'string', - }, - version: { - type: 'string', - }, - }, - required: ['ip', 'port'], - }, - height: { - type: 'object', - properties: { - height: { - type: 'integer', - minimum: 0, - }, - id: { - type: 'string', - minLength: 1, - }, - }, - required: ['height'], - }, - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/peers.js b/packages/core-api/lib/versions/1/schemas/peers.js deleted file mode 100755 index d0625846bd..0000000000 --- a/packages/core-api/lib/versions/1/schemas/peers.js +++ /dev/null @@ -1,125 +0,0 @@ -/** - * The AJV schema for the peer endpoints. - * @type {Object} - */ -module.exports = { - headers: { - type: 'object', - properties: { - port: { - type: 'integer', - minimum: 1, - maximum: 65535, - }, - os: { - type: 'string', - maxLength: 64, - }, - nethash: { - type: 'string', - maxLength: 64, - }, - height: { - type: 'integer', - minimum: 0, - }, - version: { - type: 'string', - maxLength: 11, - }, - blockheader: { - type: 'object', - }, - }, - required: ['port', 'nethash', 'version'], - }, - updatePeersList: { - peers: { - type: 'object', - properties: { - peers: { - type: 'array', - uniqueItems: true, - }, - }, - required: ['peers'], - }, - peer: { - type: 'object', - properties: { - ip: { - type: 'string', - format: 'ip', - }, - port: { - type: 'integer', - minimum: 1, - maximum: 65535, - }, - state: { - type: 'integer', - minimum: 0, - maximum: 3, - }, - os: { - type: 'string', - maxLength: 64, - }, - version: { - type: 'string', - maxLength: 11, - }, - }, - required: ['ip', 'port'], - }, - }, - getPeers: { - type: 'object', - properties: { - port: { - type: 'integer', - minimum: 1, - maximum: 65535, - }, - status: { - type: 'string', - maxLength: 20, - }, - os: { - type: 'string', - maxLength: 64, - }, - version: { - type: 'string', - maxLength: 11, - }, - orderBy: { - type: 'string', - }, - limit: { - type: 'integer', - minimum: 0, - maximum: 100, - }, - offset: { - type: 'integer', - minimum: 0, - }, - }, - }, - getPeer: { - type: 'object', - properties: { - ip: { - type: 'string', - format: 'ip', - }, - port: { - type: 'integer', - minimum: 0, - maximum: 65535, - }, - }, - required: ['ip', 'port'], - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/signatures.js b/packages/core-api/lib/versions/1/schemas/signatures.js deleted file mode 100755 index bfe01d1373..0000000000 --- a/packages/core-api/lib/versions/1/schemas/signatures.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * The AJV schema for the signature endpoints. - * @type {Object} - */ -module.exports = { - getFee: { - type: 'object', - properties: { - address: { - type: 'string', - minLength: 1, - format: 'address', - }, - }, - required: ['address'], - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/transactions.js b/packages/core-api/lib/versions/1/schemas/transactions.js deleted file mode 100755 index f100b71157..0000000000 --- a/packages/core-api/lib/versions/1/schemas/transactions.js +++ /dev/null @@ -1,96 +0,0 @@ -/** - * The AJV schema for the transaction endpoints. - * @type {Object} - */ -module.exports = { - getTransactions: { - type: 'object', - properties: { - blockId: { - type: 'string', - }, - limit: { - type: 'integer', - minimum: 0, - maximum: 100, - }, - type: { - type: 'integer', - minimum: 0, - maximum: 10, - }, - orderBy: { - type: 'string', - }, - offset: { - type: 'integer', - minimum: 0, - }, - senderPublicKey: { - type: 'string', - format: 'publicKey', - }, - vendorField: { - type: 'string', - format: 'vendorField', - }, - ownerPublicKey: { - type: 'string', - format: 'publicKey', - }, - ownerAddress: { - type: 'string', - }, - senderId: { - type: 'string', - format: 'address', - }, - recipientId: { - type: 'string', - format: 'address', - }, - amount: { - type: 'integer', - minimum: 0, - maximum: 10 ** 8, - }, - fee: { - type: 'integer', - minimum: 0, - maximum: 10 ** 8, - }, - }, - }, - getTransaction: { - type: 'object', - properties: { - id: { - type: 'string', - minLength: 1, - }, - }, - required: ['id'], - }, - getUnconfirmedTransaction: { - type: 'object', - properties: { - id: { - type: 'string', - minLength: 1, - }, - }, - required: ['id'], - }, - getUnconfirmedTransactions: { - type: 'object', - properties: { - senderPublicKey: { - type: 'string', - format: 'publicKey', - }, - address: { - type: 'string', - }, - }, - }, -} diff --git a/packages/core-api/lib/versions/1/schemas/transport.js b/packages/core-api/lib/versions/1/schemas/transport.js deleted file mode 100644 index 125c5f03c3..0000000000 --- a/packages/core-api/lib/versions/1/schemas/transport.js +++ /dev/null @@ -1,94 +0,0 @@ -/** - * The AJV schema for the transport endpoints. - * @type {Object} - */ -module.exports = { - headers: { - type: 'object', - properties: { - ip: { - type: 'string', - format: 'ip', - }, - port: { - type: 'integer', - minimum: 1, - maximum: 65535, - }, - os: { - type: 'string', - maxLength: 64, - }, - nethash: { - type: 'string', - maxLength: 64, - }, - version: { - type: 'string', - maxLength: 11, - }, - }, - required: ['ip', 'port', 'nethash', 'version'], - }, - commonBlocks: { - type: 'object', - properties: { - ids: { - type: 'string', - format: 'csv', - }, - }, - required: ['ids'], - }, - transactionsFromIds: { - type: 'object', - properties: { - ids: { - type: 'string', - format: 'csv', - }, - }, - required: ['ids'], - }, - blocks: { - type: 'object', - properties: { - lastBlockHeight: { - type: 'integer', - }, - }, - }, - block: { - type: 'object', - properties: { - id: { - type: 'string', - }, - }, - }, - signatures: { - type: 'object', - properties: { - signature: { - type: 'object', - properties: { - transaction: { - type: 'string', - }, - signature: { - type: 'string', - format: 'signature', - }, - }, - required: ['transaction', 'signature'], - }, - }, - required: ['signature'], - }, - transactions: { - id: 'nodeManager.transactions', - type: 'array', - uniqueItems: true, - required: ['transactions'], - }, -} diff --git a/packages/core-api/lib/versions/1/transformers/account.js b/packages/core-api/lib/versions/1/transformers/account.js deleted file mode 100644 index 42552aa92e..0000000000 --- a/packages/core-api/lib/versions/1/transformers/account.js +++ /dev/null @@ -1,24 +0,0 @@ -/* eslint camelcase: "off" */ - -/** - * Turns a "wallet" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const hasSecondSignature = !!model.secondPublicKey - - return { - address: model.address, - publicKey: model.publicKey, - secondPublicKey: model.secondPublicKey, - votes: model.votes, - username: model.username, - balance: `${model.balance}`, - unconfirmedBalance: `${model.balance}`, - multisignatures: [], - u_multisignatures: [], - unconfirmedSignature: hasSecondSignature ? 1 : 0, - secondSignature: hasSecondSignature ? 1 : 0, - } -} diff --git a/packages/core-api/lib/versions/1/transformers/block.js b/packages/core-api/lib/versions/1/transformers/block.js deleted file mode 100644 index f400f8dcd7..0000000000 --- a/packages/core-api/lib/versions/1/transformers/block.js +++ /dev/null @@ -1,33 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') -const blockchain = require('@arkecosystem/core-container').resolvePlugin( - 'blockchain', -) - -/** - * Turns a "block" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const lastBlock = blockchain.getLastBlock() - - return { - id: model.id, - version: model.version, - timestamp: model.timestamp, - previousBlock: model.previousBlock, - height: model.height, - numberOfTransactions: model.numberOfTransactions, - totalAmount: +bignumify(model.totalAmount).toFixed(), - totalForged: +bignumify(model.reward) - .plus(model.totalFee) - .toString(), - totalFee: +bignumify(model.totalFee).toFixed(), - reward: +bignumify(model.reward).toFixed(), - payloadLength: model.payloadLength, - payloadHash: model.payloadHash, - generatorPublicKey: model.generatorPublicKey, - blockSignature: model.blockSignature, - confirmations: lastBlock ? lastBlock.data.height - model.height : 0, - } -} diff --git a/packages/core-api/lib/versions/1/transformers/delegate.js b/packages/core-api/lib/versions/1/transformers/delegate.js deleted file mode 100644 index b453280f1c..0000000000 --- a/packages/core-api/lib/versions/1/transformers/delegate.js +++ /dev/null @@ -1,19 +0,0 @@ -const { delegateCalculator } = require('@arkecosystem/core-utils') - -/** - * Turns a "delegate" object into a generic object. - * @param {Object} delegate - * @return {Object} - */ -module.exports = delegate => ({ - username: delegate.username, - address: delegate.address, - publicKey: delegate.publicKey, - vote: `${delegate.voteBalance}`, - producedblocks: delegate.producedBlocks, - missedblocks: delegate.missedBlocks, - forged: delegate.forged, - rate: delegate.rate, - approval: delegateCalculator.calculateApproval(delegate), - productivity: delegateCalculator.calculateProductivity(delegate), -}) diff --git a/packages/core-api/lib/versions/1/transformers/fee-statistics.js b/packages/core-api/lib/versions/1/transformers/fee-statistics.js deleted file mode 100644 index 2a5e5fd250..0000000000 --- a/packages/core-api/lib/versions/1/transformers/fee-statistics.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Turns a "fee-statistics" object into readable object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => ({ - type: model.type, - fees: { - minFee: parseInt(model.minFee), - maxFee: parseInt(model.maxFee), - avgFee: parseInt(model.avgFee), - }, -}) diff --git a/packages/core-api/lib/versions/1/transformers/peer.js b/packages/core-api/lib/versions/1/transformers/peer.js deleted file mode 100644 index cde2573111..0000000000 --- a/packages/core-api/lib/versions/1/transformers/peer.js +++ /dev/null @@ -1,26 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') - -/** - * Turns a "peer" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const peer = { - ip: model.ip, - port: model.port, - version: model.version, - height: model.height, - status: model.status, - os: model.os, - delay: model.delay, - } - - if (config.network.name !== 'mainnet') { - peer.hashid = model.hashid - } - - return peer -} diff --git a/packages/core-api/lib/versions/1/transformers/ports.js b/packages/core-api/lib/versions/1/transformers/ports.js deleted file mode 100644 index 8c8615740f..0000000000 --- a/packages/core-api/lib/versions/1/transformers/ports.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Turns a "config" object into readable object. - * @param {Object} model - * @return {Object} - */ -module.exports = config => { - const result = {} - const keys = [ - '@arkecosystem/core-p2p', - '@arkecosystem/core-api', - '@arkecosystem/core-graphql', - '@arkecosystem/core-json-rpc', - '@arkecosystem/core-webhooks', - ] - - for (const [name, options] of Object.entries(config.plugins)) { - if (keys.includes(name) && options.enabled) { - if (options.server && options.server.enabled) { - result[name] = options.server.port - - continue - } - - result[name] = options.port - } - } - - return result -} diff --git a/packages/core-api/lib/versions/1/transformers/transaction.js b/packages/core-api/lib/versions/1/transformers/transaction.js deleted file mode 100644 index fbe730c867..0000000000 --- a/packages/core-api/lib/versions/1/transformers/transaction.js +++ /dev/null @@ -1,41 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') -const { bignumify } = require('@arkecosystem/core-utils') - -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') - -const { Transaction } = require('@arkecosystem/crypto').models - -/** - * Turns a "transaction" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const data = new Transaction(model.serialized.toString('hex')) - - return { - id: data.id, - blockid: model.blockId, - type: data.type, - timestamp: model.timestamp || data.timestamp, - amount: +bignumify(data.amount).toFixed(), - fee: +bignumify(data.fee).toFixed(), - recipientId: data.recipientId, - senderId: crypto.getAddress( - data.senderPublicKey, - config.network.pubKeyHash, - ), - senderPublicKey: data.senderPublicKey, - vendorField: data.vendorField, - signature: data.signature, - signSignature: data.signSignature, - signatures: data.signatures, - asset: data.asset || {}, - confirmations: model.block - ? blockchain.getLastBlock().data.height - model.block.height - : 0, - } -} diff --git a/packages/core-api/lib/versions/1/transformers/voter.js b/packages/core-api/lib/versions/1/transformers/voter.js deleted file mode 100644 index e40f5b76c6..0000000000 --- a/packages/core-api/lib/versions/1/transformers/voter.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Turns a "voter" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => ({ - username: model.username, - address: model.address, - publicKey: model.publicKey, - balance: `${model.balance}`, -}) diff --git a/packages/core-api/lib/versions/1/utils.js b/packages/core-api/lib/versions/1/utils.js deleted file mode 100644 index eeaf7dbbbb..0000000000 --- a/packages/core-api/lib/versions/1/utils.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint max-len: "off" */ - -const { - transformResource, - transformCollection, -} = require('../../utils/transformer') - -/** - * Create a pagination object for the request. - * @param {Hapi.Request} request - * @return {Object} - */ -const paginate = request => ({ - offset: request.query.offset || 0, - limit: request.query.limit || 100, -}) - -/** - * Create a hapi.js response. - * @param {Object} data - * @param {Boolean} error - * @return {Object} - */ -const respondWith = (data, error = false) => - error ? { error: data, success: false } : { ...data, success: true } - -/** - * Respond with data from cache. - * @param {Object} data - * @param {Hapi.Toolkit} h - * @return {Object} - */ -const respondWithCache = (data, h) => { - const { value, cached } = data - const lastModified = cached ? new Date(cached.stored) : new Date() - - return value.isBoom - ? h.response(value.output.payload).code(value.output.statusCode) - : h.response(value).header('Last-modified', lastModified.toUTCString()) -} - -/** - * Transform the given data into a resource. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const toResource = (request, data, transformer) => - transformResource(request, data, transformer) - -/** - * Transform the given data into a collection. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const toCollection = transformCollection - -/** - * @type {Object} - */ -module.exports = { - paginate, - respondWith, - respondWithCache, - toResource, - toCollection, -} diff --git a/packages/core-api/lib/versions/2/handlers/blockchain.js b/packages/core-api/lib/versions/2/handlers/blockchain.js deleted file mode 100644 index e41b419ed8..0000000000 --- a/packages/core-api/lib/versions/2/handlers/blockchain.js +++ /dev/null @@ -1,29 +0,0 @@ -const app = require('@arkecosystem/core-container') -const { bignumify, supplyCalculator } = require('@arkecosystem/core-utils') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = blockchain.getLastBlock() - - return { - data: { - block: { - height: lastBlock.data.height, - id: lastBlock.data.id, - }, - supply: supplyCalculator.calculate(lastBlock.data.height), - }, - } - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/blocks.js b/packages/core-api/lib/versions/2/handlers/blocks.js deleted file mode 100644 index 8f5356be0e..0000000000 --- a/packages/core-api/lib/versions/2/handlers/blocks.js +++ /dev/null @@ -1,78 +0,0 @@ -const { respondWithCache } = require('../utils') -const schema = require('../schema/blocks') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.blocks.index(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.index, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.blocks.show(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.show, - }, -} - -/** - * @type {Object} - */ -exports.transactions = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.blocks.transactions(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.transactions, - }, -} - -/** - * @type {Object} - */ -exports.search = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.blocks.search(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.search, - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/delegates.js b/packages/core-api/lib/versions/2/handlers/delegates.js deleted file mode 100644 index 71d9626cf7..0000000000 --- a/packages/core-api/lib/versions/2/handlers/delegates.js +++ /dev/null @@ -1,118 +0,0 @@ -const { respondWithCache } = require('../utils') -const schema = require('../schema/delegates') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.delegates.index(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.index, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.delegates.show(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.show, - }, -} - -/** - * @type {Object} - */ -exports.search = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.delegates.search(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.search, - }, -} - -/** - * @type {Object} - */ -exports.blocks = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.delegates.blocks(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.blocks, - }, -} - -/** - * @type {Object} - */ -exports.voters = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.delegates.voters(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.voters, - }, -} - -/** - * @type {Object} - */ -exports.voterBalances = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.delegates.voterBalances( - request, - ) - - return respondWithCache(data, h) - }, - options: { - validate: schema.voterBalances, - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/node.js b/packages/core-api/lib/versions/2/handlers/node.js deleted file mode 100644 index b2a164a521..0000000000 --- a/packages/core-api/lib/versions/2/handlers/node.js +++ /dev/null @@ -1,84 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const blockchain = app.resolvePlugin('blockchain') -const config = app.resolvePlugin('config') -const utils = require('../utils') -const { transactions } = require('../../../repositories') - -/** - * @type {Object} - */ -exports.status = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const lastBlock = blockchain.getLastBlock() - const networkHeight = await blockchain.p2p.getNetworkHeight() - - return { - data: { - synced: blockchain.isSynced(), - now: lastBlock ? lastBlock.data.height : 0, - blocksCount: networkHeight - lastBlock.data.height || 0, - }, - } - }, -} - -/** - * @type {Object} - */ -exports.syncing = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const lastBlock = blockchain.getLastBlock() - const networkHeight = await blockchain.p2p.getNetworkHeight() - - return { - data: { - syncing: !blockchain.isSynced(), - blocks: networkHeight - lastBlock.data.height || 0, - height: lastBlock.data.height, - id: lastBlock.data.id, - }, - } - }, -} - -/** - * @type {Object} - */ -exports.configuration = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const feeStatisticsData = await transactions.getFeeStatistics() - - return { - data: { - nethash: config.network.nethash, - token: config.network.client.token, - symbol: config.network.client.symbol, - explorer: config.network.client.explorer, - version: config.network.pubKeyHash, - ports: utils.toResource(request, config, 'ports'), - constants: config.getConstants(blockchain.getLastBlock().data.height), - feeStatistics: utils.toCollection( - request, - feeStatisticsData, - 'fee-statistics', - ), - }, - } - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/peers.js b/packages/core-api/lib/versions/2/handlers/peers.js deleted file mode 100644 index c1039f8ffc..0000000000 --- a/packages/core-api/lib/versions/2/handlers/peers.js +++ /dev/null @@ -1,98 +0,0 @@ -const Boom = require('boom') -const app = require('@arkecosystem/core-container') - -const blockchain = app.resolvePlugin('blockchain') -const utils = require('../utils') -const schema = require('../schema/peers') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const allPeers = await blockchain.p2p.getPeers() - - let result = allPeers.sort((a, b) => a.delay - b.delay) - result = request.query.os - ? result.filter(peer => peer.os === request.query.os) - : result - result = request.query.status - ? result.filter(peer => peer.status === request.query.status) - : result - result = request.query.port - ? result.filter(peer => peer.port === request.query.port) - : result - result = request.query.version - ? result.filter(peer => peer.version === request.query.version) - : result - result = result.slice(0, request.query.limit || 100) - - if (request.query.orderBy) { - const order = request.query.orderBy.split(':') - - if (['port', 'status', 'os', 'version'].includes(order[0])) { - result = order[1].toUpperCase() === 'ASC' - ? result.sort((a, b) => a[order[0]] - b[order[0]]) - : result.sort((a, b) => a[order[0]] + b[order[0]]) - } - } - - return utils.toPagination( - request, - { rows: result, count: allPeers.length }, - 'peer', - ) - }, - options: { - validate: schema.index, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const peers = await blockchain.p2p.getPeers() - const peer = peers.find(p => p.ip === request.params.ip) - - if (!peer) { - return Boom.notFound('Peer not found') - } - - return utils.respondWithResource(request, peer, 'peer') - }, - options: { - validate: schema.show, - }, -} - -/** - * @type {Object} - */ -exports.suspended = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const peers = app.resolvePlugin('p2p').getSuspendedPeers() - - return utils.respondWithCollection( - request, - Object.values(peers).map(peer => peer.peer), - 'peer', - ) - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/transactions.js b/packages/core-api/lib/versions/2/handlers/transactions.js deleted file mode 100644 index cda76c5e98..0000000000 --- a/packages/core-api/lib/versions/2/handlers/transactions.js +++ /dev/null @@ -1,214 +0,0 @@ -const Boom = require('boom') - -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants -const { TransactionGuard } = require('@arkecosystem/core-transaction-pool') - -const app = require('@arkecosystem/core-container') - -const blockchain = app.resolvePlugin('blockchain') -const config = app.resolvePlugin('config') -const transactionPool = app.resolvePlugin('transactionPool') - -const utils = require('../utils') -const schema = require('../schema/transactions') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.transactions.index(request) - - return utils.respondWithCache(data, h) - }, - options: { - validate: schema.index, - }, -} - -/** - * @type {Object} - */ -exports.store = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - if (!transactionPool.options.enabled) { - return Boom.serverUnavailable('Transaction pool is disabled.') - } - - const guard = new TransactionGuard(transactionPool) - - const result = await guard.validate(request.payload.transactions) - - if (result.broadcast.length > 0) { - app - .resolvePlugin('p2p') - .broadcastTransactions(guard.getBroadcastTransactions()) - } - - return { - data: { - accept: result.accept, - broadcast: result.broadcast, - excess: result.excess, - invalid: result.invalid, - }, - errors: result.errors, - } - }, - options: { - validate: schema.store, - plugins: { - pagination: { - enabled: false, - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.transactions.show(request) - - return utils.respondWithCache(data, h) - }, - options: { - validate: schema.show, - }, -} - -/** - * @type {Object} - */ -exports.unconfirmed = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - if (!transactionPool.options.enabled) { - return Boom.serverUnavailable('Transaction pool is disabled.') - } - - const pagination = utils.paginate(request) - - let transactions = transactionPool.getTransactions( - pagination.offset, - pagination.limit, - ) - transactions = transactions.map(transaction => ({ - serialized: transaction, - })) - - return utils.toPagination( - request, - { - count: transactionPool.getPoolSize(), - rows: transactions, - }, - 'transaction', - ) - }, - options: { - validate: schema.unconfirmed, - }, -} - -/** - * @type {Object} - */ -exports.showUnconfirmed = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - if (!transactionPool.options.enabled) { - return Boom.serverUnavailable('Transaction pool is disabled.') - } - - let transaction = transactionPool.getTransaction(request.params.id) - - if (!transaction) { - return Boom.notFound('Transaction not found') - } - - transaction = { serialized: transaction.serialized } - - return utils.respondWithResource(request, transaction, 'transaction') - }, - options: { - validate: schema.showUnconfirmed, - }, -} - -/** - * @type {Object} - */ -exports.search = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.transactions.search(request) - - return utils.respondWithCache(data, h) - }, - options: { - validate: schema.search, - }, -} - -/** - * @type {Object} - */ -exports.types = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - return { - data: TRANSACTION_TYPES, - } - }, -} - -/** - * @type {Object} - */ -exports.fees = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - return { - data: config.getConstants(blockchain.getLastBlock().data.height).fees - .staticFees, - } - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/votes.js b/packages/core-api/lib/versions/2/handlers/votes.js deleted file mode 100644 index 18804a03be..0000000000 --- a/packages/core-api/lib/versions/2/handlers/votes.js +++ /dev/null @@ -1,40 +0,0 @@ -const { respondWithCache } = require('../utils') -const schema = require('../schema/votes') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.votes.index(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.index, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.votes.show(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.show, - }, -} diff --git a/packages/core-api/lib/versions/2/handlers/wallets.js b/packages/core-api/lib/versions/2/handlers/wallets.js deleted file mode 100644 index 44e31f8da8..0000000000 --- a/packages/core-api/lib/versions/2/handlers/wallets.js +++ /dev/null @@ -1,156 +0,0 @@ -const { respondWithCache } = require('../utils') -const schema = require('../schema/wallets') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.index(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.index, - }, -} - -/** - * @type {Object} - */ -exports.top = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.top(request) - - return respondWithCache(data, h) - }, - // TODO: create top schema -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.show(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.show, - }, -} - -/** - * @type {Object} - */ -exports.transactions = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.transactions(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.transactions, - }, -} - -/** - * @type {Object} - */ -exports.transactionsSent = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.transactionsSent( - request, - ) - - return respondWithCache(data, h) - }, - options: { - validate: schema.transactionsSent, - }, -} - -/** - * @type {Object} - */ -exports.transactionsReceived = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.transactionsReceived( - request, - ) - - return respondWithCache(data, h) - }, - options: { - validate: schema.transactionsReceived, - }, -} - -/** - * @type {Object} - */ -exports.votes = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.votes(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.votes, - }, -} - -/** - * @type {Object} - */ -exports.search = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const data = await request.server.methods.v2.wallets.search(request) - - return respondWithCache(data, h) - }, - options: { - validate: schema.search, - }, -} diff --git a/packages/core-api/lib/versions/2/index.js b/packages/core-api/lib/versions/2/index.js deleted file mode 100644 index c16c9cd1be..0000000000 --- a/packages/core-api/lib/versions/2/index.js +++ /dev/null @@ -1,111 +0,0 @@ -const blockchain = require('./handlers/blockchain') -const blocks = require('./handlers/blocks') -const delegates = require('./handlers/delegates') -const node = require('./handlers/node') -const peers = require('./handlers/peers') -const transactions = require('./handlers/transactions') -const votes = require('./handlers/votes') -const wallets = require('./handlers/wallets') - -const registerBlockMethods = require('./methods/blocks') -const registerDelegateMethods = require('./methods/delegates') -const registerTransactionMethods = require('./methods/transactions') -const registerWalletMethods = require('./methods/wallets') -const registerVoteMethods = require('./methods/votes') - -/** - * Register the v2 routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - registerBlockMethods(server) - registerDelegateMethods(server) - registerTransactionMethods(server) - registerWalletMethods(server) - registerVoteMethods(server) - - server.route([ - { method: 'GET', path: '/blockchain', ...blockchain.index }, - - { method: 'GET', path: '/blocks', ...blocks.index }, - { method: 'GET', path: '/blocks/{id}', ...blocks.show }, - { - method: 'GET', - path: '/blocks/{id}/transactions', - ...blocks.transactions, - }, - { method: 'POST', path: '/blocks/search', ...blocks.search }, - - { method: 'GET', path: '/delegates', ...delegates.index }, - { method: 'GET', path: '/delegates/{id}', ...delegates.show }, - { method: 'GET', path: '/delegates/{id}/blocks', ...delegates.blocks }, - { method: 'GET', path: '/delegates/{id}/voters', ...delegates.voters }, - { - method: 'GET', - path: '/delegates/{id}/voters/balances', - ...delegates.voterBalances, - }, - { method: 'POST', path: '/delegates/search', ...delegates.search }, - - { method: 'GET', path: '/node/status', ...node.status }, - { method: 'GET', path: '/node/syncing', ...node.syncing }, - { method: 'GET', path: '/node/configuration', ...node.configuration }, - - { method: 'GET', path: '/peers', ...peers.index }, - { method: 'GET', path: '/peers/suspended', ...peers.suspended }, - { method: 'GET', path: '/peers/{ip}', ...peers.show }, - - { method: 'GET', path: '/transactions', ...transactions.index }, - { method: 'POST', path: '/transactions', ...transactions.store }, - { method: 'GET', path: '/transactions/{id}', ...transactions.show }, - { - method: 'GET', - path: '/transactions/unconfirmed', - ...transactions.unconfirmed, - }, - { - method: 'GET', - path: '/transactions/unconfirmed/{id}', - ...transactions.showUnconfirmed, - }, - { method: 'POST', path: '/transactions/search', ...transactions.search }, - { method: 'GET', path: '/transactions/types', ...transactions.types }, - { method: 'GET', path: '/transactions/fees', ...transactions.fees }, - - { method: 'GET', path: '/votes', ...votes.index }, - { method: 'GET', path: '/votes/{id}', ...votes.show }, - - { method: 'GET', path: '/wallets', ...wallets.index }, - { method: 'GET', path: '/wallets/top', ...wallets.top }, - { method: 'GET', path: '/wallets/{id}', ...wallets.show }, - { - method: 'GET', - path: '/wallets/{id}/transactions', - ...wallets.transactions, - }, - { - method: 'GET', - path: '/wallets/{id}/transactions/sent', - ...wallets.transactionsSent, - }, - { - method: 'GET', - path: '/wallets/{id}/transactions/received', - ...wallets.transactionsReceived, - }, - { method: 'GET', path: '/wallets/{id}/votes', ...wallets.votes }, - { method: 'POST', path: '/wallets/search', ...wallets.search }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark Public API - v2', - version: '2.0.0', - register, -} diff --git a/packages/core-api/lib/versions/2/methods/blocks.js b/packages/core-api/lib/versions/2/methods/blocks.js deleted file mode 100644 index 821cf32cfa..0000000000 --- a/packages/core-api/lib/versions/2/methods/blocks.js +++ /dev/null @@ -1,105 +0,0 @@ -const Boom = require('boom') -const generateCacheKey = require('../../../utils/generate-cache-key') -const { - blocks: blocksRepository, - transactions: transactionsRepository, -} = require('../../../repositories') -const utils = require('../utils') - -const index = async request => { - const blocks = await blocksRepository.findAll({ - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, blocks, 'block') -} - -const show = async request => { - const block = await blocksRepository.findById(request.params.id) - - if (!block) { - return Boom.notFound('Block not found') - } - - return utils.respondWithResource(request, block, 'block') -} - -const transactions = async request => { - const block = await blocksRepository.findById(request.params.id) - - if (!block) { - return Boom.notFound('Block not found') - } - - const rows = await transactionsRepository.findAllByBlock(block.id, { - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, rows, 'transaction') -} - -const search = async request => { - const blocks = await blocksRepository.search({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, blocks, 'block') -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v2.blocks.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.blocks.show', show, { - cache: { - expiresIn: 600 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.params.id }), - }) - - server.method('v2.blocks.transactions', transactions, { - cache: { - expiresIn: 600 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...{ id: request.params.id }, - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.blocks.search', search, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }), - }) -} diff --git a/packages/core-api/lib/versions/2/methods/delegates.js b/packages/core-api/lib/versions/2/methods/delegates.js deleted file mode 100644 index 7390c2f3f6..0000000000 --- a/packages/core-api/lib/versions/2/methods/delegates.js +++ /dev/null @@ -1,161 +0,0 @@ -const Boom = require('boom') -const orderBy = require('lodash/orderBy') -const app = require('@arkecosystem/core-container') -const generateCacheKey = require('../../../utils/generate-cache-key') -const { blocks: blocksRepository } = require('../../../repositories') -const utils = require('../utils') - -const database = app.resolvePlugin('database') - -const index = async request => { - const delegates = await database.delegates.paginate({ - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, delegates, 'delegate') -} - -const show = async request => { - const delegate = await database.delegates.findById(request.params.id) - - if (!delegate) { - return Boom.notFound('Delegate not found') - } - - return utils.respondWithResource(request, delegate, 'delegate') -} - -const search = async request => { - const delegates = await database.delegates.search({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, delegates, 'delegate') -} - -const blocks = async request => { - const delegate = await database.delegates.findById(request.params.id) - - if (!delegate) { - return Boom.notFound('Delegate not found') - } - - const rows = await blocksRepository.findAllByGenerator( - delegate.publicKey, - utils.paginate(request), - ) - - return utils.toPagination(request, rows, 'block') -} - -const voters = async request => { - const delegate = await database.delegates.findById(request.params.id) - - if (!delegate) { - return Boom.notFound('Delegate not found') - } - - const wallets = await database.wallets.findAllByVote( - delegate.publicKey, - utils.paginate(request), - ) - - return utils.toPagination(request, wallets, 'wallet') -} - -const voterBalances = async request => { - const delegate = await database.delegates.findById(request.params.id) - - if (!delegate) { - return Boom.notFound('Delegate not found') - } - - const wallets = await database.wallets - .all() - .filter(wallet => wallet.vote === delegate.publicKey) - - const data = {} - orderBy(wallets, ['balance'], ['desc']).forEach(wallet => { - data[wallet.address] = +wallet.balance.toFixed() - }) - - return { data } -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v2.delegates.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.delegates.show', show, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.params.id }), - }) - - server.method('v2.delegates.search', search, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.delegates.blocks', blocks, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...{ id: request.params.id }, - ...utils.paginate(request), - }), - }) - - server.method('v2.delegates.voters', voters, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...{ id: request.params.id }, - ...utils.paginate(request), - }), - }) - - server.method('v2.delegates.voterBalances', voterBalances, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.params.id }), - }) -} diff --git a/packages/core-api/lib/versions/2/methods/transactions.js b/packages/core-api/lib/versions/2/methods/transactions.js deleted file mode 100644 index 2eaa0897d3..0000000000 --- a/packages/core-api/lib/versions/2/methods/transactions.js +++ /dev/null @@ -1,75 +0,0 @@ -const Boom = require('boom') -const generateCacheKey = require('../../../utils/generate-cache-key') -const { - transactions: transactionsRepository, -} = require('../../../repositories') -const utils = require('../utils') - -const index = async request => { - const transactions = await transactionsRepository.findAll({ - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, transactions, 'transaction') -} - -const show = async request => { - const transaction = await transactionsRepository.findById(request.params.id) - - if (!transaction) { - return Boom.notFound('Transaction not found') - } - - return utils.respondWithResource(request, transaction, 'transaction') -} - -const search = async request => { - const transactions = await transactionsRepository.search({ - ...request.query, - ...request.payload, - ...utils.paginate(request), - }) - - return utils.toPagination(request, transactions, 'transaction') -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v2.transactions.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.transactions.show', show, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.params.id }), - }) - - server.method('v2.transactions.search', search, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }), - }) -} diff --git a/packages/core-api/lib/versions/2/methods/votes.js b/packages/core-api/lib/versions/2/methods/votes.js deleted file mode 100644 index 7d4df6060b..0000000000 --- a/packages/core-api/lib/versions/2/methods/votes.js +++ /dev/null @@ -1,58 +0,0 @@ -const Boom = require('boom') -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants -const generateCacheKey = require('../../../utils/generate-cache-key') -const { - transactions: transactionsRepository, -} = require('../../../repositories') -const utils = require('../utils') - -const index = async request => { - const transactions = await transactionsRepository.findAllByType( - TRANSACTION_TYPES.VOTE, - { - ...request.query, - ...utils.paginate(request), - }, - ) - - return utils.toPagination(request, transactions, 'transaction') -} - -const show = async request => { - const transaction = await transactionsRepository.findByTypeAndId( - TRANSACTION_TYPES.VOTE, - request.params.id, - ) - - if (!transaction) { - return Boom.notFound('Vote not found') - } - - return utils.respondWithResource(request, transaction, 'transaction') -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v2.votes.index', index, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.votes.show', show, { - cache: { - expiresIn: 8 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.params.id }), - }) -} diff --git a/packages/core-api/lib/versions/2/methods/wallets.js b/packages/core-api/lib/versions/2/methods/wallets.js deleted file mode 100644 index 4902fffe32..0000000000 --- a/packages/core-api/lib/versions/2/methods/wallets.js +++ /dev/null @@ -1,221 +0,0 @@ -const Boom = require('boom') -const app = require('@arkecosystem/core-container') -const generateCacheKey = require('../../../utils/generate-cache-key') -const utils = require('../utils') -const { - transactions: transactionsRepository, -} = require('../../../repositories') - -const database = app.resolvePlugin('database') - -const index = async request => { - const wallets = await database.wallets.findAll({ - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, wallets, 'wallet') -} - -const top = async request => { - const wallets = await database.wallets.top(utils.paginate(request)) - - return utils.toPagination(request, wallets, 'wallet') -} - -const show = async request => { - const wallet = await database.wallets.findById(request.params.id) - - if (!wallet) { - return Boom.notFound('Wallet not found') - } - - return utils.respondWithResource(request, wallet, 'wallet') -} - -const transactions = async request => { - const wallet = await database.wallets.findById(request.params.id) - - if (!wallet) { - return Boom.notFound('Wallet not found') - } - - const rows = await transactionsRepository.findAllByWallet(wallet, { - ...request.query, - ...request.params, - ...utils.paginate(request), - }) - - return utils.toPagination(request, rows, 'transaction') -} - -const transactionsSent = async request => { - const wallet = await database.wallets.findById(request.params.id) - - if (!wallet) { - return Boom.notFound('Wallet not found') - } - - // NOTE: We unset this value because it otherwise will produce a faulty SQL query - delete request.params.id - - const rows = await transactionsRepository.findAllBySender(wallet.publicKey, { - ...request.query, - ...request.params, - ...utils.paginate(request), - }) - - return utils.toPagination(request, rows, 'transaction') -} - -const transactionsReceived = async request => { - const wallet = await database.wallets.findById(request.params.id) - - if (!wallet) { - return Boom.notFound('Wallet not found') - } - - // NOTE: We unset this value because it otherwise will produce a faulty SQL query - delete request.params.id - - const rows = await transactionsRepository.findAllByRecipient(wallet.address, { - ...request.query, - ...request.params, - ...utils.paginate(request), - }) - - return utils.toPagination(request, rows, 'transaction') -} - -const votes = async request => { - const wallet = await database.wallets.findById(request.params.id) - - if (!wallet) { - return Boom.notFound('Wallet not found') - } - - // NOTE: We unset this value because it otherwise will produce a faulty SQL query - delete request.params.id - - const rows = await transactionsRepository.allVotesBySender(wallet.publicKey, { - ...request.params, - ...utils.paginate(request), - }) - - return utils.toPagination(request, rows, 'transaction') -} - -const search = async request => { - const wallets = await database.wallets.search({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }) - - return utils.toPagination(request, wallets, 'wallet') -} - -module.exports = server => { - const generateTimeout = require('../../utils').getCacheTimeout() - - server.method('v2.wallets.index', index, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }), - }) - - server.method('v2.wallets.top', top, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey(utils.paginate(request)), - }) - - server.method('v2.wallets.show', show, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => generateCacheKey({ id: request.params.id }), - }) - - server.method('v2.wallets.transactions', transactions, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...request.params, - ...utils.paginate(request), - }), - }) - - server.method('v2.wallets.transactionsSent', transactionsSent, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...request.params, - ...utils.paginate(request), - }), - }) - - server.method('v2.wallets.transactionsReceived', transactionsReceived, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.query, - ...request.params, - ...utils.paginate(request), - }), - }) - - server.method('v2.wallets.votes', votes, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.params, - ...utils.paginate(request), - }), - }) - - server.method('v2.wallets.search', search, { - cache: { - expiresIn: 30 * 1000, - generateTimeout, - getDecoratedValue: true, - }, - generateKey: request => - generateCacheKey({ - ...request.payload, - ...request.query, - ...utils.paginate(request), - }), - }) -} diff --git a/packages/core-api/lib/versions/2/schema/blocks.js b/packages/core-api/lib/versions/2/schema/blocks.js deleted file mode 100644 index 8960fcab17..0000000000 --- a/packages/core-api/lib/versions/2/schema/blocks.js +++ /dev/null @@ -1,173 +0,0 @@ -const Joi = require('joi') -const pagination = require('./pagination') - -/** - * @type {Object} - */ -exports.index = { - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - id: Joi.string().regex(/^[0-9]+$/, 'numbers'), - version: Joi.number() - .integer() - .min(0), - timestamp: Joi.number() - .integer() - .min(0), - previousBlock: Joi.string().regex(/^[0-9]+$/, 'numbers'), - height: Joi.number() - .integer() - .positive(), - numberOfTransactions: Joi.number() - .integer() - .min(0), - totalAmount: Joi.number() - .integer() - .min(0), - totalFee: Joi.number() - .integer() - .min(0), - reward: Joi.number() - .integer() - .min(0), - payloadLength: Joi.number() - .integer() - .positive(), - payloadHash: Joi.string().hex(), - generatorPublicKey: Joi.string() - .hex() - .length(66), - blockSignature: Joi.string().hex(), - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - params: { - id: Joi.string().regex(/^[0-9]+$/, 'numbers'), - }, -} - -/** - * @type {Object} - */ -exports.transactions = { - params: { - id: Joi.string(), - }, - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - id: Joi.string() - .hex() - .length(66), - blockId: Joi.string().regex(/^[0-9]+$/, 'numbers'), - type: Joi.number() - .integer() - .min(0), - version: Joi.number() - .integer() - .min(0), - senderPublicKey: Joi.string() - .hex() - .length(66), - senderId: Joi.string() - .alphanum() - .length(34), - recipientId: Joi.string() - .alphanum() - .length(34), - timestamp: Joi.number() - .integer() - .min(0), - amount: Joi.number() - .integer() - .min(0), - fee: Joi.number() - .integer() - .min(0), - vendorFieldHex: Joi.string().hex(), - }, - }, -} - -/** - * @type {Object} - */ -exports.search = { - query: pagination, - payload: { - id: Joi.string().regex(/^[0-9]+$/, 'numbers'), - version: Joi.number() - .integer() - .min(0), - previousBlock: Joi.string().regex(/^[0-9]+$/, 'numbers'), - payloadHash: Joi.string().hex(), - generatorPublicKey: Joi.string() - .hex() - .length(66), - blockSignature: Joi.string().hex(), - timestamp: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - height: Joi.object().keys({ - from: Joi.number() - .integer() - .positive(), - to: Joi.number() - .integer() - .positive(), - }), - numberOfTransactions: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - totalAmount: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - totalFee: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - reward: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - payloadLength: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - }, -} diff --git a/packages/core-api/lib/versions/2/schema/delegates.js b/packages/core-api/lib/versions/2/schema/delegates.js deleted file mode 100644 index 200911d270..0000000000 --- a/packages/core-api/lib/versions/2/schema/delegates.js +++ /dev/null @@ -1,153 +0,0 @@ -const Joi = require('joi') -const pagination = require('./pagination') - -/** - * @type {Object} - */ -exports.index = { - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - address: Joi.string() - .alphanum() - .length(34), - publicKey: Joi.string() - .hex() - .length(66), - secondPublicKey: Joi.string() - .hex() - .length(66), - vote: Joi.string() - .hex() - .length(66), - username: Joi.string(), - balance: Joi.number() - .integer() - .min(0), - voteBalance: Joi.number() - .integer() - .min(0), - producedBlocks: Joi.number() - .integer() - .min(0), - missedBlocks: Joi.number() - .integer() - .min(0), - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - params: { - id: Joi.string(), - }, -} - -/** - * @type {Object} - */ -exports.search = { - query: pagination, - payload: { - username: Joi.string(), - }, -} - -/** - * @type {Object} - */ -exports.blocks = { - params: { - id: Joi.string(), - }, - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - id: Joi.string().regex(/^[0-9]+$/, 'numbers'), - version: Joi.number() - .integer() - .min(0), - timestamp: Joi.number() - .integer() - .min(0), - previousBlock: Joi.string().regex(/^[0-9]+$/, 'numbers'), - height: Joi.number() - .integer() - .positive(), - numberOfTransactions: Joi.number() - .integer() - .min(0), - totalAmount: Joi.number() - .integer() - .min(0), - totalFee: Joi.number() - .integer() - .min(0), - reward: Joi.number() - .integer() - .min(0), - payloadLength: Joi.number() - .integer() - .min(0), - payloadHash: Joi.string().hex(), - generatorPublicKey: Joi.string() - .hex() - .length(66), - blockSignature: Joi.string().hex(), - }, - }, -} - -/** - * @type {Object} - */ -exports.voters = { - params: { - id: Joi.string(), - }, - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - address: Joi.string() - .alphanum() - .length(34), - publicKey: Joi.string() - .hex() - .length(66), - secondPublicKey: Joi.string() - .hex() - .length(66), - vote: Joi.string() - .hex() - .length(66), - username: Joi.string(), - balance: Joi.number() - .integer() - .min(0), - voteBalance: Joi.number() - .integer() - .min(0), - producedBlocks: Joi.number() - .integer() - .min(0), - missedBlocks: Joi.number() - .integer() - .min(0), - }, - }, -} - -/** - * @type {Object} - */ -exports.voterBalances = { - params: { - id: Joi.string(), - }, -} diff --git a/packages/core-api/lib/versions/2/schema/pagination.js b/packages/core-api/lib/versions/2/schema/pagination.js deleted file mode 100644 index 62543ef181..0000000000 --- a/packages/core-api/lib/versions/2/schema/pagination.js +++ /dev/null @@ -1,14 +0,0 @@ -const Joi = require('joi') - -module.exports = { - page: Joi.number() - .integer() - .positive(), - offset: Joi.number() - .integer() - .min(0), - limit: Joi.number() - .integer() - .min(1) - .max(100), -} diff --git a/packages/core-api/lib/versions/2/schema/peers.js b/packages/core-api/lib/versions/2/schema/peers.js deleted file mode 100644 index 958ce07a61..0000000000 --- a/packages/core-api/lib/versions/2/schema/peers.js +++ /dev/null @@ -1,28 +0,0 @@ -const Joi = require('joi') -const pagination = require('./pagination') - -/** - * @type {Object} - */ -exports.index = { - query: { - ...pagination, - ...{ - ip: Joi.string().ip(), - os: Joi.string(), - status: Joi.string(), - port: Joi.number().port(), - version: Joi.string(), - orderBy: Joi.string(), - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - params: { - ip: Joi.string().ip(), - }, -} diff --git a/packages/core-api/lib/versions/2/schema/transactions.js b/packages/core-api/lib/versions/2/schema/transactions.js deleted file mode 100644 index d21de61550..0000000000 --- a/packages/core-api/lib/versions/2/schema/transactions.js +++ /dev/null @@ -1,147 +0,0 @@ -const app = require('@arkecosystem/core-container') -const Joi = require('@arkecosystem/crypto').validator.engine.joi -const pagination = require('./pagination') - -/** - * @type {Object} - */ -exports.index = { - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - id: Joi.string() - .hex() - .length(64), - blockId: Joi.string().regex(/^[0-9]+$/, 'numbers'), - type: Joi.number() - .integer() - .min(0), - version: Joi.number() - .integer() - .positive(), - senderPublicKey: Joi.string() - .hex() - .length(66), - senderId: Joi.string() - .alphanum() - .length(34), - recipientId: Joi.string() - .alphanum() - .length(34), - ownerId: Joi.string() - .alphanum() - .length(34), - timestamp: Joi.number() - .integer() - .min(0), - amount: Joi.number() - .integer() - .min(0), - fee: Joi.number() - .integer() - .min(0), - vendorFieldHex: Joi.string().hex(), - }, - }, -} - -/** - * @type {Object} - */ -exports.store = { - payload: { - transactions: Joi.arkTransactions() - .min(1) - .max( - app.resolveOptions('transactionPool').maxTransactionsPerRequest, - ) - .options({ stripUnknown: true }), - }, -} - -/** - * @type {Object} - */ -exports.show = { - params: { - id: Joi.string() - .hex() - .length(64), - }, -} - -/** - * @type {Object} - */ -exports.unconfirmed = { - query: pagination, -} - -/** - * @type {Object} - */ -exports.showUnconfirmed = { - params: { - id: Joi.string() - .hex() - .length(64), - }, -} - -/** - * @type {Object} - */ -exports.search = { - query: pagination, - payload: { - orderBy: Joi.string(), - id: Joi.string() - .hex() - .length(64), - blockId: Joi.string().regex(/^[0-9]+$/, 'numbers'), - type: Joi.number() - .integer() - .min(0), - version: Joi.number() - .integer() - .positive(), - senderPublicKey: Joi.string() - .hex() - .length(66), - senderId: Joi.string() - .alphanum() - .length(34), - recipientId: Joi.string() - .alphanum() - .length(34), - ownerId: Joi.string() - .alphanum() - .length(34), - vendorFieldHex: Joi.string().hex(), - timestamp: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - amount: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - fee: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - }, -} diff --git a/packages/core-api/lib/versions/2/schema/votes.js b/packages/core-api/lib/versions/2/schema/votes.js deleted file mode 100644 index c98075f7b7..0000000000 --- a/packages/core-api/lib/versions/2/schema/votes.js +++ /dev/null @@ -1,51 +0,0 @@ -const Joi = require('joi') -const pagination = require('./pagination') - -/** - * @type {Object} - */ -exports.index = { - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - id: Joi.string() - .hex() - .length(64), - blockId: Joi.string().regex(/^[0-9]+$/, 'numbers'), - version: Joi.number() - .integer() - .positive(), - senderPublicKey: Joi.string() - .hex() - .length(66), - senderId: Joi.string() - .alphanum() - .length(34), - recipientId: Joi.string() - .alphanum() - .length(34), - timestamp: Joi.number() - .integer() - .min(0), - amount: Joi.number() - .integer() - .min(0), - fee: Joi.number() - .integer() - .min(0), - vendorFieldHex: Joi.string().hex(), - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - params: { - id: Joi.string() - .hex() - .length(64), - }, -} diff --git a/packages/core-api/lib/versions/2/schema/wallets.js b/packages/core-api/lib/versions/2/schema/wallets.js deleted file mode 100644 index 1c086c9b9a..0000000000 --- a/packages/core-api/lib/versions/2/schema/wallets.js +++ /dev/null @@ -1,136 +0,0 @@ -const Joi = require('joi') -const pagination = require('./pagination') - -/** - * @type {Object} - */ -exports.index = { - query: { - ...pagination, - ...{ - orderBy: Joi.string(), - address: Joi.string() - .alphanum() - .length(34), - publicKey: Joi.string() - .hex() - .length(66), - secondPublicKey: Joi.string() - .hex() - .length(66), - vote: Joi.string() - .hex() - .length(66), - username: Joi.string(), - balance: Joi.number().integer(), - voteBalance: Joi.number() - .integer() - .min(0), - producedBlocks: Joi.number() - .integer() - .min(0), - missedBlocks: Joi.number() - .integer() - .min(0), - }, - }, -} - -/** - * @type {Object} - */ -exports.show = { - params: { - id: Joi.string(), - }, -} - -/** - * @type {Object} - */ -exports.transactions = { - params: { - id: Joi.string(), - }, - query: { - ...pagination, - orderBy: Joi.string(), - }, -} - -/** - * @type {Object} - */ -exports.transactionsSent = { - params: { - id: Joi.string(), - }, - query: { - ...pagination, - orderBy: Joi.string(), - }, -} - -/** - * @type {Object} - */ -exports.transactionsReceived = { - params: { - id: Joi.string(), - }, - query: { - ...pagination, - orderBy: Joi.string(), - }, -} - -/** - * @type {Object} - */ -exports.votes = { - params: { - id: Joi.string(), - }, - query: pagination, -} - -/** - * @type {Object} - */ -exports.search = { - query: pagination, - payload: { - orderBy: Joi.string(), - address: Joi.string() - .alphanum() - .length(34), - publicKey: Joi.string() - .hex() - .length(66), - secondPublicKey: Joi.string() - .hex() - .length(66), - vote: Joi.string() - .hex() - .length(66), - username: Joi.string(), - producedBlocks: Joi.number() - .integer() - .min(0), - missedBlocks: Joi.number() - .integer() - .min(0), - balance: Joi.object().keys({ - from: Joi.number().integer(), - to: Joi.number().integer(), - }), - voteBalance: Joi.object().keys({ - from: Joi.number() - .integer() - .min(0), - to: Joi.number() - .integer() - .min(0), - }), - }, -} diff --git a/packages/core-api/lib/versions/2/transformers/block.js b/packages/core-api/lib/versions/2/transformers/block.js deleted file mode 100644 index 45baf0495c..0000000000 --- a/packages/core-api/lib/versions/2/transformers/block.js +++ /dev/null @@ -1,44 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) -const { formatTimestamp, bignumify } = require('@arkecosystem/core-utils') - -/** - * Turns a "block" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const generator = database.walletManager.findByPublicKey( - model.generatorPublicKey, - ) - - model.reward = bignumify(model.reward) - model.totalFee = bignumify(model.totalFee) - - return { - id: model.id, - version: +model.version, - height: +model.height, - previous: model.previousBlock, - forged: { - reward: +model.reward.toFixed(), - fee: +model.totalFee.toFixed(), - total: +model.reward.plus(model.totalFee).toFixed(), - amount: +bignumify(model.totalAmount).toFixed(), - }, - payload: { - hash: model.payloadHash, - length: model.payloadLength, - }, - generator: { - username: generator.username, - address: generator.address, - publicKey: generator.publicKey, - }, - signature: model.blockSignature, - confirmations: model.confirmations, - transactions: model.numberOfTransactions, - timestamp: formatTimestamp(model.timestamp), - } -} diff --git a/packages/core-api/lib/versions/2/transformers/delegate.js b/packages/core-api/lib/versions/2/transformers/delegate.js deleted file mode 100644 index ef81fb2246..0000000000 --- a/packages/core-api/lib/versions/2/transformers/delegate.js +++ /dev/null @@ -1,44 +0,0 @@ -const { - bignumify, - formatTimestamp, - delegateCalculator, -} = require('@arkecosystem/core-utils') - -/** - * Turns a "delegate" object into a generic object. - * @param {Object} delegate - * @return {Object} - */ -module.exports = delegate => { - const data = { - username: delegate.username, - address: delegate.address, - publicKey: delegate.publicKey, - votes: +bignumify(delegate.voteBalance).toFixed(), - rank: delegate.rate, - blocks: { - produced: delegate.producedBlocks, - missed: delegate.missedBlocks, - }, - production: { - approval: delegateCalculator.calculateApproval(delegate), - productivity: delegateCalculator.calculateProductivity(delegate), - }, - forged: { - fees: +delegate.forgedFees.toFixed(), - rewards: +delegate.forgedRewards.toFixed(), - total: +delegate.forgedFees.plus(delegate.forgedRewards).toFixed(), - }, - } - - const lastBlock = delegate.lastBlock - - if (lastBlock) { - data.blocks.last = { - id: lastBlock.id, - timestamp: formatTimestamp(lastBlock.timestamp), - } - } - - return data -} diff --git a/packages/core-api/lib/versions/2/transformers/fee-statistics.js b/packages/core-api/lib/versions/2/transformers/fee-statistics.js deleted file mode 100644 index 2a5e5fd250..0000000000 --- a/packages/core-api/lib/versions/2/transformers/fee-statistics.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Turns a "fee-statistics" object into readable object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => ({ - type: model.type, - fees: { - minFee: parseInt(model.minFee), - maxFee: parseInt(model.maxFee), - avgFee: parseInt(model.avgFee), - }, -}) diff --git a/packages/core-api/lib/versions/2/transformers/peer.js b/packages/core-api/lib/versions/2/transformers/peer.js deleted file mode 100644 index 379050e9d2..0000000000 --- a/packages/core-api/lib/versions/2/transformers/peer.js +++ /dev/null @@ -1,26 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') - -/** - * Turns a "peer" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const peer = { - ip: model.ip, - port: +model.port, - version: model.version, - height: model.state ? model.state.height : model.height, - status: model.status, - os: model.os, - latency: model.delay, - } - - if (config.network.name !== 'mainnet') { - peer.hashid = model.hashid || 'unknown' - } - - return peer -} diff --git a/packages/core-api/lib/versions/2/transformers/ports.js b/packages/core-api/lib/versions/2/transformers/ports.js deleted file mode 100644 index 02f3f4ee21..0000000000 --- a/packages/core-api/lib/versions/2/transformers/ports.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Turns a "config" object into readable object. - * @param {Object} model - * @return {Object} - */ -module.exports = config => { - const result = {} - const keys = [ - '@arkecosystem/core-p2p', - '@arkecosystem/core-api', - '@arkecosystem/core-graphql', - '@arkecosystem/core-json-rpc', - '@arkecosystem/core-webhooks', - ] - - result[keys[0]] = config.plugins[keys[0]].port - - for (const [name, options] of Object.entries(config.plugins)) { - if (keys.includes(name) && options.enabled) { - if (options.server && options.server.enabled) { - result[name] = options.server.port - - continue - } - - result[name] = options.port - } - } - - return result -} diff --git a/packages/core-api/lib/versions/2/transformers/transaction.js b/packages/core-api/lib/versions/2/transformers/transaction.js deleted file mode 100644 index 5f6268f77c..0000000000 --- a/packages/core-api/lib/versions/2/transformers/transaction.js +++ /dev/null @@ -1,37 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') - -const { crypto } = require('@arkecosystem/crypto') -const { Transaction } = require('@arkecosystem/crypto').models - -const { bignumify, formatTimestamp } = require('@arkecosystem/core-utils') - -/** - * Turns a "transaction" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => { - const data = new Transaction(model.serialized.toString('hex')) - const lastBlock = blockchain.getLastBlock() - - return { - id: data.id, - blockId: model.blockId, - version: data.version, - type: data.type, - amount: +bignumify(data.amount).toFixed(), - fee: +bignumify(data.fee).toFixed(), - sender: crypto.getAddress(data.senderPublicKey, config.network.pubKeyHash), - recipient: data.recipientId, - signature: data.signature, - signSignature: data.signSignature, - signatures: data.signatures, - vendorField: data.vendorField, - asset: data.asset, - confirmations: model.block ? lastBlock.data.height - model.block.height : 0, - timestamp: formatTimestamp(model.timestamp || data.timestamp), - } -} diff --git a/packages/core-api/lib/versions/2/transformers/wallet.js b/packages/core-api/lib/versions/2/transformers/wallet.js deleted file mode 100644 index 584e5b6ae9..0000000000 --- a/packages/core-api/lib/versions/2/transformers/wallet.js +++ /dev/null @@ -1,15 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') - -/** - * Turns a "wallet" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => ({ - address: model.address, - publicKey: model.publicKey, - username: model.username, - secondPublicKey: model.secondPublicKey, - balance: +bignumify(model.balance).toFixed(), - isDelegate: !!model.username, -}) diff --git a/packages/core-api/lib/versions/2/utils.js b/packages/core-api/lib/versions/2/utils.js deleted file mode 100644 index 262aa477a1..0000000000 --- a/packages/core-api/lib/versions/2/utils.js +++ /dev/null @@ -1,106 +0,0 @@ -const Boom = require('boom') -const { - transformResource, - transformCollection, -} = require('../../utils/transformer') - -/** - * Create a pagination object for the request. - * @param {Hapi.Request} request - * @return {Object} - */ -const paginate = request => { - const pagination = { - offset: (request.query.page - 1) * request.query.limit || 0, - limit: request.query.limit || 100, - } - - if (request.query.offset) { - pagination.offset = request.query.offset - } - - return pagination -} - -/** - * Respond with a resource. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const respondWithResource = (request, data, transformer) => - data - ? { data: transformResource(request, data, transformer) } - : Boom.notFound() - -/** - * Respond with a collection. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const respondWithCollection = (request, data, transformer) => ({ - data: transformCollection(request, data, transformer), -}) - -/** - * Respond with data from cache. - * @param {Object} data - * @param {Hapi.Toolkit} h - * @return {Object} - */ -const respondWithCache = (data, h) => { - const { value, cached } = data - const lastModified = cached ? new Date(cached.stored) : new Date() - - return value.isBoom - ? h.response(value.output.payload).code(value.output.statusCode) - : h.response(value).header('Last-modified', lastModified.toUTCString()) -} - -/** - * Transform the given data into a resource. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const toResource = (request, data, transformer) => - transformResource(request, data, transformer) - -/** - * Transform the given data into a collection. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const toCollection = (request, data, transformer) => - transformCollection(request, data, transformer) - -/** - * Transform the given data into a pagination. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const toPagination = (request, data, transformer) => ({ - results: transformCollection(request, data.rows, transformer), - totalCount: data.count, -}) - -/** - * @type {Object} - */ -module.exports = { - paginate, - respondWithResource, - respondWithCollection, - respondWithCache, - toResource, - toCollection, - toPagination, -} diff --git a/packages/core-api/lib/versions/utils.js b/packages/core-api/lib/versions/utils.js deleted file mode 100644 index 5b1b32f49d..0000000000 --- a/packages/core-api/lib/versions/utils.js +++ /dev/null @@ -1,7 +0,0 @@ -exports.getCacheTimeout = () => { - const { - generateTimeout, - } = require('@arkecosystem/core-container').resolveOptions('api').cache - - return JSON.parse(generateTimeout) -} diff --git a/packages/core-api/package.json b/packages/core-api/package.json index 69cce4989e..420bd0fbc1 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,48 +1,71 @@ { - "name": "@arkecosystem/core-api", - "description": "Public API for Ark Core", - "version": "0.2.14", - "contributors": [ - "Kristjan Košič ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "@arkecosystem/core-transaction-pool": "~0.2", - "@arkecosystem/core-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "ajv": "^6.5.5", - "boom": "^7.3.0", - "bs58check": "^2.1.2", - "dayjs-ext": "^2.2.0", - "hapi-api-version": "^2.1.0", - "hapi-pagination": "https://github.com/faustbrian/hapi-pagination", - "hapi-rate-limit": "^3.0.0", - "ip": "^1.1.5", - "joi": "^14.3.0", - "lodash": "^4.17.11", - "lodash.orderby": "^4.6.0", - "lodash.snakecase": "^4.1.1" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2", - "axios": "^0.18.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-api", + "description": "Public API for Ark Core", + "version": "2.1.0", + "contributors": [ + "Kristjan Košič ", + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@arkecosystem/core-transaction-pool": "^2.1.0", + "@arkecosystem/core-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/lodash.orderby": "^4.6.4", + "@types/lodash.partition": "^4.6.4", + "@types/lodash.snakecase": "^4.1.4", + "ajv": "^6.6.2", + "boom": "^7.3.0", + "bs58check": "^2.1.2", + "dayjs-ext": "^2.2.0", + "delay": "^4.1.0", + "hapi-api-version": "^2.1.0", + "hapi-pagination": "https://github.com/faustbrian/hapi-pagination", + "hapi-rate-limit": "^3.0.0", + "ip": "^1.1.5", + "joi": "^14.3.0", + "lodash.orderby": "^4.6.0", + "lodash.partition": "^4.6.0", + "lodash.snakecase": "^4.1.1" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0", + "@types/boom": "^7.2.1", + "@types/ip": "^1.1.0", + "@types/joi": "^14.0.1", + "axios": "^0.18.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-api/src/defaults.ts b/packages/core-api/src/defaults.ts new file mode 100644 index 0000000000..647268235b --- /dev/null +++ b/packages/core-api/src/defaults.ts @@ -0,0 +1,84 @@ +import { resolve } from "path"; + +export const defaults = { + enabled: false, + host: process.env.CORE_API_HOST || "0.0.0.0", + port: process.env.CORE_API_PORT || 4003, + cache: { + enabled: true, + /** + * How many seconds the server will try to complete the request and cache the result. + * + * Defaults to 8 seconds, set it to false if you do not care about the timeout. + * + * Setting it to false can result in requests never being completed, which is usually + * caused by low-spec servers that are unable to handle the heavy load that results + * out of SQL queries on the blocks and transactions tables. + * + * If you experience issues with the cache timeout, which is indicated by a 503 status codes, + * you should consider upgrading your hardware or tweak your PostgreSQL settings. + */ + generateTimeout: process.env.CORE_API_CACHE_TIMEOUT || 8000, + }, + // @see https://hapijs.com/api#-serveroptionstls + ssl: { + enabled: process.env.CORE_API_SSL, + host: process.env.CORE_API_SSL_HOST || "0.0.0.0", + port: process.env.CORE_API_SSL_PORT || 8443, + key: process.env.CORE_API_SSL_KEY, + cert: process.env.CORE_API_SSL_CERT, + }, + // @see https://github.com/p-meier/hapi-api-version + versions: { + validVersions: [1, 2], + defaultVersion: 1, + basePath: "/api/", + vendorName: "core-api", + }, + // @see https://github.com/wraithgar/hapi-rate-limit + rateLimit: { + enabled: !process.env.CORE_API_RATE_LIMIT, + pathLimit: false, + userLimit: process.env.CORE_API_RATE_LIMIT_USER_LIMIT || 300, + userCache: { + expiresIn: process.env.CORE_API_RATE_LIMIT_USER_EXPIRES || 60000, + }, + ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + // @see https://github.com/fknop/hapi-pagination + pagination: { + limit: 100, + include: [ + "/api/v2/blocks", + "/api/v2/blocks/{id}/transactions", + "/api/v2/blocks/search", + "/api/v2/delegates", + "/api/v2/delegates/{id}/blocks", + "/api/v2/delegates/{id}/voters", + "/api/v2/delegates/search", + "/api/v2/peers", + "/api/v2/transactions", + "/api/v2/transactions/search", + "/api/v2/transactions/unconfirmed", + "/api/v2/votes", + "/api/v2/wallets", + "/api/v2/wallets/top", + "/api/v2/wallets/{id}/transactions", + "/api/v2/wallets/{id}/transactions/received", + "/api/v2/wallets/{id}/transactions/sent", + "/api/v2/wallets/{id}/votes", + "/api/v2/wallets/search", + ], + }, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + plugins: [ + { + plugin: resolve(__dirname, "./versions/1"), + routes: { prefix: "/api/v1" }, + }, + { + plugin: resolve(__dirname, "./versions/2"), + routes: { prefix: "/api/v2" }, + }, + ], +}; diff --git a/packages/core-api/src/index.ts b/packages/core-api/src/index.ts new file mode 100644 index 0000000000..cb7249fe8e --- /dev/null +++ b/packages/core-api/src/index.ts @@ -0,0 +1,5 @@ +export * from "./defaults"; +export * from "./server"; +export * from "./interfaces"; +export * from "./repositories"; +export * from "./plugin"; diff --git a/packages/core-api/src/interfaces/index.ts b/packages/core-api/src/interfaces/index.ts new file mode 100644 index 0000000000..5ed29ebdf6 --- /dev/null +++ b/packages/core-api/src/interfaces/index.ts @@ -0,0 +1 @@ +export * from "./repository"; diff --git a/packages/core-api/src/interfaces/repository.ts b/packages/core-api/src/interfaces/repository.ts new file mode 100644 index 0000000000..f91ed6026c --- /dev/null +++ b/packages/core-api/src/interfaces/repository.ts @@ -0,0 +1,9 @@ +export interface IRepository { + databaseService: any; + cache: any; + model: any; + query: any; + columns: string[]; + + getModel(): object; +} diff --git a/packages/core-api/src/plugin.ts b/packages/core-api/src/plugin.ts new file mode 100644 index 0000000000..be469c15b2 --- /dev/null +++ b/packages/core-api/src/plugin.ts @@ -0,0 +1,28 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { Server } from "./server"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "api", + async register(container: Container.IContainer, options) { + if (!options.enabled) { + container.resolvePlugin("logger").info("Public API is disabled :grey_exclamation:"); + + return false; + } + + const server = new Server(options); + await server.start(); + + return server; + }, + async deregister(container: Container.IContainer, options) { + if (options.enabled) { + container.resolvePlugin("logger").info(`Stopping Public API`); + + await container.resolvePlugin("api").stop(); + } + }, +}; diff --git a/packages/core-api/src/plugins/caster.ts b/packages/core-api/src/plugins/caster.ts new file mode 100644 index 0000000000..f1af31b848 --- /dev/null +++ b/packages/core-api/src/plugins/caster.ts @@ -0,0 +1,50 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import Hapi from "hapi"; + +function isBoolean(value) { + try { + return value.toLowerCase() === "true" || value.toLowerCase() === "false"; + } catch (e) { + return false; + } +} + +function isNumber(value) { + return !isNaN(value); +} + +const register = async (server: Hapi.Server, options: object): Promise => { + server.ext({ + type: "onPreHandler", + method: (request, h) => { + const query = request.query; + + Object.keys(query).map((key, index) => { + // Special fields that should always be a "string" + if (key === "id" || key === "blockId" || key === "previousBlock") { + query[key] = query[key]; + } else if (isBoolean(query[key])) { + query[key] = query[key].toLowerCase() === "true"; + } else if (isNumber(query[key])) { + query[key] = + // @ts-ignore + // tslint:disable-next-line triple-equals + query[key] == Number(query[key]) ? Number(query[key]) : bignumify(query[key]).toString(); + } else { + query[key] = query[key]; + } + }); + + // @ts-ignore + request.query = query; + + return h.continue; + }, + }); +}; + +export = { + register, + name: "core-caster", + version: "1.0.0", +}; diff --git a/packages/core-api/src/plugins/endpoint-version.ts b/packages/core-api/src/plugins/endpoint-version.ts new file mode 100644 index 0000000000..bbe8f4f191 --- /dev/null +++ b/packages/core-api/src/plugins/endpoint-version.ts @@ -0,0 +1,31 @@ +import Boom from "boom"; +import Hapi from "hapi"; + +const versionRegex = /^\/api\/v([0-9])\//; + +const register = async (server: Hapi.Server, options: any): Promise => { + server.ext({ + type: "onRequest", + async method(request, h) { + const match = versionRegex.exec(request.path); + + if (match && match.length === 2) { + const apiVersion = parseInt(match[1], 10); + + if (!options.validVersions.includes(apiVersion)) { + return Boom.badRequest(`Invalid api-version! Valid values: ${options.validVersions.join()}`); + } + + request.pre.apiVersion = apiVersion; + } + + return h.continue; + }, + }); +}; + +export = { + register, + name: "endpoint-version", + version: "1.0.0", +}; diff --git a/packages/core-api/src/plugins/set-headers.ts b/packages/core-api/src/plugins/set-headers.ts new file mode 100644 index 0000000000..a31e66f880 --- /dev/null +++ b/packages/core-api/src/plugins/set-headers.ts @@ -0,0 +1,28 @@ +import Boom from "boom"; +import Hapi from "hapi"; + +const register = async (server: Hapi.Server, options: object): Promise => { + server.ext({ + type: "onPreResponse", + async method(request, h) { + const response = request.response; + + // @ts-ignore + if (response.isBoom && response.data) { + // Deleting the property beforehand makes it appear last in the response body. + // @ts-ignore + delete response.output.payload.error; + // @ts-ignore + response.output = { payload: { error: response.data } }; + } + + return h.continue; + }, + }); +}; + +export = { + register, + name: "set-headers", + version: "1.0.0", +}; diff --git a/packages/core-api/src/plugins/validation/formats/address.ts b/packages/core-api/src/plugins/validation/formats/address.ts new file mode 100644 index 0000000000..7e4ecc56d7 --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/address.ts @@ -0,0 +1,17 @@ +import { app } from "@arkecosystem/core-container"; +import * as bs58check from "bs58check"; + +export function registerAddressFormat(ajv) { + const config = app.getConfig(); + + ajv.addFormat("address", { + type: "string", + validate: value => { + try { + return bs58check.decode(value)[0] === config.get("network.pubKeyHash"); + } catch (e) { + return false; + } + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/csv.ts b/packages/core-api/src/plugins/validation/formats/csv.ts new file mode 100644 index 0000000000..f3a2af591f --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/csv.ts @@ -0,0 +1,14 @@ +export function registerCsvFormat(ajv) { + ajv.addFormat("csv", { + type: "string", + validate: value => { + try { + const a = value.split(","); + + return a.length > 0 && a.length <= 1000; + } catch (e) { + return false; + } + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/hex.ts b/packages/core-api/src/plugins/validation/formats/hex.ts new file mode 100644 index 0000000000..77ca40ebb1 --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/hex.ts @@ -0,0 +1,14 @@ +export function registerHexFormat(ajv) { + ajv.addFormat("hex", { + type: "string", + validate: value => { + try { + Buffer.from(value, "hex"); + + return true; + } catch (e) { + return false; + } + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/ip.ts b/packages/core-api/src/plugins/validation/formats/ip.ts new file mode 100644 index 0000000000..2d0639c83e --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/ip.ts @@ -0,0 +1,8 @@ +import * as ip from "ip"; + +export function registerIpFormat(ajv) { + ajv.addFormat("ip", { + type: "string", + validate: value => ip.isV4Format(value) || ip.isV6Format(value), + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/parseInt.ts b/packages/core-api/src/plugins/validation/formats/parseInt.ts new file mode 100644 index 0000000000..8e8da2ff7f --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/parseInt.ts @@ -0,0 +1,14 @@ +export function registerParseIntFormat(ajv) { + ajv.addFormat("parsedInt", { + type: "string", + validate: value => { + if (isNaN(value) || parseInt(value, 10) !== value || isNaN(parseInt(value, 10))) { + return false; + } + + value = parseInt(value, 10); + + return true; + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/publicKey.ts b/packages/core-api/src/plugins/validation/formats/publicKey.ts new file mode 100644 index 0000000000..df19146296 --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/publicKey.ts @@ -0,0 +1,12 @@ +export function registerPublicKeyFormat(ajv) { + ajv.addFormat("publicKey", { + type: "string", + validate: value => { + try { + return Buffer.from(value, "hex").length === 33; + } catch (e) { + return false; + } + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/signature.ts b/packages/core-api/src/plugins/validation/formats/signature.ts new file mode 100644 index 0000000000..1f96dac2d4 --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/signature.ts @@ -0,0 +1,12 @@ +export function registerSignatureFormat(ajv) { + ajv.addFormat("signature", { + type: "string", + validate: value => { + try { + return Buffer.from(value, "hex").length < 73; + } catch (e) { + return false; + } + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/formats/vendorField.ts b/packages/core-api/src/plugins/validation/formats/vendorField.ts new file mode 100644 index 0000000000..4b634941c0 --- /dev/null +++ b/packages/core-api/src/plugins/validation/formats/vendorField.ts @@ -0,0 +1,12 @@ +export function registerVendorFieldFormat(ajv) { + ajv.addFormat("vendorField", { + type: "string", + validate: value => { + try { + return Buffer.from(value).length < 65; + } catch (e) { + return false; + } + }, + }); +} diff --git a/packages/core-api/src/plugins/validation/index.ts b/packages/core-api/src/plugins/validation/index.ts new file mode 100644 index 0000000000..66d75776ae --- /dev/null +++ b/packages/core-api/src/plugins/validation/index.ts @@ -0,0 +1,81 @@ +import AJV from "ajv"; +import Boom from "boom"; +import * as fs from "fs"; +import Hapi from "hapi"; +import * as path from "path"; + +// SOF: IMPORT CUSTOM AJV FORMATS +import { registerAddressFormat } from "./formats/address"; +import { registerCsvFormat } from "./formats/csv"; +import { registerHexFormat } from "./formats/hex"; +import { registerIpFormat } from "./formats/ip"; +import { registerParseIntFormat } from "./formats/parseInt"; +import { registerPublicKeyFormat } from "./formats/publicKey"; +import { registerSignatureFormat } from "./formats/signature"; +import { registerVendorFieldFormat } from "./formats/vendorField"; +// EOF: IMPORT CUSTOM AJV FORMATS + +const PLUGIN_NAME = "hapi-ajv"; + +const register = async (server: Hapi.Server, options: object): Promise => { + const ajv = new AJV(); + registerCsvFormat(ajv); + registerAddressFormat(ajv); + registerHexFormat(ajv); + registerIpFormat(ajv); + registerParseIntFormat(ajv); + registerPublicKeyFormat(ajv); + registerSignatureFormat(ajv); + registerVendorFieldFormat(ajv); + + const validate = (schema, data) => { + return ajv.validate(schema, data) ? null : ajv.errors; + }; + + const createErrorResponse = (request, h, errors) => { + if (request.pre.apiVersion === 1) { + return h + .response({ + path: errors[0].dataPath, + error: errors[0].message, + success: false, + }) + .takeover(); + } + + return Boom.badData(errors); + }; + + server.ext({ + type: "onPreHandler", + method: (request, h) => { + const config = request.route.settings.plugins[PLUGIN_NAME] || {}; + + let errors; + + if (config.payloadSchema) { + errors = validate(config.payloadSchema, request.payload); + + if (errors) { + return createErrorResponse(request, h, errors); + } + } + + if (config.querySchema) { + errors = validate(config.querySchema, request.query); + + if (errors) { + return createErrorResponse(request, h, errors); + } + } + + return h.continue; + }, + }); +}; + +export = { + register, + name: PLUGIN_NAME, + version: "1.0.0", +}; diff --git a/packages/core-api/src/repositories/blocks.ts b/packages/core-api/src/repositories/blocks.ts new file mode 100644 index 0000000000..9d312fbf50 --- /dev/null +++ b/packages/core-api/src/repositories/blocks.ts @@ -0,0 +1,137 @@ +import { IRepository } from "../interfaces"; +import { Repository } from "./repository"; +import { buildFilterQuery } from "./utils/build-filter-query"; + +export class BlockRepository extends Repository implements IRepository { + constructor() { + super(); + } + + /** + * Get all blocks for the given parameters. + * @param {Object} parameters + * @return {Object} + */ + public async findAll(parameters: any = {}): Promise { + const selectQuery = this.query.select().from(this.query); + + const conditions = Object.entries(this._formatConditions(parameters)); + + if (conditions.length) { + const first = conditions.shift(); + + selectQuery.where(this.query[first[0]].equals(first[1])); + + for (const condition of conditions) { + selectQuery.and(this.query[condition[0]].equals(condition[1])); + } + } + + return this._findManyWithCount(selectQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: this.__orderBy(parameters), + }); + } + + /** + * Get all blocks for the given generator. + * @param {String} generatorPublicKey + * @param {Object} paginator + * @return {Object} + */ + public async findAllByGenerator(generatorPublicKey, paginator): Promise { + return this.findAll({ ...{ generatorPublicKey }, ...paginator }); + } + + /** + * Get a block. + * @param {Number} id + * @return {Object} + */ + public async findById(value): Promise { + const query = this.query + .select() + .from(this.query) + .where(this.query.id.equals(value)); + + // ensure that the value is not greater than 2147483647 (psql max int size) + const height = +value; + if (height <= 2147483647) { + query.or(this.query.height.equals(height)); + } + + return this._find(query); + } + + /** + * Get the last block for the given generator. + * TODO is this right? + * @param {String} generatorPublicKey + * @return {Object} + */ + public async findLastByPublicKey(generatorPublicKey): Promise { + const query = this.query + .select(this.query.id, this.query.timestamp) + .from(this.query) + .where(this.query.generator_public_key.equals(generatorPublicKey)) + .order(this.query.height.desc); + + return this._find(query); + } + + /** + * Search all blocks. + * @param {Object} parameters + * @return {Object} + */ + public async search(parameters): Promise { + const selectQuery = this.query.select().from(this.query); + + const conditions = buildFilterQuery(this._formatConditions(parameters), { + exact: ["id", "version", "previous_block", "payload_hash", "generator_public_key", "block_signature"], + between: [ + "timestamp", + "height", + "number_of_transactions", + "total_amount", + "total_fee", + "reward", + "payload_length", + ], + }); + + if (conditions.length) { + const first = conditions.shift(); + + selectQuery.where(this.query[first.column][first.method](first.value)); + + for (const condition of conditions) { + selectQuery.and(this.query[condition.column][condition.method](condition.value)); + } + } + + return this._findManyWithCount(selectQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: this.__orderBy(parameters), + }); + } + + public getModel(): any { + return (this.databaseService.connection as any).models.block; + } + + public __orderBy(parameters): string[] { + if (!parameters.orderBy) { + return ["height", "desc"]; + } + + const orderBy = parameters.orderBy.split(":").map(p => p.toLowerCase()); + if (orderBy.length !== 2 || ["desc", "asc"].includes(orderBy[1]) !== true) { + return ["height", "desc"]; + } + + return orderBy; + } +} diff --git a/packages/core-api/src/repositories/index.ts b/packages/core-api/src/repositories/index.ts new file mode 100644 index 0000000000..585fe72b50 --- /dev/null +++ b/packages/core-api/src/repositories/index.ts @@ -0,0 +1,7 @@ +import { BlockRepository } from "./blocks"; +import { TransactionsRepository } from "./transactions"; + +const blocksRepository = new BlockRepository(); +const transactionsRepository = new TransactionsRepository(); + +export { blocksRepository, transactionsRepository, BlockRepository, TransactionsRepository }; diff --git a/packages/core-api/src/repositories/repository.ts b/packages/core-api/src/repositories/repository.ts new file mode 100644 index 0000000000..9dae5dcf47 --- /dev/null +++ b/packages/core-api/src/repositories/repository.ts @@ -0,0 +1,110 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, TransactionPool } from "@arkecosystem/core-interfaces"; +import snakeCase from "lodash/snakeCase"; +import { IRepository } from "../interfaces"; + +export abstract class Repository implements IRepository { + public databaseService = app.resolvePlugin("database"); + public cache = this.databaseService.cache; + public transactionPool = app.resolvePlugin("transactionPool"); + public model = this.getModel(); + public query = this.model.query(); + public columns: string[] = []; + + protected constructor() { + this.__mapColumns(); + } + + // todo: Introduce a generic param to return type-safe models + public abstract getModel(): any; + + public async _find(query): Promise { + return (this.databaseService.connection as any).query.oneOrNone(query.toQuery()); + } + + public async _findMany(query): Promise { + return (this.databaseService.connection as any).query.manyOrNone(query.toQuery()); + } + + public async _findManyWithCount(selectQuery, { limit, offset, orderBy }): Promise { + if (Array.isArray(orderBy) && this.columns.includes(orderBy[0])) { + selectQuery.order(this.query[snakeCase(orderBy[0])][orderBy[1]]); + } + + const offsetIsSet = Number.isInteger(offset) && offset > 0; + const limitIsSet = Number.isInteger(limit); + + if (!offsetIsSet && !limitIsSet) { + // tslint:disable-next-line:no-shadowed-variable + const rows = await this._findMany(selectQuery); + + return { rows, count: rows.length }; + } + + selectQuery.offset(offset).limit(limit); + + const rows = await this._findMany(selectQuery); + + if (rows.length < limit) { + return { rows, count: offset + rows.length }; + } + + // Get the last rows=... from something that looks like (1 column, few rows): + // + // QUERY PLAN + // ------------------------------------------------------------------ + // Limit (cost=15.34..15.59 rows=100 width=622) + // -> Sort (cost=15.34..15.64 rows=120 width=622) + // Sort Key: "timestamp" DESC + // -> Seq Scan on transactions (cost=0.00..11.20 rows=120 width=622) + + let count = 0; + const explainSql = `EXPLAIN ${selectQuery.toString()}`; + for (const row of await (this.databaseService.connection as any).query.manyOrNone(explainSql)) { + const line: any = Object.values(row)[0]; + const match = line.match(/rows=([0-9]+)/); + if (match !== null) { + count = Number(match[1]); + } + } + + return { rows, count: Math.max(count, rows.length) }; + } + + public _makeCountQuery(): Promise { + return this.query.select("count(*) AS count").from(this.query); + } + + public _makeEstimateQuery(): Promise { + return this.query.select("count(*) AS count").from(`${this.model.getTable()} TABLESAMPLE SYSTEM (100)`); + } + + public _formatConditions(parameters): any { + const columns = this.model.getColumnSet().columns.map(column => ({ + name: column.name, + prop: column.prop || column.name, + })); + + return Object.keys(parameters) + .filter(arg => this.columns.includes(arg)) + .reduce((items, item) => { + const column = columns.find(value => value.name === item || value.prop === item); + + column ? (items[column.name] = parameters[item]) : delete items[item]; + + return items; + }, {}); + } + + public __mapColumns(): void { + this.columns = []; + + for (const column of this.model.getColumnSet().columns) { + this.columns.push(column.name); + + if (column.prop) { + this.columns.push(column.prop); + } + } + } +} diff --git a/packages/core-api/src/repositories/transactions.ts b/packages/core-api/src/repositories/transactions.ts new file mode 100644 index 0000000000..59a0a6dbc4 --- /dev/null +++ b/packages/core-api/src/repositories/transactions.ts @@ -0,0 +1,521 @@ +import { constants, slots } from "@arkecosystem/crypto"; +import dayjs from "dayjs-ext"; +import partition from "lodash/partition"; +import snakeCase from "lodash/snakeCase"; +import { IRepository } from "../interfaces"; +import { Repository } from "./repository"; +import { buildFilterQuery } from "./utils/build-filter-query"; + +export class TransactionsRepository extends Repository implements IRepository { + constructor() { + super(); + } + + /** + * Get all transactions. + * @param {Object} params + * @return {Object} + */ + public async findAll(parameters: any = {}, sequenceOrder: "asc" | "desc" = "desc"): Promise { + const selectQuery = this.query.select().from(this.query); + + if (parameters.senderId) { + const senderPublicKey = this.__publicKeyFromAddress(parameters.senderId); + + if (!senderPublicKey) { + return { rows: [], count: 0 }; + } + + parameters.senderPublicKey = senderPublicKey; + } + + const conditions = Object.entries(this._formatConditions(parameters)); + + if (conditions.length) { + const first = conditions.shift(); + + selectQuery.where(this.query[first[0]].equals(first[1])); + + for (const condition of conditions) { + selectQuery.and(this.query[condition[0]].equals(condition[1])); + } + } + + if (parameters.ownerId) { + const owner = this.databaseService.walletManager.findByAddress(parameters.ownerId); + + selectQuery.and(this.query.sender_public_key.equals(owner.publicKey)); + selectQuery.or(this.query.recipient_id.equals(owner.address)); + } + + this.__orderBy(selectQuery, parameters, sequenceOrder); + + const results = await this._findManyWithCount(selectQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: null, + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + /** + * Get all transactions (LEGACY, for V1 only). + * @param {Object} params + * @return {Object} + */ + public async findAllLegacy(parameters: any = {}): Promise { + const selectQuery = this.query + .select(this.query.block_id, this.query.serialized, this.query.timestamp) + .from(this.query); + + if (parameters.senderId) { + parameters.senderPublicKey = this.__publicKeyFromAddress(parameters.senderId); + } + + const applyConditions = queries => { + const conditions = Object.entries(this._formatConditions(parameters)); + + if (conditions.length) { + const first = conditions.shift(); + + for (const item of queries) { + item.where(this.query[first[0]].equals(first[1])); + + for (const [key, value] of conditions) { + item.or(this.query[key].equals(value)); + } + } + } + }; + + applyConditions([selectQuery]); + + this.__orderBy(selectQuery, parameters); + + const results = await this._findManyWithCount(selectQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: null, + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + /** + * Get all transactions for the given Wallet object. + * @param {Wallet} wallet + * @param {Object} parameters + * @return {Object} + */ + public async findAllByWallet(wallet, parameters: any = {}): Promise { + const selectQuery = this.query + .select(this.query.block_id, this.query.serialized, this.query.timestamp) + .from(this.query); + + const applyConditions = queries => { + for (const item of queries) { + item.where(this.query.sender_public_key.equals(wallet.publicKey)).or( + this.query.recipient_id.equals(wallet.address), + ); + } + }; + + applyConditions([selectQuery]); + + this.__orderBy(selectQuery, parameters); + + const results = await this._findManyWithCount(selectQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: null, + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + /** + * Get all transactions for the given sender public key. + * @param {String} senderPublicKey + * @param {Object} parameters + * @return {Object} + */ + public async findAllBySender(senderPublicKey, parameters: any = {}): Promise { + return this.findAll({ ...{ senderPublicKey }, ...parameters }); + } + + /** + * Get all transactions for the given recipient address. + * @param {String} recipientId + * @param {Object} parameters + * @return {Object} + */ + public async findAllByRecipient(recipientId, parameters: any = {}): Promise { + return this.findAll({ ...{ recipientId }, ...parameters }); + } + + /** + * Get all vote transactions for the given sender public key. + * TODO rename to findAllVotesBySender or not? + * @param {String} senderPublicKey + * @param {Object} parameters + * @return {Object} + */ + public async allVotesBySender(senderPublicKey, parameters: any = {}): Promise { + return this.findAll({ + ...{ senderPublicKey, type: constants.TransactionTypes.Vote }, + ...parameters, + }); + } + + /** + * Get all transactions for the given block. + * @param {Number} blockId + * @param {Object} parameters + * @return {Object} + */ + public async findAllByBlock(blockId, parameters: any = {}): Promise { + return this.findAll({ ...{ blockId }, ...parameters }, "asc"); + } + + /** + * Get all transactions for the given type. + * @param {Number} type + * @param {Object} parameters + * @return {Object} + */ + public async findAllByType(type, parameters: any = {}): Promise { + return this.findAll({ ...{ type }, ...parameters }); + } + + /** + * Get a transaction. + * @param {Number} id + * @return {Object} + */ + public async findById(id): Promise { + const query = this.query + .select(this.query.block_id, this.query.serialized, this.query.timestamp) + .from(this.query) + .where(this.query.id.equals(id)); + + const transaction = await this._find(query); + + return this.__mapBlocksToTransactions(transaction); + } + + /** + * Get a transactions for the given type and id. + * @param {Number} type + * @param {Number} id + * @return {Object} + */ + public async findByTypeAndId(type, id): Promise { + const query = this.query + .select(this.query.block_id, this.query.serialized, this.query.timestamp) + .from(this.query) + .where(this.query.id.equals(id).and(this.query.type.equals(type))); + + const transaction = await this._find(query); + + return this.__mapBlocksToTransactions(transaction); + } + + /** + * Get transactions for the given ids. + * @param {Array} ids + * @return {Object} + */ + public async findByIds(ids): Promise { + const query = this.query + .select(this.query.block_id, this.query.serialized, this.query.timestamp) + .from(this.query) + .where(this.query.id.in(ids)); + + return this._findMany(query); + } + + /** + * Get all transactions that have a vendor field. + * @return {Object} + */ + public async findWithVendorField(): Promise { + const query = this.query + .select(this.query.block_id, this.query.serialized, this.query.timestamp) + .from(this.query) + .where(this.query.vendor_field_hex.isNotNull()); + + const transactions = await this._findMany(query); + + return this.__mapBlocksToTransactions(transactions); + } + + /** + * Calculates min, max and average fee statistics based on transactions table + * @return {Object} + */ + public async getFeeStatistics(): Promise { + const query = this.query + .select( + this.query.type, + this.query.fee.min("minFee"), + this.query.fee.max("maxFee"), + this.query.fee.avg("avgFee"), + this.query.timestamp.max("timestamp"), + ) + .from(this.query) + .where( + this.query.timestamp.gte( + slots.getTime( + dayjs() + .subtract(30, "day") + .valueOf(), + ), + ), + ) + .and(this.query.fee.gte(this.transactionPool.options.dynamicFees.minFeeBroadcast)) + .group(this.query.type) + .order('"timestamp" DESC'); + + return this._findMany(query); + } + + /** + * Search all transactions. + * + * @param {Object} parameters + * @param {Number} [parameters.limit] - Limit the number of results + * @param {Number} [parameters.offset] - Skip some results + * @param {Array} [parameters.orderBy] - Order of the results + * @param {String} [parameters.id] - Search by transaction id + * @param {String} [parameters.blockId] - Search by block id + * @param {String} [parameters.recipientId] - Search by recipient address + * @param {String} [parameters.senderPublicKey] - Search by sender public key + * @param {String} [parameters.senderId] - Search by sender address + * @param {Array} [parameters.addresses] - Search by senders or recipients addresses + * @param {Number} [parameters.type] - Search by transaction type + * @param {Number} [parameters.version] - Search by transaction version + * @param {Object} [parameters.timestamp] - Search by transaction date + * @param {Number} [parameters.timestamp.from] - Since date + * @param {Number} [parameters.timestamp.to] - Until date + * @param {Object} [parameters.amount] - Search by transaction amount + * @param {Number} [parameters.amount.from] - From amount + * @param {Number} [parameters.amount.to] - To date + * @param {Object} [parameters.fee] - Search by transaction fee + * @param {Number} [parameters.fee.from] - From fee + * @param {Number} [parameters.fee.to] - To fee + * @return {Object} + */ + public async search(parameters): Promise { + const selectQuery = this.query.select().from(this.query); + + const filters = { + exact: ["id", "block_id", "type", "version"], + between: ["timestamp", "amount", "fee"], + wildcard: ["vendor_field_hex"], + in: [], + }; + + if (parameters.senderId) { + const senderPublicKey = this.__publicKeyFromAddress(parameters.senderId); + + if (senderPublicKey) { + parameters.senderPublicKey = senderPublicKey; + } else { + return { count: 0, rows: [] }; + } + } + + if (parameters.recipientId) { + filters.exact.push("recipient_id"); + } + if (parameters.senderPublicKey) { + filters.exact.push("sender_public_key"); + } + + // When both participants, sender and recipient, are provided, searching by addresses is not useful + if (parameters.addresses) { + if (!parameters.recipientId) { + filters.in.push("recipient_id"); + parameters.recipientId = parameters.addresses; + } + if (!parameters.senderPublicKey) { + filters.in.push("sender_public_key"); + parameters.senderPublicKey = parameters.addresses.map(address => { + return this.__publicKeyFromAddress(address); + }); + } + } + + const conditions = buildFilterQuery(this._formatConditions(parameters), filters); + + /* + * Searching by `addresses` could create queries: + * - 1 `senderPublicKey` AND n `recipientId` + * - n `senderPublicKey` AND 1 `recipientId`. + * - n `senderPublicKey` OR n `recipientId`. + */ + if (conditions.length) { + const [participants, rest] = partition(conditions, condition => { + return ["sender_public_key", "recipient_id"].indexOf(condition.column) > -1; + }); + + if (participants.length > 0) { + const [first, last] = participants; + selectQuery.where(this.query[first.column][first.method](first.value)); + + if (last) { + const usesInOperator = participants.every(condition => condition.method === "in"); + if (usesInOperator) { + selectQuery.or(this.query[last.column][last.method](last.value)); + } else { + // This search is 1 `senderPublicKey` and 1 `recipientId` + selectQuery.and(this.query[last.column][last.method](last.value)); + } + } + } else if (rest.length) { + const first = rest.shift(); + selectQuery.where(this.query[first.column][first.method](first.value)); + } + + for (const condition of rest) { + selectQuery.and(this.query[condition.column][condition.method](condition.value)); + } + } + + this.__orderBy(selectQuery, parameters); + + const results = await this._findManyWithCount(selectQuery, { + limit: parameters.limit || 100, + offset: parameters.offset || 0, + orderBy: null, + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + public getModel(): object { + return (this.databaseService.connection as any).models.transaction; + } + + /** + * [__mapBlocksToTransactions description] + * @param {Array|Object} data + * @return {Object} + */ + public async __mapBlocksToTransactions(data): Promise { + const blockQuery = (this.databaseService.connection as any).models.block.query(); + + // Array... + if (Array.isArray(data)) { + // 1. get heights from cache + const missingFromCache = []; + + for (let i = 0; i < data.length; i++) { + const cachedBlock = this.__getBlockCache(data[i].blockId); + + if (cachedBlock) { + data[i].block = cachedBlock; + } else { + missingFromCache.push({ + index: i, + blockId: data[i].blockId, + }); + } + } + + // 2. get missing heights from database + if (missingFromCache.length) { + const query = blockQuery + .select(blockQuery.id, blockQuery.height) + .from(blockQuery) + .where(blockQuery.id.in(missingFromCache.map(d => d.blockId))) + .group(blockQuery.id); + + const blocks = await this._findMany(query); + + for (const missing of missingFromCache) { + const block = blocks.find(item => item.id === missing.blockId); + if (block) { + data[missing.index].block = block; + this.__setBlockCache(block); + } + } + } + + return data; + } + + // Object... + if (data) { + const cachedBlock = this.__getBlockCache(data.blockId); + + if (cachedBlock) { + data.block = cachedBlock; + } else { + const query = blockQuery + .select(blockQuery.id, blockQuery.height) + .from(blockQuery) + .where(blockQuery.id.equals(data.blockId)); + + data.block = await this._find(query); + + this.__setBlockCache(data.block); + } + } + + return data; + } + + /** + * Tries to retrieve the height of the block from the cache + * @param {String} blockId + * @return {Object|null} + */ + public __getBlockCache(blockId): any { + const height = this.cache.get(`heights:${blockId}`); + + return height ? { height, id: blockId } : null; + } + + /** + * Stores the height of the block on the cache + * @param {Object} block + * @param {String} block.id + * @param {Number} block.height + */ + public __setBlockCache({ id, height }): void { + this.cache.set(`heights:${id}`, height); + } + + /** + * Retrieves the publicKey of the address from the WalletManager in-memory data + * @param {String} senderId + * @return {String} + */ + public __publicKeyFromAddress(senderId): string { + if (this.databaseService.walletManager.exists(senderId)) { + return this.databaseService.walletManager.findByAddress(senderId).publicKey; + } + + return null; + } + + public __orderBy(selectQuery, parameters, sequenceOrder: "asc" | "desc" = "desc"): void { + const orderBy = parameters.orderBy + ? parameters.orderBy.split(":").map(p => p.toLowerCase()) + : ["timestamp", "desc"]; + + selectQuery.order(this.query[snakeCase(orderBy[0])][orderBy[1]]); + + selectQuery.order(this.query.sequence[sequenceOrder]); + } +} diff --git a/packages/core-api/src/repositories/utils/build-filter-query.ts b/packages/core-api/src/repositories/utils/build-filter-query.ts new file mode 100644 index 0000000000..dd2e26f7fa --- /dev/null +++ b/packages/core-api/src/repositories/utils/build-filter-query.ts @@ -0,0 +1,77 @@ +export function buildFilterQuery(parameters, filters) { + const where = []; + + if (filters.hasOwnProperty("exact")) { + for (const elem of filters.exact) { + if (typeof parameters[elem] !== "undefined") { + where.push({ + column: elem, + method: "equals", + value: parameters[elem], + }); + } + } + } + + if (filters.hasOwnProperty("between")) { + for (const elem of filters.between) { + if (!parameters[elem]) { + continue; + } + + if (!parameters[elem].hasOwnProperty("from") && !parameters[elem].hasOwnProperty("to")) { + where.push({ + column: elem, + method: "equals", + value: parameters[elem], + }); + } + + if (parameters[elem].hasOwnProperty("from") || parameters[elem].hasOwnProperty("to")) { + where[elem] = {}; + + if (parameters[elem].hasOwnProperty("from")) { + where.push({ + column: elem, + method: "gte", + value: parameters[elem].from, + }); + } + + if (parameters[elem].hasOwnProperty("to")) { + where.push({ + column: elem, + method: "lte", + value: parameters[elem].to, + }); + } + } + } + } + + if (filters.hasOwnProperty("in")) { + for (const elem of filters.in) { + if (parameters[elem]) { + where.push({ + column: elem, + method: "in", + value: parameters[elem], + }); + } + } + } + + if (filters.hasOwnProperty("wildcard")) { + for (const elem of filters.wildcard) { + if (parameters[elem]) { + where.push({ + column: elem, + method: "like", + value: `%${parameters[elem]}%`, + }); + } + } + } + + return where; +} diff --git a/packages/core-api/src/server.ts b/packages/core-api/src/server.ts new file mode 100644 index 0000000000..a329e40e62 --- /dev/null +++ b/packages/core-api/src/server.ts @@ -0,0 +1,147 @@ +import { app } from "@arkecosystem/core-container"; +import { createSecureServer, createServer, mountServer, plugins } from "@arkecosystem/core-http-utils"; +import { Logger } from "@arkecosystem/core-interfaces"; +import Hapi from "hapi"; + +export class Server { + private logger = app.resolvePlugin("logger"); + + private http: any; + private https: any; + + public constructor(private config: any) {} + + public async start(): Promise { + const options = { + host: this.config.host, + port: this.config.port, + routes: { + cors: { + additionalHeaders: ["api-version"], + }, + validate: { + async failAction(request, h, err) { + throw err; + }, + }, + }, + }; + + if (this.config.enabled) { + this.http = await createServer(options); + this.http.app.config = this.config; + + this.registerPlugins("HTTP", this.http); + } + + if (this.config.ssl.enabled) { + this.https = await createSecureServer(options, null, this.config.ssl); + this.https.app.config = this.config; + + this.registerPlugins("HTTPS", this.https); + } + } + + public async stop(): Promise { + if (this.http) { + this.logger.info(`Stopping Public HTTP API`); + await this.http.stop(); + } + + if (this.https) { + this.logger.info(`Stopping Public HTTPS API`); + await this.https.stop(); + } + } + + public async restart(): Promise { + if (this.http) { + await this.http.stop(); + await this.http.start(); + } + + if (this.https) { + await this.https.stop(); + await this.https.start(); + } + } + + public instance(type: string): Hapi.Server { + return this[type]; + } + + private async registerPlugins(name: string, server: Hapi.Server): Promise { + // TODO: enable after mainnet migration + // await server.register({ plugin: plugins.contentType }) + + await server.register({ + plugin: plugins.corsHeaders, + }); + + await server.register({ + plugin: plugins.whitelist, + options: { + whitelist: this.config.whitelist, + name: "Public API", + }, + }); + + await server.register({ + plugin: require("./plugins/set-headers"), + }); + + await server.register({ + plugin: require("hapi-api-version"), + options: this.config.versions, + }); + + await server.register({ + plugin: require("./plugins/endpoint-version"), + options: { validVersions: this.config.versions.validVersions }, + }); + + await server.register({ + plugin: require("./plugins/caster"), + }); + + await server.register({ + plugin: require("./plugins/validation"), + }); + + await server.register({ + plugin: require("hapi-rate-limit"), + options: this.config.rateLimit, + }); + + await server.register({ + plugin: require("hapi-pagination"), + options: { + meta: { + baseUri: "", + }, + query: { + limit: { + default: this.config.pagination.limit, + }, + }, + results: { + name: "data", + }, + routes: { + include: this.config.pagination.include, + exclude: ["*"], + }, + }, + }); + + for (const plugin of this.config.plugins) { + if (typeof plugin.plugin === "string") { + plugin.plugin = require(plugin.plugin); + } + + await server.register(plugin); + } + + await mountServer(`Public ${name.toUpperCase()} API`, server); + } +} diff --git a/packages/core-api/src/services/cache.ts b/packages/core-api/src/services/cache.ts new file mode 100644 index 0000000000..f0f354bee2 --- /dev/null +++ b/packages/core-api/src/services/cache.ts @@ -0,0 +1,43 @@ +import { app } from "@arkecosystem/core-container"; +import { createHash } from "crypto"; +import Hapi from "hapi"; + +export class ServerCache { + public static make(server: Hapi.Server): ServerCache { + return new ServerCache(server); + } + + private constructor(readonly server: Hapi.Server) {} + + public method(name: string, method: any, expiresIn: number, argsCallback?: any): this { + let options = {}; + + // @ts-ignore + if (this.server.app.config.cache.enabled) { + options = { + cache: { + expiresIn: expiresIn * 1000, + generateTimeout: this.getCacheTimeout(), + getDecoratedValue: true, + }, + generateKey: request => this.generateCacheKey(argsCallback(request)), + }; + } + + this.server.method(name, method, options); + + return this; + } + + private generateCacheKey(value: object): string { + return createHash("sha256") + .update(JSON.stringify(value)) + .digest("hex"); + } + + private getCacheTimeout(): number | boolean { + const { generateTimeout } = app.resolveOptions("api").cache; + + return JSON.parse(generateTimeout); + } +} diff --git a/packages/core-api/src/services/index.ts b/packages/core-api/src/services/index.ts new file mode 100644 index 0000000000..e60aa06216 --- /dev/null +++ b/packages/core-api/src/services/index.ts @@ -0,0 +1,4 @@ +import { ServerCache } from "./cache"; +import { transformerService } from "./transformer"; + +export { ServerCache, transformerService }; diff --git a/packages/core-api/src/services/transformer.ts b/packages/core-api/src/services/transformer.ts new file mode 100644 index 0000000000..832778a47f --- /dev/null +++ b/packages/core-api/src/services/transformer.ts @@ -0,0 +1,53 @@ +import { transformAccountLegacy } from "../versions/1/accounts/transformer"; +import { transformBlockLegacy } from "../versions/1/blocks/transformer"; +import { transformDelegateLegacy } from "../versions/1/delegates/transformer"; +import { transformPeerLegacy } from "../versions/1/peers/transformer"; +import { transformFeeStatisticsLegacy } from "../versions/1/shared/transformers/fee-statistics"; +import { transformPortsLegacy } from "../versions/1/shared/transformers/ports"; +import { transformVoterLegacy } from "../versions/1/shared/transformers/voter"; +import { transformTransactionLegacy } from "../versions/1/transactions/transformer"; + +import { transformBlock } from "../versions/2/blocks/transformer"; +import { transformDelegate } from "../versions/2/delegates/transformer"; +import { transformPeer } from "../versions/2/peers/transformer"; +import { transformFeeStatistics } from "../versions/2/shared/transformers/fee-statistics"; +import { transformPorts } from "../versions/2/shared/transformers/ports"; +import { transformTransaction } from "../versions/2/transactions/transformer"; +import { transformWallet } from "../versions/2/wallets/transformer"; + +class Transformer { + private transformers: Map = new Map(); + + public constructor() { + this.transformers.set(1, { + account: transformAccountLegacy, + block: transformBlockLegacy, + delegate: transformDelegateLegacy, + "fee-statistics": transformFeeStatisticsLegacy, + peer: transformPeerLegacy, + ports: transformPortsLegacy, + transaction: transformTransactionLegacy, + voter: transformVoterLegacy, + }); + + this.transformers.set(2, { + block: transformBlock, + delegate: transformDelegate, + "fee-statistics": transformFeeStatistics, + peer: transformPeer, + ports: transformPorts, + transaction: transformTransaction, + wallet: transformWallet, + }); + } + + public toResource(request, data, transformer): object { + return this.transformers.get(request.pre.apiVersion)[transformer](data); + } + + public toCollection(request, data, transformer): object[] { + return data.map(d => this.toResource(request, d, transformer)); + } +} + +export const transformerService = new Transformer(); diff --git a/packages/core-api/src/versions/1/accounts/controller.ts b/packages/core-api/src/versions/1/accounts/controller.ts new file mode 100644 index 0000000000..5c8a6410cc --- /dev/null +++ b/packages/core-api/src/versions/1/accounts/controller.ts @@ -0,0 +1,112 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class AccountsController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.accounts.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.accounts.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async balance(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.accounts.balance(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async publicKey(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.accounts.publicKey(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async fee(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + fee: this.config.getMilestone(this.blockchain.getLastHeight()).fees.staticFees.delegateRegistration, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async delegates(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const account = await this.databaseService.wallets.findById(request.query.address); + + if (!account) { + return super.respondWith("Address not found.", true); + } + + if (!account.vote) { + return super.respondWith( + // @ts-ignore + `Address ${request.query.address} hasn't voted yet.`, + true, + ); + } + + const delegate = await this.databaseService.delegates.findById(account.vote); + + return super.respondWith({ + delegates: [super.toResource(request, delegate, "delegate")], + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async top(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const wallets = this.databaseService.wallets.top(super.paginate(request)); + + const accounts = wallets.rows.map(account => ({ + address: account.address, + balance: `${account.balance}`, + publicKey: account.publicKey, + })); + + return super.respondWith({ accounts }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async count(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const { count } = await this.databaseService.wallets.findAll(); + + return super.respondWith({ count }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/accounts/index.ts b/packages/core-api/src/versions/1/accounts/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/1/accounts/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/accounts/methods.ts b/packages/core-api/src/versions/1/accounts/methods.ts new file mode 100644 index 0000000000..ce6e32fa63 --- /dev/null +++ b/packages/core-api/src/versions/1/accounts/methods.ts @@ -0,0 +1,63 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { ServerCache } from "../../../services"; +import { paginate, respondWith, toCollection, toResource } from "../utils"; + +const databaseService = app.resolvePlugin("database"); + +const index = async request => { + const { rows } = await databaseService.wallets.findAll({ + ...request.query, + ...paginate(request), + }); + + return respondWith({ + accounts: toCollection(request, rows, "account"), + }); +}; + +const show = async request => { + const account = await databaseService.wallets.findById(request.query.address); + + if (!account) { + return respondWith("Account not found", true); + } + + return respondWith({ + account: toResource(request, account, "account"), + }); +}; + +const balance = async request => { + const account = await databaseService.wallets.findById(request.query.address); + + if (!account) { + return respondWith({ balance: "0", unconfirmedBalance: "0" }); + } + + return respondWith({ + balance: account ? `${account.balance}` : "0", + unconfirmedBalance: account ? `${account.balance}` : "0", + }); +}; + +const publicKey = async request => { + const account = await databaseService.wallets.findById(request.query.address); + + if (!account) { + return respondWith("Account not found", true); + } + + return respondWith({ publicKey: account.publicKey }); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v1.accounts.index", index, 8, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v1.accounts.show", show, 8, request => ({ address: request.query.address })) + .method("v1.accounts.balance", balance, 8, request => ({ address: request.query.address })) + .method("v1.accounts.publicKey", publicKey, 600, request => ({ address: request.query.address })); +} diff --git a/packages/core-api/src/versions/1/accounts/routes.ts b/packages/core-api/src/versions/1/accounts/routes.ts new file mode 100644 index 0000000000..b28f5133bf --- /dev/null +++ b/packages/core-api/src/versions/1/accounts/routes.ts @@ -0,0 +1,91 @@ +import Hapi from "hapi"; +import { AccountsController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new AccountsController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/accounts/getAllAccounts", + handler: controller.index, + }); + + server.route({ + method: "GET", + path: "/accounts", + handler: controller.show, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getAccount, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/accounts/getBalance", + handler: controller.balance, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getBalance, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/accounts/getPublicKey", + handler: controller.publicKey, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getPublicKey, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/accounts/delegates/fee", + handler: controller.fee, + }); + + server.route({ + method: "GET", + path: "/accounts/delegates", + handler: controller.delegates, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getDelegates, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/accounts/top", + handler: controller.top, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.top, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/accounts/count", + handler: controller.count, + }); +} diff --git a/packages/core-api/src/versions/1/accounts/schema.ts b/packages/core-api/src/versions/1/accounts/schema.ts new file mode 100644 index 0000000000..f157165932 --- /dev/null +++ b/packages/core-api/src/versions/1/accounts/schema.ts @@ -0,0 +1,73 @@ +export const getBalance: object = { + type: "object", + properties: { + address: { + type: "string", + minLength: 1, + format: "address", + }, + }, + required: ["address"], +}; + +export const getPublicKey: object = { + type: "object", + properties: { + address: { + type: "string", + minLength: 1, + format: "address", + }, + }, + required: ["address"], +}; + +export const generatePublicKey: object = { + type: "object", + properties: { + secret: { + type: "string", + minLength: 1, + }, + }, + required: ["secret"], +}; + +export const getDelegates: object = { + type: "object", + properties: { + address: { + type: "string", + minLength: 1, + format: "address", + }, + }, + required: ["address"], +}; + +export const getAccount: object = { + type: "object", + properties: { + address: { + type: "string", + minLength: 1, + format: "address", + }, + }, + required: ["address"], +}; + +export const top: object = { + type: "object", + properties: { + limit: { + type: "integer", + minimum: 0, + maximum: 100, + }, + offset: { + type: "integer", + minimum: 0, + }, + }, +}; diff --git a/packages/core-api/src/versions/1/accounts/transformer.ts b/packages/core-api/src/versions/1/accounts/transformer.ts new file mode 100644 index 0000000000..efe62ec6b2 --- /dev/null +++ b/packages/core-api/src/versions/1/accounts/transformer.ts @@ -0,0 +1,17 @@ +export function transformAccountLegacy(model) { + const hasSecondSignature = !!model.secondPublicKey; + + return { + address: model.address, + publicKey: model.publicKey, + secondPublicKey: model.secondPublicKey, + votes: model.votes, + username: model.username, + balance: `${model.balance}`, + unconfirmedBalance: `${model.balance}`, + multisignatures: [], + u_multisignatures: [], + unconfirmedSignature: hasSecondSignature ? 1 : 0, + secondSignature: hasSecondSignature ? 1 : 0, + }; +} diff --git a/packages/core-api/src/versions/1/blocks/controller.ts b/packages/core-api/src/versions/1/blocks/controller.ts new file mode 100644 index 0000000000..ef791fe30b --- /dev/null +++ b/packages/core-api/src/versions/1/blocks/controller.ts @@ -0,0 +1,143 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class BlocksController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.blocks.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.blocks.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async epoch(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + epoch: this.config.getMilestone(this.blockchain.getLastHeight()).epoch, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async height(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const block = this.blockchain.getLastBlock(); + + return super.respondWith({ height: block.data.height, id: block.data.id }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async nethash(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ nethash: this.config.get("network.nethash") }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async fee(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + fee: this.config.getMilestone(this.blockchain.getLastHeight()).fees.staticFees.transfer, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async fees(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastHeight = this.blockchain.getLastHeight(); + const fees = this.config.getMilestone(lastHeight).fees.staticFees; + + return super.respondWith({ + fees: { + send: fees.transfer, + vote: fees.vote, + secondsignature: fees.secondSignature, + delegate: fees.delegateRegistration, + multisignature: fees.multiSignature, + }, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async milestone(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + milestone: Math.floor(this.blockchain.getLastHeight() / 3000000), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async reward(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + reward: this.config.getMilestone(this.blockchain.getLastHeight()).reward, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async supply(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + const constants = this.config.getMilestone(lastBlock.data.height); + const rewards = bignumify(constants.reward).times(lastBlock.data.height - constants.height); + + return super.respondWith({ + supply: +bignumify(this.config.get("genesisBlock.totalAmount")) + .plus(rewards) + .toFixed(), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async status(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + const constants = this.config.getMilestone(lastBlock.data.height); + const rewards = bignumify(constants.reward).times(lastBlock.data.height - constants.height); + + return super.respondWith({ + epoch: constants.epoch, + height: lastBlock.data.height, + fee: constants.fees.staticFees.transfer, + milestone: Math.floor(lastBlock.data.height / 3000000), + nethash: this.config.get("network.nethash"), + reward: constants.reward, + supply: +bignumify(this.config.get("genesisBlock.totalAmount")) + .plus(rewards) + .toFixed(), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/blocks/index.ts b/packages/core-api/src/versions/1/blocks/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/1/blocks/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/blocks/methods.ts b/packages/core-api/src/versions/1/blocks/methods.ts new file mode 100644 index 0000000000..3a61ae2fe3 --- /dev/null +++ b/packages/core-api/src/versions/1/blocks/methods.ts @@ -0,0 +1,40 @@ +import { blocksRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWith, toCollection, toResource } from "../utils"; + +const index = async request => { + const { count, rows } = await blocksRepository.findAll({ + ...request.query, + ...paginate(request), + }); + + if (!rows) { + return respondWith("No blocks found", true); + } + + return respondWith({ + blocks: toCollection(request, rows, "block"), + count, + }); +}; + +const show = async request => { + const block = await blocksRepository.findById(request.query.id); + + if (!block) { + return respondWith(`Block with id ${request.query.id} not found`, true); + } + + return respondWith({ + block: toResource(request, block, "block"), + }); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v1.blocks.index", index, 8, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v1.blocks.show", show, 600, request => ({ id: request.query.id })); +} diff --git a/packages/core-api/src/versions/1/blocks/routes.ts b/packages/core-api/src/versions/1/blocks/routes.ts new file mode 100644 index 0000000000..40cf97c72e --- /dev/null +++ b/packages/core-api/src/versions/1/blocks/routes.ts @@ -0,0 +1,100 @@ +import Hapi from "hapi"; +import { BlocksController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new BlocksController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/blocks", + handler: controller.index, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getBlocks, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/blocks/get", + handler: controller.show, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getBlock, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/blocks/getEpoch", + handler: controller.epoch, + }); + + server.route({ + method: "GET", + path: "/blocks/getHeight", + handler: controller.height, + }); + + server.route({ + method: "GET", + path: "/blocks/getheight", + handler: controller.height, + }); + + server.route({ + method: "GET", + path: "/blocks/getNethash", + handler: controller.nethash, + }); + + server.route({ + method: "GET", + path: "/blocks/getFee", + handler: controller.fee, + }); + + server.route({ + method: "GET", + path: "/blocks/getFees", + handler: controller.fees, + }); + + server.route({ + method: "GET", + path: "/blocks/getfees", + handler: controller.fees, + }); + + server.route({ + method: "GET", + path: "/blocks/getMilestone", + handler: controller.milestone, + }); + + server.route({ + method: "GET", + path: "/blocks/getReward", + handler: controller.reward, + }); + + server.route({ + method: "GET", + path: "/blocks/getSupply", + handler: controller.supply, + }); + + server.route({ + method: "GET", + path: "/blocks/getStatus", + handler: controller.status, + }); +} diff --git a/packages/core-api/src/versions/1/blocks/schema.ts b/packages/core-api/src/versions/1/blocks/schema.ts new file mode 100644 index 0000000000..552e3048bc --- /dev/null +++ b/packages/core-api/src/versions/1/blocks/schema.ts @@ -0,0 +1,50 @@ +export const getBlock: object = { + type: "object", + properties: { + id: { + type: "string", + minLength: 1, + }, + }, + required: ["id"], +}; + +export const getBlocks: object = { + type: "object", + properties: { + limit: { + type: "integer", + minimum: 0, + maximum: 100, + }, + orderBy: { + type: "string", + }, + offset: { + type: "integer", + minimum: 0, + }, + generatorPublicKey: { + type: "string", + format: "publicKey", + }, + totalAmount: { + type: "integer", + minimum: 0, + }, + totalFee: { + type: "integer", + minimum: 0, + }, + reward: { + type: "integer", + minimum: 0, + }, + previousBlock: { + type: "string", + }, + height: { + type: "integer", + }, + }, +}; diff --git a/packages/core-api/src/versions/1/blocks/transformer.ts b/packages/core-api/src/versions/1/blocks/transformer.ts new file mode 100644 index 0000000000..6facbc8ff7 --- /dev/null +++ b/packages/core-api/src/versions/1/blocks/transformer.ts @@ -0,0 +1,27 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { bignumify } from "@arkecosystem/core-utils"; + +export function transformBlockLegacy(model) { + const lastBlock = app.resolvePlugin("blockchain").getLastBlock(); + + return { + id: model.id, + version: model.version, + timestamp: model.timestamp, + previousBlock: model.previousBlock, + height: model.height, + numberOfTransactions: model.numberOfTransactions, + totalAmount: +bignumify(model.totalAmount).toFixed(), + totalForged: +bignumify(model.reward) + .plus(model.totalFee) + .toString(), + totalFee: +bignumify(model.totalFee).toFixed(), + reward: +bignumify(model.reward).toFixed(), + payloadLength: model.payloadLength, + payloadHash: model.payloadHash, + generatorPublicKey: model.generatorPublicKey, + blockSignature: model.blockSignature, + confirmations: lastBlock ? lastBlock.data.height - model.height : 0, + }; +} diff --git a/packages/core-api/src/versions/1/delegates/controller.ts b/packages/core-api/src/versions/1/delegates/controller.ts new file mode 100644 index 0000000000..4ee2ce26f1 --- /dev/null +++ b/packages/core-api/src/versions/1/delegates/controller.ts @@ -0,0 +1,119 @@ +import { slots } from "@arkecosystem/crypto"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class DelegatesController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.delegates.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.delegates.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async count(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.delegates.count(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async search(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.delegates.search(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async voters(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.delegates.voters(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async fee(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + fee: this.config.getMilestone(this.blockchain.getLastHeight()).fees.staticFees.delegateRegistration, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async forged(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const wallet = this.databaseService.walletManager.findByPublicKey( + // @ts-ignore + request.query.generatorPublicKey, + ); + + return super.respondWith({ + fees: Number(wallet.forgedFees), + rewards: Number(wallet.forgedRewards), + forged: Number(wallet.forgedFees) + Number(wallet.forgedRewards), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async nextForgers(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + // @ts-ignore + const limit = request.query.limit || 10; + + const delegatesCount = this.config.getMilestone(lastBlock).activeDelegates; + const currentSlot = slots.getSlotNumber(lastBlock.data.timestamp); + + let activeDelegates = await this.databaseService.getActiveDelegates(lastBlock.data.height); + activeDelegates = activeDelegates.map(delegate => delegate.publicKey); + + const nextForgers = []; + for (let i = 1; i <= delegatesCount && i <= limit; i++) { + const delegate = activeDelegates[(currentSlot + i) % delegatesCount]; + + if (delegate) { + nextForgers.push(delegate); + } + } + + return super.respondWith({ + currentBlock: lastBlock.data.height, + currentSlot, + delegates: nextForgers, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/delegates/index.ts b/packages/core-api/src/versions/1/delegates/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/1/delegates/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/delegates/methods.ts b/packages/core-api/src/versions/1/delegates/methods.ts new file mode 100644 index 0000000000..a0024fd189 --- /dev/null +++ b/packages/core-api/src/versions/1/delegates/methods.ts @@ -0,0 +1,90 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { ServerCache } from "../../../services"; +import { paginate, respondWith, toCollection, toResource } from "../utils"; + +const databaseService = app.resolvePlugin("database"); + +const index = async request => { + const { count, rows } = await databaseService.delegates.findAll({ + ...request.query, + ...{ + offset: request.query.offset || 0, + limit: request.query.limit || 51, + }, + }); + + return respondWith({ + delegates: toCollection(request, rows, "delegate"), + totalCount: count, + }); +}; + +const show = async request => { + if (!request.query.publicKey && !request.query.username) { + return respondWith("Delegate not found", true); + } + + const delegate = await databaseService.delegates.findById(request.query.publicKey || request.query.username); + + if (!delegate) { + return respondWith("Delegate not found", true); + } + + return respondWith({ + delegate: toResource(request, delegate, "delegate"), + }); +}; + +const countDelegates = async request => { + const delegate = await databaseService.delegates.findAll(); + + return respondWith({ count: delegate.count }); +}; + +const search = async request => { + const { rows } = await databaseService.delegates.search({ + ...{ username: request.query.q }, + ...paginate(request), + }); + + return respondWith({ + delegates: toCollection(request, rows, "delegate"), + }); +}; + +const voters = async request => { + const delegate = await databaseService.delegates.findById(request.query.publicKey); + + if (!delegate) { + return respondWith({ + accounts: [], + }); + } + + const accounts = await databaseService.wallets.findAllByVote(delegate.publicKey); + + return respondWith({ + accounts: toCollection(request, accounts.rows, "voter"), + }); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v1.delegates.index", index, 8, request => ({ + ...request.query, + ...{ + offset: request.query.offset || 0, + limit: request.query.limit || 51, + }, + })) + .method("v1.delegates.show", show, 8, request => ({ + id: request.query.publicKey || request.query.username, + })) + .method("v1.delegates.count", countDelegates, 8, request => ({ time: +new Date() })) + .method("v1.delegates.search", search, 8, request => ({ + ...{ username: request.query.q }, + ...paginate(request), + })) + .method("v1.delegates.voters", voters, 8, request => ({ id: request.query.publicKey })); +} diff --git a/packages/core-api/src/versions/1/delegates/routes.ts b/packages/core-api/src/versions/1/delegates/routes.ts new file mode 100644 index 0000000000..a1f1f4f572 --- /dev/null +++ b/packages/core-api/src/versions/1/delegates/routes.ts @@ -0,0 +1,91 @@ +import Hapi from "hapi"; +import { DelegatesController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new DelegatesController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/delegates", + handler: controller.index, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getDelegates, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/get", + handler: controller.show, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getDelegate, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/count", + handler: controller.count, + }); + + server.route({ + method: "GET", + path: "/delegates/search", + handler: controller.search, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.search, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/voters", + handler: controller.voters, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getVoters, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/fee", + handler: controller.fee, + }); + + server.route({ + method: "GET", + path: "/delegates/forging/getForgedByAccount", + handler: controller.forged, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getForgedByAccount, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/getNextForgers", + handler: controller.nextForgers, + }); +} diff --git a/packages/core-api/src/versions/1/delegates/schema.ts b/packages/core-api/src/versions/1/delegates/schema.ts new file mode 100644 index 0000000000..2cdab0866d --- /dev/null +++ b/packages/core-api/src/versions/1/delegates/schema.ts @@ -0,0 +1,84 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; + +const lastBlock = app.resolvePlugin("blockchain").getLastBlock(); + +export const forgingStatus: object = { + type: "object", + properties: { + publicKey: { + type: "string", + format: "publicKey", + }, + }, + required: ["publicKey"], +}; + +export const getDelegate: object = { + type: "object", + properties: { + publicKey: { + type: "string", + }, + username: { + type: "string", + }, + }, +}; + +export const search: object = { + type: "object", + properties: { + q: { + type: "string", + minLength: 1, + maxLength: 20, + }, + limit: { + type: "integer", + minimum: 1, + maximum: 100, + }, + }, + required: ["q"], +}; + +export const getVoters: object = { + type: "object", + properties: { + publicKey: { + type: "string", + format: "publicKey", + }, + }, + required: ["publicKey"], +}; + +export const getDelegates: object = { + type: "object", + properties: { + orderBy: { + type: "string", + }, + limit: { + type: "integer", + minimum: 1, + maximum: lastBlock ? app.getConfig().getMilestone(lastBlock.data.height).activeDelegates : 51, + }, + offset: { + type: "integer", + minimum: 0, + }, + }, +}; + +export const getForgedByAccount: object = { + type: "object", + properties: { + generatorPublicKey: { + type: "string", + format: "publicKey", + }, + }, + required: ["generatorPublicKey"], +}; diff --git a/packages/core-api/src/versions/1/delegates/transformer.ts b/packages/core-api/src/versions/1/delegates/transformer.ts new file mode 100644 index 0000000000..84cff93572 --- /dev/null +++ b/packages/core-api/src/versions/1/delegates/transformer.ts @@ -0,0 +1,16 @@ +import { delegateCalculator } from "@arkecosystem/core-utils"; + +export function transformDelegateLegacy(model) { + return { + username: model.username, + address: model.address, + publicKey: model.publicKey, + vote: `${model.voteBalance}`, + producedblocks: model.producedBlocks, + missedblocks: model.missedBlocks, + forged: model.forged, + rate: model.rate, + approval: delegateCalculator.calculateApproval(model), + productivity: delegateCalculator.calculateProductivity(model), + }; +} diff --git a/packages/core-api/src/versions/1/index.ts b/packages/core-api/src/versions/1/index.ts new file mode 100644 index 0000000000..5218afe26f --- /dev/null +++ b/packages/core-api/src/versions/1/index.ts @@ -0,0 +1,22 @@ +import Hapi from "hapi"; +import * as Accounts from "./accounts"; +import * as Blocks from "./blocks"; +import * as Delegates from "./delegates"; +import * as Loader from "./loader"; +import * as Peers from "./peers"; +import * as Signatures from "./signatures"; +import * as Transactions from "./transactions"; + +const register = async (server: Hapi.Server): Promise => { + const modules = [Accounts, Blocks, Delegates, Loader, Peers, Signatures, Transactions]; + + for (const module of modules) { + module.register(server); + } +}; + +export = { + register, + name: "Public API - Legacy", + version: "1.0.0", +}; diff --git a/packages/core-api/src/versions/1/loader/controller.ts b/packages/core-api/src/versions/1/loader/controller.ts new file mode 100644 index 0000000000..3f159f532f --- /dev/null +++ b/packages/core-api/src/versions/1/loader/controller.ts @@ -0,0 +1,66 @@ +import { app } from "@arkecosystem/core-container"; +import Boom from "boom"; +import Hapi from "hapi"; +import { transactionsRepository } from "../../../repositories"; +import { Controller } from "../shared/controller"; + +export class LoaderController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return { data: true }; + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async status(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + + return super.respondWith({ + loaded: this.blockchain.isSynced(), + now: lastBlock ? lastBlock.data.height : 0, + blocksCount: this.blockchain.p2p.getNetworkHeight() - (lastBlock ? lastBlock.data.height : 0), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async syncing(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + + return super.respondWith({ + syncing: !this.blockchain.isSynced(), + blocks: this.blockchain.p2p.getNetworkHeight() - lastBlock.data.height, + height: lastBlock.data.height, + id: lastBlock.data.id, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async autoconfigure(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const feeStatisticsData = await transactionsRepository.getFeeStatistics(); + + const network = this.config.get("network"); + + return super.respondWith({ + network: { + nethash: network.nethash, + token: network.client.token, + symbol: network.client.symbol, + explorer: network.client.explorer, + version: network.pubKeyHash, + ports: super.toResource(request, this.config, "ports"), + feeStatistics: super.toCollection(request, feeStatisticsData, "fee-statistics"), + }, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/loader/index.ts b/packages/core-api/src/versions/1/loader/index.ts new file mode 100644 index 0000000000..f13324009a --- /dev/null +++ b/packages/core-api/src/versions/1/loader/index.ts @@ -0,0 +1,6 @@ +import Hapi from "hapi"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/loader/routes.ts b/packages/core-api/src/versions/1/loader/routes.ts new file mode 100644 index 0000000000..cc14deef54 --- /dev/null +++ b/packages/core-api/src/versions/1/loader/routes.ts @@ -0,0 +1,25 @@ +import Hapi from "hapi"; +import { LoaderController } from "./controller"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new LoaderController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/loader/status", + handler: controller.status, + }); + + server.route({ + method: "GET", + path: "/loader/status/sync", + handler: controller.syncing, + }); + + server.route({ + method: "GET", + path: "/loader/autoconfigure", + handler: controller.autoconfigure, + }); +} diff --git a/packages/core-api/src/versions/1/peers/controller.ts b/packages/core-api/src/versions/1/peers/controller.ts new file mode 100644 index 0000000000..ac40e3fc0d --- /dev/null +++ b/packages/core-api/src/versions/1/peers/controller.ts @@ -0,0 +1,111 @@ +import { app } from "@arkecosystem/core-container"; +import { P2P } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class PeersController extends Controller { + protected p2p: P2P.IMonitor; + + public constructor() { + super(); + + this.p2p = app.resolvePlugin("p2p"); + } + + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const allPeers: any[] = await this.p2p.getPeers(); + + if (!allPeers) { + return super.respondWith("No peers found", true); + } + + let peers = allPeers + .map(peer => { + // just use 'OK' status for API instead of p2p http status codes + peer.status = peer.status === 200 ? "OK" : peer.status; + return peer; + }) + .sort((a, b) => a.delay - b.delay); + // @ts-ignore + peers = request.query.os + ? // @ts-ignore + allPeers.filter(peer => peer.os === (request.query as any).os) + : peers; + // @ts-ignore + peers = request.query.status + ? // @ts-ignore + allPeers.filter(peer => peer.status === (request.query as any).status) + : peers; + // @ts-ignore + peers = request.query.port + ? // @ts-ignore + allPeers.filter(peer => peer.port === (request.query as any).port) + : peers; + // @ts-ignore + peers = request.query.version + ? // @ts-ignore + allPeers.filter(peer => peer.version === (request.query as any).version) + : peers; + // @ts-ignore + peers = peers.slice(0, request.query.limit || 100); + + // @ts-ignore + if (request.query.orderBy) { + // @ts-ignore + const order = request.query.orderBy.split(":"); + if (["port", "status", "os", "version"].includes(order[0])) { + peers = + order[1].toUpperCase() === "ASC" + ? peers.sort((a, b) => a[order[0]] - b[order[0]]) + : peers.sort((a, b) => a[order[0]] + b[order[0]]); + } + } + + return super.respondWith({ + peers: super.toCollection(request, peers.map(peer => peer.toBroadcastInfo()), "peer"), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const peers = await this.p2p.getPeers(); + if (!peers) { + return super.respondWith("No peers found", true); + } + + const peer = peers.find( + // @ts-ignore + elem => elem.ip === (request.query as any).ip && +elem.port === +request.query.port, + ); + + if (!peer) { + return super.respondWith( + // @ts-ignore + `Peer ${request.query.ip}:${request.query.port} not found`, + true, + ); + } + + return super.respondWith({ + peer: super.toResource(request, peer.toBroadcastInfo(), "peer"), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async version(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return super.respondWith({ + version: app.getVersion(), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/peers/index.ts b/packages/core-api/src/versions/1/peers/index.ts new file mode 100644 index 0000000000..f13324009a --- /dev/null +++ b/packages/core-api/src/versions/1/peers/index.ts @@ -0,0 +1,6 @@ +import Hapi from "hapi"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/peers/routes.ts b/packages/core-api/src/versions/1/peers/routes.ts new file mode 100644 index 0000000000..7291d152ad --- /dev/null +++ b/packages/core-api/src/versions/1/peers/routes.ts @@ -0,0 +1,40 @@ +import Hapi from "hapi"; +import { PeersController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new PeersController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/peers", + handler: controller.index, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getPeers, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/peers/get", + handler: controller.show, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getPeer, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/peers/version", + handler: controller.version, + }); +} diff --git a/packages/core-api/src/versions/1/peers/schema.ts b/packages/core-api/src/versions/1/peers/schema.ts new file mode 100644 index 0000000000..087a724c01 --- /dev/null +++ b/packages/core-api/src/versions/1/peers/schema.ts @@ -0,0 +1,50 @@ +export const getPeers: object = { + type: "object", + properties: { + port: { + type: "integer", + minimum: 1, + maximum: 65535, + }, + status: { + type: "string", + maxLength: 20, + }, + os: { + type: "string", + maxLength: 64, + }, + version: { + type: "string", + maxLength: 11, + }, + orderBy: { + type: "string", + }, + limit: { + type: "integer", + minimum: 0, + maximum: 100, + }, + offset: { + type: "integer", + minimum: 0, + }, + }, +}; + +export const getPeer: object = { + type: "object", + properties: { + ip: { + type: "string", + format: "ip", + }, + port: { + type: "integer", + minimum: 0, + maximum: 65535, + }, + }, + required: ["ip", "port"], +}; diff --git a/packages/core-api/src/versions/1/peers/transformer.ts b/packages/core-api/src/versions/1/peers/transformer.ts new file mode 100644 index 0000000000..3571e2f861 --- /dev/null +++ b/packages/core-api/src/versions/1/peers/transformer.ts @@ -0,0 +1,21 @@ +import { app } from "@arkecosystem/core-container"; + +export function transformPeerLegacy(model) { + const config = app.getConfig(); + + const peer: any = { + ip: model.ip, + port: model.port, + version: model.version, + height: model.height, + status: [200, "OK"].includes(model.status) ? "OK" : "ERROR", + os: model.os, + delay: model.delay, + }; + + if (config.get("network.name") !== "mainnet") { + peer.hashid = model.hashid; + } + + return peer; +} diff --git a/packages/core-api/src/versions/1/shared/controller.ts b/packages/core-api/src/versions/1/shared/controller.ts new file mode 100644 index 0000000000..7e43933440 --- /dev/null +++ b/packages/core-api/src/versions/1/shared/controller.ts @@ -0,0 +1,31 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database, Logger } from "@arkecosystem/core-interfaces"; +import Hapi from "hapi"; +import { paginate, respondWith, respondWithCache, toCollection, toResource } from "../utils"; + +export class Controller { + protected config = app.getConfig(); + protected blockchain = app.resolvePlugin("blockchain"); + protected databaseService = app.resolvePlugin("database"); + protected logger = app.resolvePlugin("logger"); + + protected paginate(request: Hapi.Request): any { + return paginate(request); + } + + protected respondWith(data, error = false): object { + return respondWith(data, error); + } + + protected respondWithCache(data, h): any { + return respondWithCache(data, h); + } + + protected toResource(request, data, transformer): object { + return toResource(request, data, transformer); + } + + protected toCollection(request, data, transformer): object { + return toCollection(request, data, transformer); + } +} diff --git a/packages/core-api/src/versions/1/shared/transformers/fee-statistics.ts b/packages/core-api/src/versions/1/shared/transformers/fee-statistics.ts new file mode 100644 index 0000000000..ba64483456 --- /dev/null +++ b/packages/core-api/src/versions/1/shared/transformers/fee-statistics.ts @@ -0,0 +1,10 @@ +export function transformFeeStatisticsLegacy(model: any) { + return { + type: model.type, + fees: { + minFee: parseInt(model.minFee, 10), + maxFee: parseInt(model.maxFee, 10), + avgFee: parseInt(model.avgFee, 10), + }, + }; +} diff --git a/packages/core-api/src/versions/1/shared/transformers/ports.ts b/packages/core-api/src/versions/1/shared/transformers/ports.ts new file mode 100644 index 0000000000..579a94b156 --- /dev/null +++ b/packages/core-api/src/versions/1/shared/transformers/ports.ts @@ -0,0 +1,32 @@ +export function transformPortsLegacy(config: any) { + const result = {}; + const keys = [ + "@arkecosystem/core-p2p", + "@arkecosystem/core-api", + "@arkecosystem/core-graphql", + "@arkecosystem/core-json-rpc", + "@arkecosystem/core-webhooks", + ]; + + const plugins = config.get("plugins"); + + result[keys[0]] = +plugins[keys[0]].port; + + for (const [name, options] of Object.entries(plugins)) { + // @ts-ignore + if (keys.includes(name) && options.enabled) { + // @ts-ignore + if (options.server && options.server.enabled) { + // @ts-ignore + result[name] = +options.server.port; + + continue; + } + + // @ts-ignore + result[name] = +options.port; + } + } + + return result; +} diff --git a/packages/core-api/src/versions/1/shared/transformers/voter.ts b/packages/core-api/src/versions/1/shared/transformers/voter.ts new file mode 100644 index 0000000000..c595446bae --- /dev/null +++ b/packages/core-api/src/versions/1/shared/transformers/voter.ts @@ -0,0 +1,8 @@ +export function transformVoterLegacy(model: any) { + return { + username: model.username, + address: model.address, + publicKey: model.publicKey, + balance: `${model.balance}`, + }; +} diff --git a/packages/core-api/src/versions/1/signatures/controller.ts b/packages/core-api/src/versions/1/signatures/controller.ts new file mode 100644 index 0000000000..ebe6a1ffc6 --- /dev/null +++ b/packages/core-api/src/versions/1/signatures/controller.ts @@ -0,0 +1,17 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class SignaturesController extends Controller { + public async fee(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const height: number = this.blockchain.getLastHeight(); + + return super.respondWith({ + fee: this.config.getMilestone(height).fees.staticFees.secondSignature, + }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/signatures/index.ts b/packages/core-api/src/versions/1/signatures/index.ts new file mode 100644 index 0000000000..f13324009a --- /dev/null +++ b/packages/core-api/src/versions/1/signatures/index.ts @@ -0,0 +1,6 @@ +import Hapi from "hapi"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/signatures/routes.ts b/packages/core-api/src/versions/1/signatures/routes.ts new file mode 100644 index 0000000000..758c6afd82 --- /dev/null +++ b/packages/core-api/src/versions/1/signatures/routes.ts @@ -0,0 +1,13 @@ +import Hapi from "hapi"; +import { SignaturesController } from "./controller"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new SignaturesController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/signatures/fee", + handler: controller.fee, + }); +} diff --git a/packages/core-api/src/versions/1/transactions/controller.ts b/packages/core-api/src/versions/1/transactions/controller.ts new file mode 100644 index 0000000000..15ce629a3f --- /dev/null +++ b/packages/core-api/src/versions/1/transactions/controller.ts @@ -0,0 +1,76 @@ +import { app } from "@arkecosystem/core-container"; +import { TransactionPool } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class TransactionsController extends Controller { + protected transactionPool = app.resolvePlugin("transactionPool"); + + public constructor() { + super(); + } + + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.transactions.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v1.transactions.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async unconfirmed(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const pagination = super.paginate(request); + + const transactions = this.transactionPool + .getTransactions(pagination.offset, pagination.limit) + .map(transaction => ({ + serialized: transaction, + })); + + return super.respondWith({ + transactions: super.toCollection(request, transactions, "transaction"), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async showUnconfirmed(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const transaction = this.transactionPool.getTransaction(request.query.id); + + if (!transaction) { + return super.respondWith("Transaction not found", true); + } + + return super.respondWith({ + transaction: super.toResource( + request, + { + serialized: transaction.serialized, + }, + "transaction", + ), + }); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/1/transactions/index.ts b/packages/core-api/src/versions/1/transactions/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/1/transactions/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/1/transactions/methods.ts b/packages/core-api/src/versions/1/transactions/methods.ts new file mode 100644 index 0000000000..9e4dac3261 --- /dev/null +++ b/packages/core-api/src/versions/1/transactions/methods.ts @@ -0,0 +1,40 @@ +import { transactionsRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWith, toCollection, toResource } from "../utils"; + +const index = async request => { + const { count, rows } = await transactionsRepository.findAllLegacy({ + ...request.query, + ...paginate(request), + }); + + if (!rows) { + return respondWith("No transactions found", true); + } + + return respondWith({ + transactions: toCollection(request, rows, "transaction"), + count, + }); +}; + +const show = async request => { + const result = await transactionsRepository.findById(request.query.id); + + if (!result) { + return respondWith("No transactions found", true); + } + + return respondWith({ + transaction: toResource(request, result, "transaction"), + }); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v1.transactions.index", index, 8, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v1.transactions.show", show, 8, request => ({ id: request.query.id })); +} diff --git a/packages/core-api/src/versions/1/transactions/routes.ts b/packages/core-api/src/versions/1/transactions/routes.ts new file mode 100644 index 0000000000..b31a618d21 --- /dev/null +++ b/packages/core-api/src/versions/1/transactions/routes.ts @@ -0,0 +1,46 @@ +import Hapi from "hapi"; +import { TransactionsController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new TransactionsController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/transactions", + handler: controller.index, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getTransactions, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/transactions/get", + handler: controller.show, + options: { + plugins: { + "hapi-ajv": { + querySchema: Schema.getTransaction, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/transactions/unconfirmed", + handler: controller.unconfirmed, + }); + + server.route({ + method: "GET", + path: "/transactions/unconfirmed/get", + handler: controller.showUnconfirmed, + }); +} diff --git a/packages/core-api/src/versions/1/transactions/schema.ts b/packages/core-api/src/versions/1/transactions/schema.ts new file mode 100644 index 0000000000..f9522579f5 --- /dev/null +++ b/packages/core-api/src/versions/1/transactions/schema.ts @@ -0,0 +1,93 @@ +export const getTransactions: object = { + type: "object", + properties: { + blockId: { + type: "string", + }, + limit: { + type: "integer", + minimum: 0, + maximum: 100, + }, + type: { + type: "integer", + minimum: 0, + maximum: 10, + }, + orderBy: { + type: "string", + }, + offset: { + type: "integer", + minimum: 0, + }, + senderPublicKey: { + type: "string", + format: "publicKey", + }, + vendorField: { + type: "string", + format: "vendorField", + }, + ownerPublicKey: { + type: "string", + format: "publicKey", + }, + ownerAddress: { + type: "string", + }, + senderId: { + type: "string", + format: "address", + }, + recipientId: { + type: "string", + format: "address", + }, + amount: { + type: "integer", + minimum: 0, + maximum: 10 ** 8, + }, + fee: { + type: "integer", + minimum: 0, + maximum: 10 ** 8, + }, + }, +}; + +export const getTransaction: object = { + type: "object", + properties: { + id: { + type: "string", + minLength: 1, + }, + }, + required: ["id"], +}; + +export const getUnconfirmedTransaction: object = { + type: "object", + properties: { + id: { + type: "string", + minLength: 1, + }, + }, + required: ["id"], +}; + +export const getUnconfirmedTransactions: object = { + type: "object", + properties: { + senderPublicKey: { + type: "string", + format: "publicKey", + }, + address: { + type: "string", + }, + }, +}; diff --git a/packages/core-api/src/versions/1/transactions/transformer.ts b/packages/core-api/src/versions/1/transactions/transformer.ts new file mode 100644 index 0000000000..6785d8b343 --- /dev/null +++ b/packages/core-api/src/versions/1/transactions/transformer.ts @@ -0,0 +1,29 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { bignumify } from "@arkecosystem/core-utils"; +import { crypto, models } from "@arkecosystem/crypto"; + +export function transformTransactionLegacy(model) { + const config = app.getConfig(); + const blockchain = app.resolvePlugin("blockchain"); + + const data: any = new models.Transaction(model.serialized.toString("hex")); + + return { + id: data.id, + blockid: model.blockId, + type: data.type, + timestamp: model.timestamp || data.timestamp, + amount: +bignumify(data.amount).toFixed(), + fee: +bignumify(data.fee).toFixed(), + recipientId: data.recipientId, + senderId: crypto.getAddress(data.senderPublicKey, config.get("network.pubKeyHash")), + senderPublicKey: data.senderPublicKey, + vendorField: data.vendorField, + signature: data.signature, + signSignature: data.signSignature, + signatures: data.signatures, + asset: data.asset || {}, + confirmations: model.block ? blockchain.getLastBlock().data.height - model.block.height : 0, + }; +} diff --git a/packages/core-api/src/versions/1/utils.ts b/packages/core-api/src/versions/1/utils.ts new file mode 100644 index 0000000000..8ccb75962c --- /dev/null +++ b/packages/core-api/src/versions/1/utils.ts @@ -0,0 +1,35 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { transformerService } from "../../services/transformer"; + +function paginate(request: Hapi.Request): any { + return { + // @ts-ignore + offset: request.query.offset || 0, + // @ts-ignore + limit: request.query.limit || 100, + }; +} + +function respondWith(data, error = false): object { + return error ? { error: data, success: false } : { ...data, success: true }; +} + +function respondWithCache(data, h): any { + const { value, cached } = data; + const lastModified = cached ? new Date(cached.stored) : new Date(); + + return value.isBoom + ? h.response(value.output.payload).code(value.output.statusCode) + : h.response(value).header("Last-modified", lastModified.toUTCString()); +} + +function toResource(request, data, transformer): object { + return transformerService.toResource(request, data, transformer); +} + +function toCollection(request, data, transformer): object { + return transformerService.toCollection(request, data, transformer); +} + +export { paginate, respondWith, respondWithCache, toResource, toCollection }; diff --git a/packages/core-api/src/versions/2/blockchain/controller.ts b/packages/core-api/src/versions/2/blockchain/controller.ts new file mode 100644 index 0000000000..ed112186cc --- /dev/null +++ b/packages/core-api/src/versions/2/blockchain/controller.ts @@ -0,0 +1,24 @@ +import { supplyCalculator } from "@arkecosystem/core-utils"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class BlockchainController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + + return { + data: { + block: { + height: lastBlock.data.height, + id: lastBlock.data.id, + }, + supply: supplyCalculator.calculate(lastBlock.data.height), + }, + }; + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/blockchain/index.ts b/packages/core-api/src/versions/2/blockchain/index.ts new file mode 100644 index 0000000000..f13324009a --- /dev/null +++ b/packages/core-api/src/versions/2/blockchain/index.ts @@ -0,0 +1,6 @@ +import Hapi from "hapi"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/blockchain/routes.ts b/packages/core-api/src/versions/2/blockchain/routes.ts new file mode 100644 index 0000000000..9d8f4bad20 --- /dev/null +++ b/packages/core-api/src/versions/2/blockchain/routes.ts @@ -0,0 +1,13 @@ +import Hapi from "hapi"; +import { BlockchainController } from "./controller"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new BlockchainController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/blockchain", + handler: controller.index, + }); +} diff --git a/packages/core-api/src/versions/2/blocks/controller.ts b/packages/core-api/src/versions/2/blocks/controller.ts new file mode 100644 index 0000000000..6882959c98 --- /dev/null +++ b/packages/core-api/src/versions/2/blocks/controller.ts @@ -0,0 +1,49 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class BlocksController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.blocks.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.blocks.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async transactions(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.blocks.transactions(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async search(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.blocks.search(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/blocks/index.ts b/packages/core-api/src/versions/2/blocks/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/2/blocks/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/blocks/methods.ts b/packages/core-api/src/versions/2/blocks/methods.ts new file mode 100644 index 0000000000..a12628c4d8 --- /dev/null +++ b/packages/core-api/src/versions/2/blocks/methods.ts @@ -0,0 +1,67 @@ +import Boom from "boom"; +import { blocksRepository, transactionsRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWithResource, toPagination } from "../utils"; + +const index = async request => { + const blocks = await blocksRepository.findAll({ + ...request.query, + ...paginate(request), + }); + + return toPagination(request, blocks, "block"); +}; + +const show = async request => { + const block = await blocksRepository.findById(request.params.id); + + if (!block) { + return Boom.notFound("Block not found"); + } + + return respondWithResource(request, block, "block"); +}; + +const transactions = async request => { + const block = await blocksRepository.findById(request.params.id); + + if (!block) { + return Boom.notFound("Block not found"); + } + + const rows = await transactionsRepository.findAllByBlock(block.id, { + ...request.query, + ...paginate(request), + }); + + return toPagination(request, rows, "transaction"); +}; + +const search = async request => { + const blocks = await blocksRepository.search({ + ...request.payload, + ...request.query, + ...paginate(request), + }); + + return toPagination(request, blocks, "block"); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v2.blocks.index", index, 6, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v2.blocks.show", show, 600, request => ({ id: request.params.id })) + .method("v2.blocks.transactions", transactions, 600, request => ({ + ...{ id: request.params.id }, + ...request.query, + ...paginate(request), + })) + .method("v2.blocks.search", search, 30, request => ({ + ...request.payload, + ...request.query, + ...paginate(request), + })); +} diff --git a/packages/core-api/src/versions/2/blocks/routes.ts b/packages/core-api/src/versions/2/blocks/routes.ts new file mode 100644 index 0000000000..ebdb465167 --- /dev/null +++ b/packages/core-api/src/versions/2/blocks/routes.ts @@ -0,0 +1,44 @@ +import Hapi from "hapi"; +import { BlocksController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new BlocksController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/blocks", + handler: controller.index, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "GET", + path: "/blocks/{id}", + handler: controller.show, + options: { + validate: Schema.show, + }, + }); + + server.route({ + method: "GET", + path: "/blocks/{id}/transactions", + handler: controller.transactions, + options: { + validate: Schema.transactions, + }, + }); + + server.route({ + method: "POST", + path: "/blocks/search", + handler: controller.search, + options: { + validate: Schema.search, + }, + }); +} diff --git a/packages/core-api/src/versions/2/blocks/schema.ts b/packages/core-api/src/versions/2/blocks/schema.ts new file mode 100644 index 0000000000..d8f70e1098 --- /dev/null +++ b/packages/core-api/src/versions/2/blocks/schema.ts @@ -0,0 +1,161 @@ +import * as Joi from "joi"; +import { pagination } from "../shared/schemas/pagination"; + +export const index: object = { + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string().regex(/^[0-9]+$/, "numbers"), + version: Joi.number() + .integer() + .min(0), + timestamp: Joi.number() + .integer() + .min(0), + previousBlock: Joi.string().regex(/^[0-9]+$/, "numbers"), + height: Joi.number() + .integer() + .positive(), + numberOfTransactions: Joi.number() + .integer() + .min(0), + totalAmount: Joi.number() + .integer() + .min(0), + totalFee: Joi.number() + .integer() + .min(0), + reward: Joi.number() + .integer() + .min(0), + payloadLength: Joi.number() + .integer() + .positive(), + payloadHash: Joi.string().hex(), + generatorPublicKey: Joi.string() + .hex() + .length(66), + blockSignature: Joi.string().hex(), + }, + }, +}; + +export const show: object = { + params: { + id: Joi.string().regex(/^[0-9]+$/, "numbers"), + }, +}; + +export const transactions: object = { + params: { + id: Joi.string(), + }, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(66), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + type: Joi.number() + .integer() + .min(0), + version: Joi.number() + .integer() + .min(0), + senderPublicKey: Joi.string() + .hex() + .length(66), + senderId: Joi.string() + .alphanum() + .length(34), + recipientId: Joi.string() + .alphanum() + .length(34), + timestamp: Joi.number() + .integer() + .min(0), + amount: Joi.number() + .integer() + .min(0), + fee: Joi.number() + .integer() + .min(0), + vendorFieldHex: Joi.string().hex(), + }, + }, +}; + +export const search: object = { + query: pagination, + payload: { + id: Joi.string().regex(/^[0-9]+$/, "numbers"), + version: Joi.number() + .integer() + .min(0), + previousBlock: Joi.string().regex(/^[0-9]+$/, "numbers"), + payloadHash: Joi.string().hex(), + generatorPublicKey: Joi.string() + .hex() + .length(66), + blockSignature: Joi.string().hex(), + timestamp: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + height: Joi.object().keys({ + from: Joi.number() + .integer() + .positive(), + to: Joi.number() + .integer() + .positive(), + }), + numberOfTransactions: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + totalAmount: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + totalFee: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + reward: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + payloadLength: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + }, +}; diff --git a/packages/core-api/src/versions/2/blocks/transformer.ts b/packages/core-api/src/versions/2/blocks/transformer.ts new file mode 100644 index 0000000000..cd22eb234e --- /dev/null +++ b/packages/core-api/src/versions/2/blocks/transformer.ts @@ -0,0 +1,37 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { bignumify, formatTimestamp } from "@arkecosystem/core-utils"; + +export function transformBlock(model) { + const databaseService = app.resolvePlugin("database"); + const generator = databaseService.walletManager.findByPublicKey(model.generatorPublicKey); + + model.reward = bignumify(model.reward); + model.totalFee = bignumify(model.totalFee); + + return { + id: model.id, + version: +model.version, + height: +model.height, + previous: model.previousBlock, + forged: { + reward: +model.reward.toFixed(), + fee: +model.totalFee.toFixed(), + total: +model.reward.plus(model.totalFee).toFixed(), + amount: +bignumify(model.totalAmount).toFixed(), + }, + payload: { + hash: model.payloadHash, + length: model.payloadLength, + }, + generator: { + username: generator.username, + address: generator.address, + publicKey: generator.publicKey, + }, + signature: model.blockSignature, + confirmations: model.confirmations, + transactions: model.numberOfTransactions, + timestamp: formatTimestamp(model.timestamp), + }; +} diff --git a/packages/core-api/src/versions/2/delegates/controller.ts b/packages/core-api/src/versions/2/delegates/controller.ts new file mode 100644 index 0000000000..c36d413476 --- /dev/null +++ b/packages/core-api/src/versions/2/delegates/controller.ts @@ -0,0 +1,71 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class DelegatesController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.delegates.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.delegates.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async search(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.delegates.search(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async blocks(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.delegates.blocks(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async voters(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.delegates.voters(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async voterBalances(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.delegates.voterBalances(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/delegates/index.ts b/packages/core-api/src/versions/2/delegates/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/2/delegates/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/delegates/methods.ts b/packages/core-api/src/versions/2/delegates/methods.ts new file mode 100644 index 0000000000..be2342af89 --- /dev/null +++ b/packages/core-api/src/versions/2/delegates/methods.ts @@ -0,0 +1,102 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; +import orderBy from "lodash/orderBy"; +import { blocksRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWithResource, toPagination } from "../utils"; + +const databaseService = app.resolvePlugin("database"); + +const index = async request => { + const delegates = await databaseService.delegates.findAll({ + ...request.query, + ...paginate(request), + }); + + return toPagination(request, delegates, "delegate"); +}; + +const show = async request => { + const delegate = await databaseService.delegates.findById(request.params.id); + + if (!delegate) { + return Boom.notFound("Delegate not found"); + } + + return respondWithResource(request, delegate, "delegate"); +}; + +const search = async request => { + const delegates = await databaseService.delegates.search({ + ...request.payload, + ...request.query, + ...paginate(request), + }); + + return toPagination(request, delegates, "delegate"); +}; + +const blocks = async request => { + const delegate = await databaseService.delegates.findById(request.params.id); + + if (!delegate) { + return Boom.notFound("Delegate not found"); + } + + const rows = await blocksRepository.findAllByGenerator(delegate.publicKey, paginate(request)); + + return toPagination(request, rows, "block"); +}; + +const voters = async request => { + const delegate = await databaseService.delegates.findById(request.params.id); + + if (!delegate) { + return Boom.notFound("Delegate not found"); + } + + const wallets = await databaseService.wallets.findAllByVote(delegate.publicKey, paginate(request)); + + return toPagination(request, wallets, "wallet"); +}; + +const voterBalances = async request => { + const delegate = await databaseService.delegates.findById(request.params.id); + + if (!delegate) { + return Boom.notFound("Delegate not found"); + } + + const wallets = await databaseService.wallets.all().filter(wallet => wallet.vote === delegate.publicKey); + + const data = {}; + orderBy(wallets, ["balance"], ["desc"]).forEach(wallet => { + data[wallet.address] = +wallet.balance.toFixed(); + }); + + return { data }; +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v2.delegates.index", index, 8, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v2.delegates.show", show, 8, request => ({ id: request.params.id })) + .method("v2.delegates.search", search, 30, request => ({ + ...request.payload, + ...request.query, + ...paginate(request), + })) + .method("v2.delegates.blocks", blocks, 8, request => ({ + ...{ id: request.params.id }, + ...paginate(request), + })) + .method("v2.delegates.voters", voters, 8, request => ({ + ...{ id: request.params.id }, + ...paginate(request), + })) + .method("v2.delegates.voterBalances", voterBalances, 8, request => ({ id: request.params.id })); +} diff --git a/packages/core-api/src/versions/2/delegates/routes.ts b/packages/core-api/src/versions/2/delegates/routes.ts new file mode 100644 index 0000000000..b1c7705e7c --- /dev/null +++ b/packages/core-api/src/versions/2/delegates/routes.ts @@ -0,0 +1,62 @@ +import Hapi from "hapi"; +import { DelegatesController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new DelegatesController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/delegates", + handler: controller.index, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/{id}", + handler: controller.show, + options: { + validate: Schema.show, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/{id}/blocks", + handler: controller.blocks, + options: { + validate: Schema.blocks, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/{id}/voters", + handler: controller.voters, + options: { + validate: Schema.voters, + }, + }); + + server.route({ + method: "GET", + path: "/delegates/{id}/voters/balances", + handler: controller.voterBalances, + options: { + validate: Schema.voterBalances, + }, + }); + + server.route({ + method: "POST", + path: "/delegates/search", + handler: controller.search, + options: { + validate: Schema.search, + }, + }); +} diff --git a/packages/core-api/src/versions/2/delegates/schema.ts b/packages/core-api/src/versions/2/delegates/schema.ts new file mode 100644 index 0000000000..8ebb84cf26 --- /dev/null +++ b/packages/core-api/src/versions/2/delegates/schema.ts @@ -0,0 +1,145 @@ +import * as Joi from "joi"; +import { pagination } from "../shared/schemas/pagination"; + +const schemaIdentifier = Joi.string() + .regex(/^[a-zA-Z0-9!@$&_.]+$/) + .min(1) + .max(66); + +const schemaUsername = Joi.string() + .regex(/^[a-z0-9!@$&_.]+$/) + .min(1) + .max(20); + +export const index: object = { + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + address: Joi.string() + .alphanum() + .length(34), + publicKey: Joi.string() + .hex() + .length(66), + secondPublicKey: Joi.string() + .hex() + .length(66), + vote: Joi.string() + .hex() + .length(66), + username: schemaUsername, + balance: Joi.number() + .integer() + .min(0), + voteBalance: Joi.number() + .integer() + .min(0), + producedBlocks: Joi.number() + .integer() + .min(0), + missedBlocks: Joi.number() + .integer() + .min(0), + }, + }, +}; + +export const show: object = { + params: { + id: schemaIdentifier, + }, +}; + +export const search: object = { + query: pagination, + payload: { + username: schemaUsername, + }, +}; + +export const blocks: object = { + params: { + id: schemaIdentifier, + }, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string().regex(/^[0-9]+$/, "numbers"), + version: Joi.number() + .integer() + .min(0), + timestamp: Joi.number() + .integer() + .min(0), + previousBlock: Joi.string().regex(/^[0-9]+$/, "numbers"), + height: Joi.number() + .integer() + .positive(), + numberOfTransactions: Joi.number() + .integer() + .min(0), + totalAmount: Joi.number() + .integer() + .min(0), + totalFee: Joi.number() + .integer() + .min(0), + reward: Joi.number() + .integer() + .min(0), + payloadLength: Joi.number() + .integer() + .min(0), + payloadHash: Joi.string().hex(), + generatorPublicKey: Joi.string() + .hex() + .length(66), + blockSignature: Joi.string().hex(), + }, + }, +}; + +export const voters: object = { + params: { + id: schemaIdentifier, + }, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + address: Joi.string() + .alphanum() + .length(34), + publicKey: Joi.string() + .hex() + .length(66), + secondPublicKey: Joi.string() + .hex() + .length(66), + vote: Joi.string() + .hex() + .length(66), + username: schemaUsername, + balance: Joi.number() + .integer() + .min(0), + voteBalance: Joi.number() + .integer() + .min(0), + producedBlocks: Joi.number() + .integer() + .min(0), + missedBlocks: Joi.number() + .integer() + .min(0), + }, + }, +}; + +export const voterBalances: object = { + params: { + id: schemaIdentifier, + }, +}; diff --git a/packages/core-api/src/versions/2/delegates/transformer.ts b/packages/core-api/src/versions/2/delegates/transformer.ts new file mode 100644 index 0000000000..6fdb040209 --- /dev/null +++ b/packages/core-api/src/versions/2/delegates/transformer.ts @@ -0,0 +1,37 @@ +import { bignumify, delegateCalculator, formatTimestamp } from "@arkecosystem/core-utils"; + +export function transformDelegate(delegate) { + const data = { + username: delegate.username, + address: delegate.address, + publicKey: delegate.publicKey, + votes: +bignumify(delegate.voteBalance).toFixed(), + rank: delegate.rate, + blocks: { + produced: delegate.producedBlocks, + missed: delegate.missedBlocks, + }, + production: { + approval: delegateCalculator.calculateApproval(delegate), + productivity: delegateCalculator.calculateProductivity(delegate), + }, + forged: { + fees: +delegate.forgedFees.toFixed(), + rewards: +delegate.forgedRewards.toFixed(), + total: +delegate.forgedFees.plus(delegate.forgedRewards).toFixed(), + }, + }; + + const lastBlock = delegate.lastBlock; + + if (lastBlock) { + // @ts-ignore + data.blocks.last = { + id: lastBlock.id, + height: lastBlock.height, + timestamp: formatTimestamp(lastBlock.timestamp), + }; + } + + return data; +} diff --git a/packages/core-api/src/versions/2/index.ts b/packages/core-api/src/versions/2/index.ts new file mode 100644 index 0000000000..5600a74a8f --- /dev/null +++ b/packages/core-api/src/versions/2/index.ts @@ -0,0 +1,23 @@ +import Hapi from "hapi"; +import * as Blockchain from "./blockchain"; +import * as Blocks from "./blocks"; +import * as Delegates from "./delegates"; +import * as Node from "./node"; +import * as Peers from "./peers"; +import * as Transactions from "./transactions"; +import * as Votes from "./votes"; +import * as Wallets from "./wallets"; + +const register = async (server: Hapi.Server): Promise => { + const modules = [Blockchain, Blocks, Delegates, Node, Peers, Transactions, Votes, Wallets]; + + for (const module of modules) { + module.register(server); + } +}; + +export = { + register, + name: "Public API", + version: "2.0.0", +}; diff --git a/packages/core-api/src/versions/2/node/controller.ts b/packages/core-api/src/versions/2/node/controller.ts new file mode 100644 index 0000000000..51c7fa7d84 --- /dev/null +++ b/packages/core-api/src/versions/2/node/controller.ts @@ -0,0 +1,68 @@ +import { app } from "@arkecosystem/core-container"; +import Boom from "boom"; +import Hapi from "hapi"; +import { transactionsRepository } from "../../../repositories"; +import { Controller } from "../shared/controller"; + +export class NodeController extends Controller { + public async status(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + const networkHeight = await this.blockchain.p2p.getNetworkHeight(); + + return { + data: { + synced: this.blockchain.isSynced(), + now: lastBlock ? lastBlock.data.height : 0, + blocksCount: networkHeight - lastBlock.data.height || 0, + }, + }; + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async syncing(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const lastBlock = this.blockchain.getLastBlock(); + const networkHeight = await this.blockchain.p2p.getNetworkHeight(); + + return { + data: { + syncing: !this.blockchain.isSynced(), + blocks: networkHeight - lastBlock.data.height || 0, + height: lastBlock.data.height, + id: lastBlock.data.id, + }, + }; + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async configuration(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const feeStatisticsData = await transactionsRepository.getFeeStatistics(); + + const network = this.config.get("network"); + + return { + data: { + nethash: network.nethash, + token: network.client.token, + symbol: network.client.symbol, + explorer: network.client.explorer, + version: network.pubKeyHash, + ports: super.toResource(request, this.config, "ports"), + constants: this.config.getMilestone(this.blockchain.getLastHeight()), + feeStatistics: super.toCollection(request, feeStatisticsData, "fee-statistics"), + transactionPool: { + maxTransactionAge: app.resolveOptions("transactionPool").maxTransactionAge, + }, + }, + }; + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/node/index.ts b/packages/core-api/src/versions/2/node/index.ts new file mode 100644 index 0000000000..f13324009a --- /dev/null +++ b/packages/core-api/src/versions/2/node/index.ts @@ -0,0 +1,6 @@ +import Hapi from "hapi"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/node/routes.ts b/packages/core-api/src/versions/2/node/routes.ts new file mode 100644 index 0000000000..7e7a799ddb --- /dev/null +++ b/packages/core-api/src/versions/2/node/routes.ts @@ -0,0 +1,25 @@ +import Hapi from "hapi"; +import { NodeController } from "./controller"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new NodeController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/node/status", + handler: controller.status, + }); + + server.route({ + method: "GET", + path: "/node/syncing", + handler: controller.syncing, + }); + + server.route({ + method: "GET", + path: "/node/configuration", + handler: controller.configuration, + }); +} diff --git a/packages/core-api/src/versions/2/peers/controller.ts b/packages/core-api/src/versions/2/peers/controller.ts new file mode 100644 index 0000000000..ddf74a28c5 --- /dev/null +++ b/packages/core-api/src/versions/2/peers/controller.ts @@ -0,0 +1,84 @@ +import { app } from "@arkecosystem/core-container"; +import { P2P } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class PeersController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const allPeers = await this.blockchain.p2p.getPeers(); + + let result = allPeers.sort((a, b) => a.delay - b.delay); + // @ts-ignore + result = request.query.os + ? // @ts-ignore + result.filter(peer => peer.os === (request.query as any).os) + : result; + // @ts-ignore + result = request.query.status + ? // @ts-ignore + result.filter(peer => peer.status === (request.query as any).status) + : result; + // @ts-ignore + result = request.query.port + ? // @ts-ignore + result.filter(peer => peer.port === (request.query as any).port) + : result; + // @ts-ignore + result = request.query.version + ? // @ts-ignore + result.filter(peer => peer.version === (request.query as any).version) + : result; + // @ts-ignore + result = result.slice(0, request.query.limit || 100); + + // @ts-ignore + if (request.query.orderBy) { + // @ts-ignore + const order = request.query.orderBy.split(":"); + + if (["port", "status", "os", "version"].includes(order[0])) { + result = + order[1].toUpperCase() === "ASC" + ? result.sort((a, b) => a[order[0]] - b[order[0]]) + : result.sort((a, b) => a[order[0]] + b[order[0]]); + } + } + + return super.toPagination(request, { rows: result, count: allPeers.length }, "peer"); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const peers = await this.blockchain.p2p.getPeers(); + const peer = peers.find(p => p.ip === request.params.ip); + + if (!peer) { + return Boom.notFound("Peer not found"); + } + + return super.respondWithResource(request, peer, "peer"); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async suspended(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + const peers = app.resolvePlugin("p2p").getSuspendedPeers(); + + return super.respondWithCollection( + request, + // @ts-ignore + Object.values(peers).map(peer => peer.peer), + "peer", + ); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/peers/index.ts b/packages/core-api/src/versions/2/peers/index.ts new file mode 100644 index 0000000000..f13324009a --- /dev/null +++ b/packages/core-api/src/versions/2/peers/index.ts @@ -0,0 +1,6 @@ +import Hapi from "hapi"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/peers/routes.ts b/packages/core-api/src/versions/2/peers/routes.ts new file mode 100644 index 0000000000..962002f5ea --- /dev/null +++ b/packages/core-api/src/versions/2/peers/routes.ts @@ -0,0 +1,32 @@ +import Hapi from "hapi"; +import { PeersController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new PeersController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/peers", + handler: controller.index, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "GET", + path: "/peers/suspended", + handler: controller.suspended, + }); + + server.route({ + method: "GET", + path: "/peers/{ip}", + handler: controller.show, + options: { + validate: Schema.show, + }, + }); +} diff --git a/packages/core-api/src/versions/2/peers/schema.ts b/packages/core-api/src/versions/2/peers/schema.ts new file mode 100644 index 0000000000..762fc6e09e --- /dev/null +++ b/packages/core-api/src/versions/2/peers/schema.ts @@ -0,0 +1,22 @@ +import * as Joi from "joi"; +import { pagination } from "../shared/schemas/pagination"; + +export const index: object = { + query: { + ...pagination, + ...{ + ip: Joi.string().ip(), + os: Joi.string(), + status: Joi.string(), + port: Joi.number().port(), + version: Joi.string(), + orderBy: Joi.string(), + }, + }, +}; + +export const show: object = { + params: { + ip: Joi.string().ip(), + }, +}; diff --git a/packages/core-api/src/versions/2/peers/transformer.ts b/packages/core-api/src/versions/2/peers/transformer.ts new file mode 100644 index 0000000000..b9837f021e --- /dev/null +++ b/packages/core-api/src/versions/2/peers/transformer.ts @@ -0,0 +1,21 @@ +import { app } from "@arkecosystem/core-container"; + +export function transformPeer(model) { + const config = app.getConfig(); + + const peer: any = { + ip: model.ip, + port: +model.port, + version: model.version, + height: model.state ? model.state.height : model.height, + status: [200, "OK"].includes(model.status) ? 200 : 400, + os: model.os, + latency: model.delay, + }; + + if (config.get("network.name") !== "mainnet") { + peer.hashid = model.hashid || "unknown"; + } + + return peer; +} diff --git a/packages/core-api/src/versions/2/shared/controller.ts b/packages/core-api/src/versions/2/shared/controller.ts new file mode 100644 index 0000000000..659a8474d2 --- /dev/null +++ b/packages/core-api/src/versions/2/shared/controller.ts @@ -0,0 +1,46 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database } from "@arkecosystem/core-interfaces"; +import Hapi from "hapi"; +import { + paginate, + respondWithCache, + respondWithCollection, + respondWithResource, + toCollection, + toPagination, + toResource, +} from "../utils"; + +export class Controller { + protected config = app.getConfig(); + protected blockchain = app.resolvePlugin("blockchain"); + protected databaseService = app.resolvePlugin("database"); + + protected paginate(request: Hapi.Request): any { + return paginate(request); + } + + protected respondWithResource(request, data, transformer): any { + return respondWithResource(request, data, transformer); + } + + protected respondWithCollection(request, data, transformer): object { + return respondWithCollection(request, data, transformer); + } + + protected respondWithCache(data, h) { + return respondWithCache(data, h); + } + + protected toResource(request, data, transformer): object { + return toResource(request, data, transformer); + } + + protected toCollection(request, data, transformer): object { + return toCollection(request, data, transformer); + } + + protected toPagination(request, data, transformer): object { + return toPagination(request, data, transformer); + } +} diff --git a/packages/core-api/src/versions/2/shared/schemas/pagination.ts b/packages/core-api/src/versions/2/shared/schemas/pagination.ts new file mode 100644 index 0000000000..01edca9da5 --- /dev/null +++ b/packages/core-api/src/versions/2/shared/schemas/pagination.ts @@ -0,0 +1,14 @@ +import * as Joi from "joi"; + +export const pagination = { + page: Joi.number() + .integer() + .positive(), + offset: Joi.number() + .integer() + .min(0), + limit: Joi.number() + .integer() + .min(1) + .max(100), +}; diff --git a/packages/core-api/src/versions/2/shared/transformers/fee-statistics.ts b/packages/core-api/src/versions/2/shared/transformers/fee-statistics.ts new file mode 100644 index 0000000000..9c8309c0ff --- /dev/null +++ b/packages/core-api/src/versions/2/shared/transformers/fee-statistics.ts @@ -0,0 +1,10 @@ +export function transformFeeStatistics(model: any) { + return { + type: model.type, + fees: { + minFee: parseInt(model.minFee, 10), + maxFee: parseInt(model.maxFee, 10), + avgFee: parseInt(model.avgFee, 10), + }, + }; +} diff --git a/packages/core-api/src/versions/2/shared/transformers/ports.ts b/packages/core-api/src/versions/2/shared/transformers/ports.ts new file mode 100644 index 0000000000..3193f00719 --- /dev/null +++ b/packages/core-api/src/versions/2/shared/transformers/ports.ts @@ -0,0 +1,32 @@ +export function transformPorts(config: any) { + const result = {}; + const keys = [ + "@arkecosystem/core-p2p", + "@arkecosystem/core-api", + "@arkecosystem/core-graphql", + "@arkecosystem/core-json-rpc", + "@arkecosystem/core-webhooks", + ]; + + const plugins = config.get("plugins"); + + result[keys[0]] = +plugins[keys[0]].port; + + for (const [name, options] of Object.entries(plugins)) { + // @ts-ignore + if (keys.includes(name) && options.enabled) { + // @ts-ignore + if (options.server && options.server.enabled) { + // @ts-ignore + result[name] = +options.server.port; + + continue; + } + + // @ts-ignore + result[name] = +options.port; + } + } + + return result; +} diff --git a/packages/core-api/src/versions/2/transactions/controller.ts b/packages/core-api/src/versions/2/transactions/controller.ts new file mode 100644 index 0000000000..2cbdc28ecd --- /dev/null +++ b/packages/core-api/src/versions/2/transactions/controller.ts @@ -0,0 +1,149 @@ +import { app } from "@arkecosystem/core-container"; +import { P2P } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +import { TransactionGuard, TransactionPool } from "@arkecosystem/core-transaction-pool"; +import { constants } from "@arkecosystem/crypto"; + +export class TransactionsController extends Controller { + private transactionPool = app.resolvePlugin("transactionPool"); + + public constructor() { + super(); + } + + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.transactions.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async store(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + if (!this.transactionPool.options.enabled) { + return Boom.serverUnavailable("Transaction pool is disabled."); + } + + const guard = new TransactionGuard(this.transactionPool); + + const result = await guard.validate((request.payload as any).transactions); + + if (result.broadcast.length > 0) { + app.resolvePlugin("p2p").broadcastTransactions(guard.getBroadcastTransactions()); + } + + return { + data: { + accept: result.accept, + broadcast: result.broadcast, + excess: result.excess, + invalid: result.invalid, + }, + errors: result.errors, + }; + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.transactions.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async unconfirmed(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + if (!this.transactionPool.options.enabled) { + return Boom.serverUnavailable("Transaction pool is disabled."); + } + + const pagination = super.paginate(request); + + let transactions = this.transactionPool.getTransactions(pagination.offset, pagination.limit); + transactions = transactions.map(transaction => ({ + serialized: transaction, + })); + + return super.toPagination( + request, + { + count: this.transactionPool.getPoolSize(), + rows: transactions, + }, + "transaction", + ); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async showUnconfirmed(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + if (!this.transactionPool.options.enabled) { + return Boom.serverUnavailable("Transaction pool is disabled."); + } + + let transaction = this.transactionPool.getTransaction(request.params.id); + + if (!transaction) { + return Boom.notFound("Transaction not found"); + } + + transaction = { serialized: transaction.serialized }; + + return super.respondWithResource(request, transaction, "transaction"); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async search(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.transactions.search(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async types(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // Remove reverse mapping from TransactionTypes enum. + const { TransactionTypes } = constants; + const data = Object.assign({}, TransactionTypes); + Object.values(TransactionTypes) + .filter(value => typeof value === "string") + .map((type: string) => data[type]) + .forEach((key: string) => delete data[key]); + + return { data }; + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async fees(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + return { + data: this.config.getMilestone(this.blockchain.getLastHeight()).fees.staticFees, + }; + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/transactions/index.ts b/packages/core-api/src/versions/2/transactions/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/2/transactions/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/transactions/methods.ts b/packages/core-api/src/versions/2/transactions/methods.ts new file mode 100644 index 0000000000..04707c22d6 --- /dev/null +++ b/packages/core-api/src/versions/2/transactions/methods.ts @@ -0,0 +1,47 @@ +import Boom from "boom"; +import { transactionsRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWithResource, toPagination } from "../utils"; + +const index = async request => { + const transactions = await transactionsRepository.findAll({ + ...request.query, + ...paginate(request), + }); + + return toPagination(request, transactions, "transaction"); +}; + +const show = async request => { + const transaction = await transactionsRepository.findById(request.params.id); + + if (!transaction) { + return Boom.notFound("Transaction not found"); + } + + return respondWithResource(request, transaction, "transaction"); +}; + +const search = async request => { + const transactions = await transactionsRepository.search({ + ...request.query, + ...request.payload, + ...paginate(request), + }); + + return toPagination(request, transactions, "transaction"); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v2.transactions.index", index, 8, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v2.transactions.show", show, 8, request => ({ id: request.params.id })) + .method("v2.transactions.search", search, 30, request => ({ + ...request.payload, + ...request.query, + ...paginate(request), + })); +} diff --git a/packages/core-api/src/versions/2/transactions/routes.ts b/packages/core-api/src/versions/2/transactions/routes.ts new file mode 100644 index 0000000000..b4a9e47663 --- /dev/null +++ b/packages/core-api/src/versions/2/transactions/routes.ts @@ -0,0 +1,79 @@ +import Hapi from "hapi"; +import { TransactionsController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new TransactionsController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/transactions", + handler: controller.index, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "POST", + path: "/transactions", + handler: controller.store, + options: { + validate: Schema.store, + plugins: { + pagination: { + enabled: false, + }, + }, + }, + }); + + server.route({ + method: "GET", + path: "/transactions/{id}", + handler: controller.show, + options: { + validate: Schema.show, + }, + }); + + server.route({ + method: "GET", + path: "/transactions/unconfirmed", + handler: controller.unconfirmed, + options: { + validate: Schema.unconfirmed, + }, + }); + + server.route({ + method: "GET", + path: "/transactions/unconfirmed/{id}", + handler: controller.showUnconfirmed, + options: { + validate: Schema.showUnconfirmed, + }, + }); + + server.route({ + method: "POST", + path: "/transactions/search", + handler: controller.search, + options: { + validate: Schema.search, + }, + }); + + server.route({ + method: "GET", + path: "/transactions/types", + handler: controller.types, + }); + + server.route({ + method: "GET", + path: "/transactions/fees", + handler: controller.fees, + }); +} diff --git a/packages/core-api/src/versions/2/transactions/schema.ts b/packages/core-api/src/versions/2/transactions/schema.ts new file mode 100644 index 0000000000..aeacd37f26 --- /dev/null +++ b/packages/core-api/src/versions/2/transactions/schema.ts @@ -0,0 +1,130 @@ +import { app } from "@arkecosystem/core-container"; +import { Joi } from "@arkecosystem/crypto"; +import { pagination } from "../shared/schemas/pagination"; + +export const index: object = { + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(64), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + type: Joi.number() + .integer() + .min(0), + version: Joi.number() + .integer() + .positive(), + senderPublicKey: Joi.string() + .hex() + .length(66), + senderId: Joi.string() + .alphanum() + .length(34), + recipientId: Joi.string() + .alphanum() + .length(34), + ownerId: Joi.string() + .alphanum() + .length(34), + timestamp: Joi.number() + .integer() + .min(0), + amount: Joi.number() + .integer() + .min(0), + fee: Joi.number() + .integer() + .min(0), + vendorFieldHex: Joi.string().hex(), + }, + }, +}; + +export const store: object = { + payload: { + transactions: Joi.transactionArray() + .min(1) + .max(app.resolveOptions("transactionPool").maxTransactionsPerRequest) + .options({ stripUnknown: true }), + }, +}; + +export const show: object = { + params: { + id: Joi.string() + .hex() + .length(64), + }, +}; + +export const unconfirmed: object = { + query: pagination, +}; + +export const showUnconfirmed: object = { + params: { + id: Joi.string() + .hex() + .length(64), + }, +}; + +const address: object = Joi.string() + .alphanum() + .length(34); + +export const search: object = { + query: pagination, + payload: { + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(64), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + type: Joi.number() + .integer() + .min(0), + version: Joi.number() + .integer() + .positive(), + senderPublicKey: Joi.string() + .hex() + .length(66), + senderId: address, + recipientId: address, + ownerId: address, + addresses: Joi.array() + .unique() + .min(1) + .max(50) + .items(address), + vendorFieldHex: Joi.string().hex(), + timestamp: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + amount: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + fee: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + }, +}; diff --git a/packages/core-api/src/versions/2/transactions/transformer.ts b/packages/core-api/src/versions/2/transactions/transformer.ts new file mode 100644 index 0000000000..1b8d48c745 --- /dev/null +++ b/packages/core-api/src/versions/2/transactions/transformer.ts @@ -0,0 +1,30 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { bignumify, formatTimestamp } from "@arkecosystem/core-utils"; +import { crypto, models } from "@arkecosystem/crypto"; + +export function transformTransaction(model) { + const config = app.getConfig(); + const blockchain = app.resolvePlugin("blockchain"); + + const data: any = new models.Transaction(model.serialized.toString("hex")); + const lastBlock = blockchain.getLastBlock(); + + return { + id: data.id, + blockId: model.blockId, + version: data.version, + type: data.type, + amount: +bignumify(data.amount).toFixed(), + fee: +bignumify(data.fee).toFixed(), + sender: crypto.getAddress(data.senderPublicKey, config.get("network.pubKeyHash")), + recipient: data.recipientId, + signature: data.signature, + signSignature: data.signSignature, + signatures: data.signatures, + vendorField: data.vendorField, + asset: data.asset, + confirmations: model.block ? lastBlock.data.height - model.block.height : 0, + timestamp: formatTimestamp(model.timestamp || data.timestamp), + }; +} diff --git a/packages/core-api/src/versions/2/utils.ts b/packages/core-api/src/versions/2/utils.ts new file mode 100644 index 0000000000..442fd4aa34 --- /dev/null +++ b/packages/core-api/src/versions/2/utils.ts @@ -0,0 +1,64 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { transformerService } from "../../services/transformer"; + +function paginate(request: Hapi.Request): any { + const pagination = { + // @ts-ignore + offset: (request.query.page - 1) * request.query.limit || 0, + // @ts-ignore + limit: request.query.limit || 100, + }; + + // @ts-ignore + if (request.query.offset) { + // @ts-ignore + pagination.offset = request.query.offset; + } + + return pagination; +} + +function respondWithResource(request, data, transformer): any { + return data ? { data: transformerService.toResource(request, data, transformer) } : Boom.notFound(); +} + +function respondWithCollection(request, data, transformer): object { + return { + data: transformerService.toCollection(request, data, transformer), + }; +} + +function respondWithCache(data, h): any { + const { value, cached } = data; + const lastModified = cached ? new Date(cached.stored) : new Date(); + + return value.isBoom + ? h.response(value.output.payload).code(value.output.statusCode) + : h.response(value).header("Last-modified", lastModified.toUTCString()); +} + +function toResource(request, data, transformer): object { + return transformerService.toResource(request, data, transformer); +} + +function toCollection(request, data, transformer): object { + return transformerService.toCollection(request, data, transformer); +} + +function toPagination(request, data, transformer): object { + return { + results: transformerService.toCollection(request, data.rows, transformer), + totalCount: data.count, + }; +} + +export { + paginate, + respondWithResource, + respondWithCollection, + respondWithCache, + toResource, + toCollection, + toPagination, +}; diff --git a/packages/core-api/src/versions/2/votes/controller.ts b/packages/core-api/src/versions/2/votes/controller.ts new file mode 100644 index 0000000000..0ef2901ee5 --- /dev/null +++ b/packages/core-api/src/versions/2/votes/controller.ts @@ -0,0 +1,27 @@ +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class VotesController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.votes.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.votes.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/votes/index.ts b/packages/core-api/src/versions/2/votes/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/2/votes/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/votes/methods.ts b/packages/core-api/src/versions/2/votes/methods.ts new file mode 100644 index 0000000000..59863752a5 --- /dev/null +++ b/packages/core-api/src/versions/2/votes/methods.ts @@ -0,0 +1,35 @@ +import { constants } from "@arkecosystem/crypto"; +import Boom from "boom"; +import { transactionsRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWithResource, toPagination } from "../utils"; + +const { TransactionTypes } = constants; + +const index = async request => { + const transactions = await transactionsRepository.findAllByType(TransactionTypes.Vote, { + ...request.query, + ...paginate(request), + }); + + return toPagination(request, transactions, "transaction"); +}; + +const show = async request => { + const transaction = await transactionsRepository.findByTypeAndId(TransactionTypes.Vote, request.params.id); + + if (!transaction) { + return Boom.notFound("Vote not found"); + } + + return respondWithResource(request, transaction, "transaction"); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v2.votes.index", index, 8, request => ({ + ...request.query, + ...paginate(request), + })) + .method("v2.votes.show", show, 8, request => ({ id: request.params.id })); +} diff --git a/packages/core-api/src/versions/2/votes/routes.ts b/packages/core-api/src/versions/2/votes/routes.ts new file mode 100644 index 0000000000..b01252f8f5 --- /dev/null +++ b/packages/core-api/src/versions/2/votes/routes.ts @@ -0,0 +1,26 @@ +import Hapi from "hapi"; +import { VotesController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new VotesController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/votes", + handler: controller.index, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "GET", + path: "/votes/{id}", + handler: controller.show, + options: { + validate: Schema.show, + }, + }); +} diff --git a/packages/core-api/src/versions/2/votes/schema.ts b/packages/core-api/src/versions/2/votes/schema.ts new file mode 100644 index 0000000000..1f9acde79d --- /dev/null +++ b/packages/core-api/src/versions/2/votes/schema.ts @@ -0,0 +1,45 @@ +import * as Joi from "joi"; +import { pagination } from "../shared/schemas/pagination"; + +export const index: object = { + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(64), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + version: Joi.number() + .integer() + .positive(), + senderPublicKey: Joi.string() + .hex() + .length(66), + senderId: Joi.string() + .alphanum() + .length(34), + recipientId: Joi.string() + .alphanum() + .length(34), + timestamp: Joi.number() + .integer() + .min(0), + amount: Joi.number() + .integer() + .min(0), + fee: Joi.number() + .integer() + .min(0), + vendorFieldHex: Joi.string().hex(), + }, + }, +}; + +export const show: object = { + params: { + id: Joi.string() + .hex() + .length(64), + }, +}; diff --git a/packages/core-api/src/versions/2/wallets/controller.ts b/packages/core-api/src/versions/2/wallets/controller.ts new file mode 100644 index 0000000000..07855fffb6 --- /dev/null +++ b/packages/core-api/src/versions/2/wallets/controller.ts @@ -0,0 +1,94 @@ +import { app } from "@arkecosystem/core-container"; +import Boom from "boom"; +import Hapi from "hapi"; +import { Controller } from "../shared/controller"; + +export class WalletsController extends Controller { + public async index(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.index(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async top(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.top(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async show(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.show(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async transactions(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.transactions(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async transactionsSent(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.transactionsSent(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async transactionsReceived(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.transactionsReceived(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async votes(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.votes(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } + + public async search(request: Hapi.Request, h: Hapi.ResponseToolkit) { + try { + // @ts-ignore + const data = await request.server.methods.v2.wallets.search(request); + + return super.respondWithCache(data, h); + } catch (error) { + return Boom.badImplementation(error); + } + } +} diff --git a/packages/core-api/src/versions/2/wallets/index.ts b/packages/core-api/src/versions/2/wallets/index.ts new file mode 100644 index 0000000000..d2511817db --- /dev/null +++ b/packages/core-api/src/versions/2/wallets/index.ts @@ -0,0 +1,8 @@ +import Hapi from "hapi"; +import { registerMethods } from "./methods"; +import { registerRoutes } from "./routes"; + +export function register(server: Hapi.Server): void { + registerMethods(server); + registerRoutes(server); +} diff --git a/packages/core-api/src/versions/2/wallets/methods.ts b/packages/core-api/src/versions/2/wallets/methods.ts new file mode 100644 index 0000000000..c354ff87f5 --- /dev/null +++ b/packages/core-api/src/versions/2/wallets/methods.ts @@ -0,0 +1,154 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; +import { transactionsRepository } from "../../../repositories"; +import { ServerCache } from "../../../services"; +import { paginate, respondWithResource, toPagination } from "../utils"; + +const databaseService = app.resolvePlugin("database"); + +const index = async request => { + const wallets = await databaseService.wallets.findAll({ + ...request.query, + ...paginate(request), + }); + + return toPagination(request, wallets, "wallet"); +}; + +const top = async request => { + const wallets = await databaseService.wallets.top(paginate(request)); + + return toPagination(request, wallets, "wallet"); +}; + +const show = async request => { + const wallet = await databaseService.wallets.findById(request.params.id); + + if (!wallet) { + return Boom.notFound("Wallet not found"); + } + + return respondWithResource(request, wallet, "wallet"); +}; + +const transactions = async request => { + const wallet = await databaseService.wallets.findById(request.params.id); + + if (!wallet) { + return Boom.notFound("Wallet not found"); + } + + const rows = await transactionsRepository.findAllByWallet(wallet, { + ...request.query, + ...request.params, + ...paginate(request), + }); + + return toPagination(request, rows, "transaction"); +}; + +const transactionsSent = async request => { + const wallet = await databaseService.wallets.findById(request.params.id); + + if (!wallet) { + return Boom.notFound("Wallet not found"); + } + + // NOTE: We unset this value because it otherwise will produce a faulty SQL query + delete request.params.id; + + const rows = await transactionsRepository.findAllBySender(wallet.publicKey, { + ...request.query, + ...request.params, + ...paginate(request), + }); + + return toPagination(request, rows, "transaction"); +}; + +const transactionsReceived = async request => { + const wallet = await databaseService.wallets.findById(request.params.id); + + if (!wallet) { + return Boom.notFound("Wallet not found"); + } + + // NOTE: We unset this value because it otherwise will produce a faulty SQL query + delete request.params.id; + + const rows = await transactionsRepository.findAllByRecipient(wallet.address, { + ...request.query, + ...request.params, + ...paginate(request), + }); + + return toPagination(request, rows, "transaction"); +}; + +const votes = async request => { + const wallet = await databaseService.wallets.findById(request.params.id); + + if (!wallet) { + return Boom.notFound("Wallet not found"); + } + + // NOTE: We unset this value because it otherwise will produce a faulty SQL query + delete request.params.id; + + const rows = await transactionsRepository.allVotesBySender(wallet.publicKey, { + ...request.params, + ...paginate(request), + }); + + return toPagination(request, rows, "transaction"); +}; + +const search = async request => { + const wallets = await databaseService.wallets.search({ + ...request.payload, + ...request.query, + ...paginate(request), + }); + + return toPagination(request, wallets, "wallet"); +}; + +export function registerMethods(server) { + ServerCache.make(server) + .method("v2.wallets.index", index, 30, request => ({ + ...request.payload, + ...request.query, + ...paginate(request), + })) + .method("v2.wallets.top", top, 30, request => paginate(request)) + .method("v2.wallets.show", show, 30, request => ({ id: request.params.id })) + .method("v2.wallets.transactions", transactions, 30, request => ({ + ...{ id: request.params.id }, + ...request.query, + ...request.params, + ...paginate(request), + })) + .method("v2.wallets.transactionsSent", transactionsSent, 30, request => ({ + ...{ id: request.params.id }, + ...request.query, + ...request.params, + ...paginate(request), + })) + .method("v2.wallets.transactionsReceived", transactionsReceived, 30, request => ({ + ...{ id: request.params.id }, + ...request.query, + ...request.params, + ...paginate(request), + })) + .method("v2.wallets.votes", votes, 30, request => ({ + ...{ id: request.params.id }, + ...request.params, + ...paginate(request), + })) + .method("v2.wallets.search", search, 30, request => ({ + ...request.payload, + ...request.query, + ...paginate(request), + })); +} diff --git a/packages/core-api/src/versions/2/wallets/routes.ts b/packages/core-api/src/versions/2/wallets/routes.ts new file mode 100644 index 0000000000..3f265ca897 --- /dev/null +++ b/packages/core-api/src/versions/2/wallets/routes.ts @@ -0,0 +1,80 @@ +import Hapi from "hapi"; +import { WalletsController } from "./controller"; +import * as Schema from "./schema"; + +export function registerRoutes(server: Hapi.Server): void { + const controller = new WalletsController(); + server.bind(controller); + + server.route({ + method: "GET", + path: "/wallets", + handler: controller.index, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "GET", + path: "/wallets/top", + handler: controller.top, + options: { + validate: Schema.index, + }, + }); + + server.route({ + method: "GET", + path: "/wallets/{id}", + handler: controller.show, + options: { + validate: Schema.show, + }, + }); + + server.route({ + method: "GET", + path: "/wallets/{id}/transactions", + handler: controller.transactions, + options: { + validate: Schema.transactions, + }, + }); + + server.route({ + method: "GET", + path: "/wallets/{id}/transactions/sent", + handler: controller.transactionsSent, + options: { + validate: Schema.transactionsSent, + }, + }); + + server.route({ + method: "GET", + path: "/wallets/{id}/transactions/received", + handler: controller.transactionsReceived, + options: { + validate: Schema.transactionsReceived, + }, + }); + + server.route({ + method: "GET", + path: "/wallets/{id}/votes", + handler: controller.votes, + options: { + validate: Schema.votes, + }, + }); + + server.route({ + method: "POST", + path: "/wallets/search", + handler: controller.search, + options: { + validate: Schema.search, + }, + }); +} diff --git a/packages/core-api/src/versions/2/wallets/schema.ts b/packages/core-api/src/versions/2/wallets/schema.ts new file mode 100644 index 0000000000..7e9bd48747 --- /dev/null +++ b/packages/core-api/src/versions/2/wallets/schema.ts @@ -0,0 +1,215 @@ +import * as Joi from "joi"; +import { pagination } from "../shared/schemas/pagination"; + +export const index: object = { + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + address: Joi.string() + .alphanum() + .length(34), + publicKey: Joi.string() + .hex() + .length(66), + secondPublicKey: Joi.string() + .hex() + .length(66), + vote: Joi.string() + .hex() + .length(66), + username: Joi.string(), + balance: Joi.number().integer(), + voteBalance: Joi.number() + .integer() + .min(0), + producedBlocks: Joi.number() + .integer() + .min(0), + missedBlocks: Joi.number() + .integer() + .min(0), + }, + }, +}; + +export const show: object = { + params: { + id: Joi.string(), + }, +}; + +export const transactions: object = { + params: { + id: Joi.string(), + }, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(64), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + type: Joi.number() + .integer() + .min(0), + version: Joi.number() + .integer() + .positive(), + senderPublicKey: Joi.string() + .hex() + .length(66), + senderId: Joi.string() + .alphanum() + .length(34), + recipientId: Joi.string() + .alphanum() + .length(34), + ownerId: Joi.string() + .alphanum() + .length(34), + timestamp: Joi.number() + .integer() + .min(0), + amount: Joi.number() + .integer() + .min(0), + fee: Joi.number() + .integer() + .min(0), + vendorFieldHex: Joi.string().hex(), + }, + }, +}; + +export const transactionsSent: object = { + params: { + id: Joi.string(), + }, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(64), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + type: Joi.number() + .integer() + .min(0), + version: Joi.number() + .integer() + .positive(), + recipientId: Joi.string() + .alphanum() + .length(34), + ownerId: Joi.string() + .alphanum() + .length(34), + timestamp: Joi.number() + .integer() + .min(0), + amount: Joi.number() + .integer() + .min(0), + fee: Joi.number() + .integer() + .min(0), + vendorFieldHex: Joi.string().hex(), + }, + }, +}; + +export const transactionsReceived: object = { + params: { + id: Joi.string(), + }, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + id: Joi.string() + .hex() + .length(64), + blockId: Joi.string().regex(/^[0-9]+$/, "numbers"), + type: Joi.number() + .integer() + .min(0), + version: Joi.number() + .integer() + .positive(), + senderPublicKey: Joi.string() + .hex() + .length(66), + senderId: Joi.string() + .alphanum() + .length(34), + ownerId: Joi.string() + .alphanum() + .length(34), + timestamp: Joi.number() + .integer() + .min(0), + amount: Joi.number() + .integer() + .min(0), + fee: Joi.number() + .integer() + .min(0), + vendorFieldHex: Joi.string().hex(), + }, + }, +}; + +export const votes: object = { + params: { + id: Joi.string(), + }, + query: pagination, +}; + +const address: object = Joi.string() + .alphanum() + .length(34); + +export const search: object = { + query: pagination, + payload: { + orderBy: Joi.string(), + address, + addresses: Joi.array() + .unique() + .min(1) + .max(50) + .items(address), + publicKey: Joi.string() + .hex() + .length(66), + secondPublicKey: Joi.string() + .hex() + .length(66), + vote: Joi.string() + .hex() + .length(66), + username: Joi.string(), + producedBlocks: Joi.number() + .integer() + .min(0), + missedBlocks: Joi.number() + .integer() + .min(0), + balance: Joi.object().keys({ + from: Joi.number().integer(), + to: Joi.number().integer(), + }), + voteBalance: Joi.object().keys({ + from: Joi.number() + .integer() + .min(0), + to: Joi.number() + .integer() + .min(0), + }), + }, +}; diff --git a/packages/core-api/src/versions/2/wallets/transformer.ts b/packages/core-api/src/versions/2/wallets/transformer.ts new file mode 100644 index 0000000000..8cafbf9ed6 --- /dev/null +++ b/packages/core-api/src/versions/2/wallets/transformer.ts @@ -0,0 +1,13 @@ +import { bignumify } from "@arkecosystem/core-utils"; + +export function transformWallet(model) { + return { + address: model.address, + publicKey: model.publicKey, + username: model.username, + secondPublicKey: model.secondPublicKey, + balance: +bignumify(model.balance).toFixed(), + isDelegate: !!model.username, + vote: model.vote, + }; +} diff --git a/packages/core-api/tsconfig.json b/packages/core-api/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-api/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-blockchain/CHANGELOG.md b/packages/core-blockchain/CHANGELOG.md deleted file mode 100644 index b7adcfa807..0000000000 --- a/packages/core-blockchain/CHANGELOG.md +++ /dev/null @@ -1,40 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.1 - 2018-12-10 - -### Fixed - -- Reset last downloaded block when block is discarded - -## 0.2.0 - 2018-12-03 - -### Added - -- More graceful handling of shutdown -- State Storage to handle state machine data -- Peer banning after forks - -### Changed - -- Improved the logic of how blocks are being processed -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Properly stop blockchain if manually started -- Various state issues with the last downloaded blocks -- Various state issues with the wallet manager -- Properly handle forks while idle - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-blockchain/LICENSE b/packages/core-blockchain/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-blockchain/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-blockchain/README.md b/packages/core-blockchain/README.md index 3254f8dc58..7f9231f70f 100644 --- a/packages/core-blockchain/README.md +++ b/packages/core-blockchain/README.md @@ -14,10 +14,11 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [François-Xavier Thoorens](https://github.com/fix) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-blockchain/__tests__/__support__/setup.js b/packages/core-blockchain/__tests__/__support__/setup.js deleted file mode 100644 index b2ba0ce46e..0000000000 --- a/packages/core-blockchain/__tests__/__support__/setup.js +++ /dev/null @@ -1,17 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(60000) - -exports.setUp = async () => { - await appHelper.setUp({ - exit: '@arkecosystem/core-p2p', - exclude: ['@arkecosystem/core-blockchain'], - }) - - return app -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-blockchain/__tests__/__support__/setup.ts b/packages/core-blockchain/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..110ad1343a --- /dev/null +++ b/packages/core-blockchain/__tests__/__support__/setup.ts @@ -0,0 +1,33 @@ +import { app } from "@arkecosystem/core-container"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; + +jest.setTimeout(60000); + +export const setUpFull = async () => { + await setUpContainer({ + exit: "@arkecosystem/core-p2p", + exclude: ["@arkecosystem/core-blockchain"], + }); + + const { plugin } = require("../../src/plugin"); + await registerWithContainer(plugin, {}); + + return app; +}; + +export const tearDownFull = async () => { + await app.tearDown(); + + const { plugin } = require("../../src/plugin"); + await plugin.deregister(app, {}); +}; + +export const setUp = async () => + setUpContainer({ + exit: "@arkecosystem/core-p2p", + exclude: ["@arkecosystem/core-blockchain"], + }); + +export const tearDown = async () => { + await app.tearDown(); +}; diff --git a/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts b/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts new file mode 100644 index 0000000000..8036b9e425 --- /dev/null +++ b/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts @@ -0,0 +1,77 @@ +/* tslint:disable:max-line-length */ +import "@arkecosystem/core-test-utils"; +import { asValue } from "awilix"; +import { Blockchain } from "../src/blockchain"; +import { defaults } from "../src/defaults"; +import { setUp, tearDown } from "./__support__/setup"; + +let container; +let blockchain: Blockchain; + +describe("constructor - networkStart", () => { + let logger; + beforeAll(async () => { + container = await setUp(); + + logger = container.resolvePlugin("logger"); + }); + afterAll(async () => { + await tearDown(); + + jest.restoreAllMocks(); + }); + + it("should output log messages if launched in networkStart mode", async () => { + const loggerWarn = jest.spyOn(logger, "warn"); + const loggerInfo = jest.spyOn(logger, "info"); + + await __start(true); + + expect(loggerWarn).toHaveBeenCalledWith( + "Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong. :warning:", + ); + expect(loggerInfo).toHaveBeenCalledWith("Starting Ark Core for a new world, welcome aboard :rocket:"); + }); + + describe("dispatch", () => { + it("should be ok", () => { + const nextState = blockchain.dispatch("START"); + + expect(blockchain.state.blockchain).toEqual(nextState); + }); + + it("should log an error if no action is found", () => { + const stateMachine = require("../src/state-machine").stateMachine; + const loggerError = jest.spyOn(logger, "error"); + + jest.spyOn(stateMachine, "transition").mockReturnValueOnce({ + actions: ["yooo"], + }); + + blockchain.dispatch("STOP"); + expect(loggerError).toHaveBeenCalledWith("No action 'yooo' found :interrobang:"); + }); + }); +}); + +async function __start(networkStart) { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + process.env.CORE_ENV = "false"; + + const plugin = require("../src").plugin; + + blockchain = await plugin.register(container, { + networkStart, + ...defaults, + }); + + await container.register( + "blockchain", + asValue({ + name: "blockchain", + version: "0.1.0", + plugin: blockchain, + options: {}, + }), + ); +} diff --git a/packages/core-blockchain/__tests__/blockchain.test.js b/packages/core-blockchain/__tests__/blockchain.test.js deleted file mode 100644 index 44140f1ed7..0000000000 --- a/packages/core-blockchain/__tests__/blockchain.test.js +++ /dev/null @@ -1,587 +0,0 @@ -/* eslint no-use-before-define: "warn" */ -/* eslint max-len: "off" */ -/* eslint no-await-in-loop: "off" */ - -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') - -const axiosMock = new MockAdapter(axios) -const delay = require('delay') - -const { asValue } = require('awilix') -const { crypto, slots } = require('@arkecosystem/crypto') -const { Block, Wallet } = require('@arkecosystem/crypto').models - -let genesisBlock -let configManager -let container -let blockchain -let logger -let loggerDebugBackup -let peerMock - -const blocks1to100 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.2-100') -const blocks101to155 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.101-155') -const app = require('./__support__/setup') - -beforeAll(async () => { - container = await app.setUp() - - // Backup logger.debug function as we are going to mock it in the test suite - logger = container.resolvePlugin('logger') - loggerDebugBackup = logger.debug - - // Mock peer responses so that we can have blocks - __mockPeer() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - - configManager = container.resolvePlugin('config') - - // Workaround: Add genesis transactions to the exceptions list, because they have a fee of 0 - // and otherwise don't pass validation. - configManager.network.exceptions.transactions = genesisBlock.transactions.map( - tx => tx.id, - ) - - // Manually register the blockchain and start it - await __start() -}) - -afterAll(async () => { - axiosMock.reset() - - delete configManager.network.exceptions.transactions - - await __resetToHeight1() - - // Manually stop the blockchain - await blockchain.stop() - - await app.tearDown() -}) - -afterEach(async () => { - // Restore original logger.debug function - logger.debug = loggerDebugBackup - - await __resetBlocksInCurrentRound() -}) - -describe('Blockchain', () => { - it('should be an object', () => { - expect(blockchain).toBeObject() - }) - - describe('dispatch', () => { - it('should be a function', () => { - expect(blockchain.dispatch).toBeFunction() - }) - - it('should be ok', () => { - const nextState = blockchain.dispatch('START') - - expect(blockchain.state.blockchain).toEqual(nextState) - }) - }) - - describe('start', () => { - it('should be a function', () => { - expect(blockchain.start).toBeFunction() - }) - - it('should be ok', async () => { - process.env.ARK_SKIP_BLOCKCHAIN = false - - const started = await blockchain.start(true) - - expect(started).toBeTrue() - }) - }) - - describe('checkNetwork', () => { - it('should be a function', () => { - expect(blockchain.checkNetwork).toBeFunction() - }) - - it('should throw an exception', () => { - expect(() => blockchain.checkNetwork()).toThrow( - 'Method [checkNetwork] not implemented!', - ) - }) - }) - - describe.skip('updateNetworkStatus', () => { - it('should be a function', () => { - expect(blockchain.updateNetworkStatus).toBeFunction() - }) - }) - - describe('rebuild', () => { - it('should be a function', () => { - expect(blockchain.rebuild).toBeFunction() - }) - - it('should throw an exception', () => { - expect(() => blockchain.rebuild()).toThrow( - 'Method [rebuild] not implemented!', - ) - }) - }) - - describe('resetState', () => { - it('should be a function', () => { - expect(blockchain.resetState).toBeFunction() - }) - }) - - describe('postTransactions', () => { - it('should be a function', () => { - expect(blockchain.postTransactions).toBeFunction() - }) - - it('should be ok', async () => { - const transactionsWithoutType2 = genesisBlock.transactions.filter( - tx => tx.type !== 2, - ) - - blockchain.transactionPool.flush() - await blockchain.postTransactions(transactionsWithoutType2, false) - const transactions = blockchain.transactionPool.getTransactions(0, 200) - - expect(transactions.length).toBe(transactionsWithoutType2.length) - - expect(transactions).toEqual( - transactionsWithoutType2.map(transaction => transaction.serialized), - ) - - blockchain.transactionPool.flush() - }) - }) - - describe('queueBlock', () => { - it('should be a function', () => { - expect(blockchain.queueBlock).toBeFunction() - }) - - it('should be ok', async () => { - const block = new Block(blocks101to155[54]) - - await blockchain.queueBlock(blocks101to155[54]) - - expect(blockchain.state.lastDownloadedBlock).toEqual(block) - }) - }) - - describe('rollbackCurrentRound', () => { - it('should be a function', () => { - expect(blockchain.rollbackCurrentRound).toBeFunction() - }) - - it('should rollback', async () => { - await blockchain.rollbackCurrentRound() - expect(blockchain.getLastBlock().data.height).toBe(153) - }) - }) - - describe('removeBlocks', () => { - it('should be a function', () => { - expect(blockchain.removeBlocks).toBeFunction() - }) - - it('should remove blocks', async () => { - const lastBlockHeight = blockchain.getLastBlock().data.height - - await blockchain.removeBlocks(2) - expect(blockchain.getLastBlock().data.height).toBe(lastBlockHeight - 2) - }) - }) - - describe('rebuildBlock', () => { - it('should be a function', () => { - expect(blockchain.rebuildBlock).toBeFunction() - }) - - it('should rebuild with a known block', async () => { - const mockCallback = jest.fn(() => true) - const lastBlock = blockchain.getLastBlock() - - await blockchain.rebuildBlock(lastBlock, mockCallback) - await delay(2000) // wait a bit to give enough time for the callback to be called - - expect(mockCallback.mock.calls.length).toBe(1) - }) - - it('should rebuild with a new chained block', async () => { - const mockCallback = jest.fn(() => true) - const lastBlock = blockchain.getLastBlock() - - await blockchain.removeBlocks(1) // remove 1 block so that we can add it then as a chained block - - expect(blockchain.getLastBlock()).not.toEqual(lastBlock) - - await blockchain.rebuildBlock(lastBlock, mockCallback) - await delay(2000) // wait a bit to give enough time for the callback to be called - - expect(mockCallback.mock.calls.length).toBe(1) - expect(blockchain.getLastBlock()).toEqual(lastBlock) - }) - }) - - describe('processBlock', () => { - it('should be a function', () => { - expect(blockchain.processBlock).toBeFunction() - }) - - it('should process a new chained block', async () => { - const mockCallback = jest.fn(() => true) - const lastBlock = blockchain.getLastBlock() - - await blockchain.removeBlocks(1) // remove 1 block so that we can add it then as a chained block - - expect(blockchain.getLastBlock()).not.toEqual(lastBlock) - - await blockchain.processBlock(lastBlock, mockCallback) - await delay(2000) // wait a bit to give enough time for the callback to be called - - expect(mockCallback.mock.calls.length).toBe(1) - expect(blockchain.getLastBlock()).toEqual(lastBlock) - }) - - it('should process a valid block already known', async () => { - const mockCallback = jest.fn(() => true) - const lastBlock = blockchain.getLastBlock() - - await blockchain.processBlock(lastBlock, mockCallback) - await delay(2000) // wait a bit to give enough time for the callback to be called - - expect(mockCallback.mock.calls.length).toBe(1) - expect(blockchain.getLastBlock()).toEqual(lastBlock) - }) - }) - - describe('acceptChainedBlock', () => { - it('should be a function', () => { - expect(blockchain.acceptChainedBlock).toBeFunction() - }) - - it('should process a new chained block', async () => { - const lastBlock = blockchain.getLastBlock() - - await blockchain.removeBlocks(1) // remove 1 block so that we can add it then as a chained block - - expect(await blockchain.database.getLastBlock()).not.toEqual(lastBlock) - - await blockchain.acceptChainedBlock(lastBlock) - - expect(await blockchain.database.getLastBlock()).toEqual(lastBlock) - - // manually set lastBlock because acceptChainedBlock doesn't do it - blockchain.state.setLastBlock(lastBlock) - }) - }) - - describe('manageUnchainedBlock', () => { - it('should be a function', () => { - expect(blockchain.manageUnchainedBlock).toBeFunction() - }) - - it('should process a new unchained block', async () => { - const mockLoggerDebug = jest.fn(message => true) - logger.debug = mockLoggerDebug - - const lastBlock = blockchain.getLastBlock() - await blockchain.removeBlocks(2) // remove 2 blocks so that we can have _lastBlock_ as an unchained block - await blockchain.manageUnchainedBlock(lastBlock) - - expect(mockLoggerDebug).toHaveBeenCalled() - - const debugMessage = `Blockchain not ready to accept new block at height ${lastBlock.data.height.toLocaleString()}. Last block: ${( - lastBlock.data.height - 2 - ).toLocaleString()} :warning:` - expect(mockLoggerDebug).toHaveBeenLastCalledWith(debugMessage) - - expect(blockchain.getLastBlock().data.height).toBe( - lastBlock.data.height - 2, - ) - }) - }) - - describe('getUnconfirmedTransactions', () => { - it('should be a function', () => { - expect(blockchain.getUnconfirmedTransactions).toBeFunction() - }) - - it('should get unconfirmed transactions', async () => { - const transactionsWithoutType2 = genesisBlock.transactions.filter( - tx => tx.type !== 2, - ) - - blockchain.transactionPool.flush() - await blockchain.postTransactions(transactionsWithoutType2, false) - const unconfirmedTransactions = blockchain.getUnconfirmedTransactions(200) - - expect(unconfirmedTransactions.transactions.length).toBe( - transactionsWithoutType2.length, - ) - - expect(unconfirmedTransactions.transactions).toEqual( - transactionsWithoutType2.map(transaction => transaction.serialized), - ) - - blockchain.transactionPool.flush() - }) - }) - - describe('getLastBlock', () => { - it('should be a function', () => { - expect(blockchain.getLastBlock).toBeFunction() - }) - - it('should be ok', () => { - blockchain.state.setLastBlock(genesisBlock) - - expect(blockchain.getLastBlock()).toEqual(genesisBlock) - }) - }) - - describe('isSynced', () => { - it('should be a function', () => { - expect(blockchain.isSynced).toBeFunction() - }) - - describe('with a block param', () => { - it('should be ok', () => { - expect( - blockchain.isSynced({ - data: { - timestamp: slots.getTime(), - height: genesisBlock.height, - }, - }), - ).toBeTrue() - }) - }) - - describe('without a block param', () => { - it('should use the last block', () => { - blockchain.getLastBlock = jest.fn().mockReturnValueOnce({ - data: { - timestamp: slots.getTime(), - height: genesisBlock.height, - }, - }) - expect(blockchain.isSynced()).toBeTrue() - expect(blockchain.getLastBlock).toHaveBeenCalled() - }) - }) - }) - - describe('isRebuildSynced', () => { - it('should be a function', () => { - expect(blockchain.isRebuildSynced).toBeFunction() - }) - - describe('with a block param', () => { - it('should be ok', () => { - expect( - blockchain.isRebuildSynced({ - data: { - timestamp: slots.getTime() - 3600 * 24 * 6, - height: blocks101to155[52].height, - }, - }), - ).toBeTrue() - }) - }) - - describe('without a block param', () => { - it('should use the last block', () => { - blockchain.getLastBlock = jest.fn().mockReturnValueOnce({ - data: { - timestamp: slots.getTime(), - height: genesisBlock.height, - }, - }) - expect(blockchain.isRebuildSynced()).toBeTrue() - expect(blockchain.getLastBlock).toHaveBeenCalled() - }) - }) - }) - - describe('__isChained', () => { - it('should be a function', () => { - expect(blockchain.__isChained).toBeFunction() - }) - - it('should be ok', () => { - const previousBlock = { - data: { - id: 1, - timestamp: 1, - height: 1, - }, - } - - const nextBlock = { - data: { - id: 2, - timestamp: 2, - height: 2, - previousBlock: 1, - }, - } - - expect(blockchain.__isChained(previousBlock, nextBlock)).toBeTrue() - }) - - it('should not be ok', () => { - const previousBlock = { - data: { - id: 2, - timestamp: 2, - height: 2, - }, - } - - const nextBlock = { - data: { - id: 1, - timestamp: 1, - height: 1, - previousBlock: 1, - }, - } - - expect(blockchain.__isChained(previousBlock, nextBlock)).toBeFalse() - }) - }) - - describe('__registerQueue', () => { - it('should be a function', () => { - expect(blockchain.__registerQueue).toBeFunction() - }) - - it('should be ok', () => { - blockchain.__registerQueue() - - expect(blockchain).toHaveProperty('queue') - expect(blockchain).toHaveProperty('processQueue') - expect(blockchain).toHaveProperty('rebuildQueue') - }) - }) -}) - -async function __start() { - process.env.ARK_SKIP_BLOCKCHAIN = false - process.env.ARK_ENV = false - - const plugin = require('../lib').plugin - - blockchain = await plugin.register(container, { - networkStart: false, - }) - - await container.register( - 'blockchain', - asValue({ - name: 'blockchain', - version: '0.1.0', - plugin: blockchain, - options: {}, - }), - ) - - const p2p = container.resolvePlugin('p2p') - await p2p.acceptNewPeer(peerMock) - - await __resetToHeight1() - - await blockchain.start(true) - while ( - !blockchain.getLastBlock() || - blockchain.getLastBlock().data.height < 155 - ) { - await delay(1000) - } -} - -async function __resetBlocksInCurrentRound() { - blockchain.database.blocksInCurrentRound = await blockchain.database.__getBlocksForRound() -} - -async function __resetToHeight1() { - const lastBlock = await blockchain.database.getLastBlock() - if (lastBlock) { - // Make sure the wallet manager has been fed or else revertRound - // cannot determine the previous delegates. This is only necessary, because - // the database is not dropped after the unit tests are done. - await blockchain.database.buildWallets(lastBlock.data.height) - - // Index the genesis wallet or else revert block at height 1 fails - const generator = crypto.getAddress(genesisBlock.data.generatorPublicKey) - const genesis = new Wallet(generator) - genesis.publicKey = genesisBlock.data.generatorPublicKey - genesis.username = 'genesis' - blockchain.database.walletManager.reindex(genesis) - - blockchain.state.clear() - - blockchain.state.setLastBlock(lastBlock) - await __resetBlocksInCurrentRound(lastBlock) - await blockchain.removeBlocks(lastBlock.data.height - 1) - } -} - -function __mockPeer() { - // Mocking a peer which will send blocks until height 155 - const Peer = require('@arkecosystem/core-p2p/lib/peer') - peerMock = new Peer('0.0.0.99', 4002) - Object.assign(peerMock, peerMock.headers, { status: 200 }) - - axiosMock - .onGet(/.*\/peer\/blocks\/common.*/) - .reply(() => [ - 200, - { status: 200, success: true, common: true }, - peerMock.headers, - ]) - axiosMock.onGet(/.*\/peer\/blocks/).reply(config => { - let blocks = [] - - if (config.params.lastBlockHeight === 1) { - blocks = blocks1to100 - } else if (config.params.lastBlockHeight === 100) { - blocks = blocks101to155 - } - - return [200, { status: 200, success: true, blocks }, peerMock.headers] - }) - axiosMock - .onGet(/.*\/peer\/status/) - .reply(() => [ - 200, - { status: 200, success: true, height: 155 }, - peerMock.headers, - ]) - axiosMock.onGet(/.*\/peer\/list/).reply(() => [ - 200, - { - success: true, - peers: [ - { - status: 200, - ip: peerMock.ip, - port: 4002, - height: 155, - delay: 8, - }, - ], - }, - peerMock.headers, - ]) -} diff --git a/packages/core-blockchain/__tests__/blockchain.test.ts b/packages/core-blockchain/__tests__/blockchain.test.ts new file mode 100644 index 0000000000..2e68b5481c --- /dev/null +++ b/packages/core-blockchain/__tests__/blockchain.test.ts @@ -0,0 +1,685 @@ +/* tslint:disable:max-line-length */ +import "@arkecosystem/core-test-utils"; +import { blocks101to155 } from "@arkecosystem/core-test-utils/src/fixtures/testnet/blocks101to155"; +import { blocks2to100 } from "@arkecosystem/core-test-utils/src/fixtures/testnet/blocks2to100"; +import { crypto, models, slots } from "@arkecosystem/crypto"; +import { asValue } from "awilix"; +import delay from "delay"; +import { Blockchain } from "../src/blockchain"; +import { defaults } from "../src/defaults"; +import { setUp, tearDown } from "./__support__/setup"; + +const { Block, Wallet } = models; + +let genesisBlock; +let configManager; +let container; +let blockchain: Blockchain; +let loggerDebugBackup; + +describe("Blockchain", () => { + let logger; + beforeAll(async () => { + container = await setUp(); + + // Backup logger.debug function as we are going to mock it in the test suite + logger = container.resolvePlugin("logger"); + loggerDebugBackup = logger.debug; + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(require("@arkecosystem/core-test-utils/src/config/testnet/genesisBlock.json")); + + configManager = container.getConfig(); + + // Workaround: Add genesis transactions to the exceptions list, because they have a fee of 0 + // and otherwise don't pass validation. + configManager.set("exceptions.transactions", genesisBlock.transactions.map(tx => tx.id)); + + // Manually register the blockchain and start it + await __start(false); + }); + + afterAll(async () => { + configManager.set("exceptions.transactions", []); + + await __resetToHeight1(); + + // Manually stop the blockchain + await blockchain.stop(); + + await tearDown(); + }); + + afterEach(async () => { + // Restore original logger.debug function + logger.debug = loggerDebugBackup; + + await __resetToHeight1(); + await __addBlocks(5); + await __resetBlocksInCurrentRound(); + }); + + describe("dispatch", () => { + it("should be ok", () => { + const nextState = blockchain.dispatch("START"); + + expect(blockchain.state.blockchain).toEqual(nextState); + }); + }); + + describe("start", () => { + it("should be ok", async () => { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + + const started = await blockchain.start(true); + + expect(started).toBeTrue(); + }); + }); + + describe("checkNetwork", () => { + it("should throw an exception", () => { + expect(() => blockchain.checkNetwork()).toThrow("Method [checkNetwork] not implemented!"); + }); + }); + + describe("updateNetworkStatus", () => { + it("should call p2p updateNetworkStatus", async () => { + const p2pUpdateNetworkStatus = jest.spyOn(blockchain.p2p, "updateNetworkStatus"); + + await blockchain.updateNetworkStatus(); + + expect(p2pUpdateNetworkStatus).toHaveBeenCalled(); + }); + }); + + describe("rebuild", () => { + it("should throw an exception", () => { + expect(() => blockchain.rebuild()).toThrow("Method [rebuild] not implemented!"); + }); + }); + + describe("postTransactions", () => { + it("should be ok", async () => { + const transactionsWithoutType2 = genesisBlock.transactions.filter(tx => tx.type !== 2); + + blockchain.transactionPool.flush(); + await blockchain.postTransactions(transactionsWithoutType2); + const transactions = blockchain.transactionPool.getTransactions(0, 200); + + expect(transactions.length).toBe(transactionsWithoutType2.length); + + expect(transactions).toEqual(transactionsWithoutType2.map(transaction => transaction.serialized)); + + blockchain.transactionPool.flush(); + }); + }); + + describe("enQueueBlocks", () => { + it("should just return if blocks provided are an empty array", async () => { + const processQueuePush = jest.spyOn(blockchain.processQueue, "push"); + + blockchain.enqueueBlocks([]); + expect(processQueuePush).not.toHaveBeenCalled(); + }); + + it("should enqueue the blocks provided", async () => { + const processQueuePush = jest.spyOn(blockchain.processQueue, "push"); + + const blocksToEnqueue = [blocks101to155[54]]; + blockchain.enqueueBlocks(blocksToEnqueue); + expect(processQueuePush).toHaveBeenCalledWith(blocksToEnqueue); + }); + }); + + describe("rollbackCurrentRound", () => { + it("should rollback", async () => { + await __addBlocks(155); + await blockchain.rollbackCurrentRound(); + expect(blockchain.getLastBlock().data.height).toBe(153); + }); + + it("shouldnt rollback more if previous round is round 2", async () => { + await __addBlocks(140); + await blockchain.rollbackCurrentRound(); + expect(blockchain.getLastBlock().data.height).toBe(102); + + await blockchain.rollbackCurrentRound(); + expect(blockchain.getLastBlock().data.height).toBe(102); + }); + }); + + describe("removeBlocks", () => { + it("should remove blocks", async () => { + const lastBlockHeight = blockchain.getLastBlock().data.height; + + await blockchain.removeBlocks(2); + expect(blockchain.getLastBlock().data.height).toBe(lastBlockHeight - 2); + }); + + it("should remove (current height - 1) blocks if we provide a greater value", async () => { + await __resetToHeight1(); + + await blockchain.removeBlocks(9999); + expect(blockchain.getLastBlock().data.height).toBe(1); + }); + }); + + describe("removeTopBlocks", () => { + it("should remove top blocks", async () => { + const dbLastBlockBefore = await blockchain.database.getLastBlock(); + const lastBlockHeight = dbLastBlockBefore.data.height; + + await blockchain.removeTopBlocks(2); + const dbLastBlockAfter = await blockchain.database.getLastBlock(); + + expect(dbLastBlockAfter.data.height).toBe(lastBlockHeight - 2); + }); + }); + + describe("rebuildBlock", () => { + it("should rebuild with a known block", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + + await blockchain.rebuildBlock(lastBlock, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + }); + + it("should rebuild with a new chained block", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + + await blockchain.removeBlocks(1); // remove 1 block so that we can add it then as a chained block + + expect(blockchain.getLastBlock()).not.toEqual(lastBlock); + + await blockchain.rebuildBlock(lastBlock, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(blockchain.getLastBlock()).toEqual(lastBlock); + }); + + it("should disregard block with height == last height but different id", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + const lastBlockCopy = new Block(lastBlock.data); + lastBlockCopy.data.id = "123456"; + + const loggerInfo = jest.spyOn(logger, "info"); + + await blockchain.rebuildBlock(lastBlockCopy, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(loggerInfo).toHaveBeenCalledWith( + `Block ${lastBlockCopy.data.height.toLocaleString()} disregarded because on a fork :knife_fork_plate:`, + ); + expect(blockchain.getLastBlock().data.id).toBe(lastBlock.data.id); + }); + + it("should disregard block with height > last height + 1", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + const lastBlockCopy = new Block(lastBlock.data); + lastBlockCopy.data.height += 2; + + await blockchain.rebuildBlock(lastBlockCopy, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(blockchain.getLastBlock().data.id).toBe(lastBlock.data.id); + expect(blockchain.state.lastDownloadedBlock).toBe(lastBlock); + }); + + it("should disregard block not verified", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + const lastBlockCopy = new Block(lastBlock.data); + lastBlockCopy.verification.verified = false; + + const loggerWarn = jest.spyOn(logger, "warn"); + + await blockchain.rebuildBlock(lastBlockCopy, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(loggerWarn).toHaveBeenCalledWith( + `Block ${lastBlockCopy.data.height.toLocaleString()} disregarded because verification failed :scroll:`, + ); + expect(blockchain.getLastBlock().data.id).toBe(lastBlock.data.id); + }); + + it("should commitQueuedQueries if block height % 20 000 == 0", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + const lastBlockHeight = lastBlock.data.height; + const nextBlock = new Block(blocks2to100[lastBlock.data.height - 1]); + lastBlock.data.height = 19999; + nextBlock.data.height = 20000; + + const commitQueuedQueries = jest + .spyOn(blockchain.database, "commitQueuedQueries") + .mockReturnValueOnce(true); + jest.spyOn(blockchain.database, "enqueueSaveBlock").mockReturnValueOnce(true); + + await blockchain.rebuildBlock(nextBlock, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(commitQueuedQueries).toHaveBeenCalled(); + expect(blockchain.getLastBlock().data.id).toBe(nextBlock.data.id); + + // reset to "stable" state + lastBlock.data.height = lastBlockHeight; + blockchain.state.setLastBlock(lastBlock); + }); + }); + + describe("processBlock", () => { + it("should process a new chained block", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.state.getLastBlock(); + + await blockchain.removeBlocks(1); // remove 1 block so that we can add it then as a chained block + + expect(blockchain.getLastBlock()).not.toEqual(lastBlock); + + await blockchain.processBlock(lastBlock, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(blockchain.getLastBlock()).toEqual(lastBlock); + }); + + it("should process a valid block already known", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + + await blockchain.processBlock(lastBlock, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(blockchain.getLastBlock()).toEqual(lastBlock); + }); + + it("should broadcast a block if (slots.getSlotNumber() * blocktime <= block.data.timestamp)", async () => { + const mockCallback = jest.fn(() => true); + const lastBlock = blockchain.getLastBlock(); + lastBlock.data.timestamp = + slots.getSlotNumber() * configManager.getMilestone(lastBlock.data.height).blocktime; + + const broadcastBlock = jest.spyOn(blockchain.p2p, "broadcastBlock"); + + await blockchain.processBlock(lastBlock, mockCallback); + await delay(200); + + expect(mockCallback.mock.calls.length).toBe(1); + expect(broadcastBlock).toHaveBeenCalled(); + }); + }); + + describe("acceptChainedBlock", () => { + it.skip("should process a new chained block", async () => { + const lastBlock = blockchain.getLastBlock(); + + await blockchain.removeBlocks(1); // remove 1 block so that we can add it then as a chained block + + expect(await blockchain.database.getLastBlock()).not.toEqual(lastBlock); + + // await blockchain.acceptChainedBlock(lastBlock); + + expect(await blockchain.database.getLastBlock()).toEqual(lastBlock); + + // manually set lastBlock because acceptChainedBlock doesn't do it + blockchain.state.setLastBlock(lastBlock); + }); + }); + + describe("manageUnchainedBlock", () => { + it.skip("should process a new unchained block", async () => { + const mockLoggerDebug = jest.fn(message => true); + logger.debug = mockLoggerDebug; + + const lastBlock = blockchain.getLastBlock(); + await blockchain.removeBlocks(2); // remove 2 blocks so that we can have _lastBlock_ as an unchained block + // await blockchain.manageUnchainedBlock(lastBlock); + + expect(mockLoggerDebug).toHaveBeenCalled(); + + const debugMessage = `Blockchain not ready to accept new block at height ${lastBlock.data.height.toLocaleString()}. Last block: ${( + lastBlock.data.height - 2 + ).toLocaleString()} :warning:`; + expect(mockLoggerDebug).toHaveBeenCalledWith(debugMessage); + + expect(blockchain.getLastBlock().data.height).toBe(lastBlock.data.height - 2); + }); + }); + + describe("getUnconfirmedTransactions", () => { + it("should get unconfirmed transactions", async () => { + const transactionsWithoutType2 = genesisBlock.transactions.filter(tx => tx.type !== 2); + + blockchain.transactionPool.flush(); + await blockchain.postTransactions(transactionsWithoutType2); + const unconfirmedTransactions = blockchain.getUnconfirmedTransactions(200); + + expect(unconfirmedTransactions.transactions.length).toBe(transactionsWithoutType2.length); + + expect(unconfirmedTransactions.transactions).toEqual( + transactionsWithoutType2.map(transaction => transaction.serialized), + ); + + blockchain.transactionPool.flush(); + }); + + it("should return object with count == -1 if getTransactionsForForging returned a falsy value", async () => { + jest.spyOn(blockchain.transactionPool, "getTransactionsForForging").mockReturnValueOnce(null); + + const unconfirmedTransactions = blockchain.getUnconfirmedTransactions(200); + expect(unconfirmedTransactions.count).toBe(-1); + }); + }); + + describe("getLastBlock", () => { + it("should be ok", () => { + blockchain.state.setLastBlock(genesisBlock); + + expect(blockchain.getLastBlock()).toEqual(genesisBlock); + }); + }); + + describe("handleIncomingBlock", () => { + it("should be ok", () => { + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: slots.getEpochTime(), + }; + + blockchain.handleIncomingBlock(block); + + expect(blockchain.dispatch).toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }); + + it("should not handle block from future slot", () => { + const dispatch = blockchain.dispatch; + const enqueueBlocks = blockchain.enqueueBlocks; + blockchain.dispatch = jest.fn(() => true); + blockchain.enqueueBlocks = jest.fn(() => true); + + const block = { + height: 100, + timestamp: slots.getSlotTime(slots.getNextSlot()), + }; + + blockchain.handleIncomingBlock(block); + + expect(blockchain.dispatch).not.toHaveBeenCalled(); + expect(blockchain.enqueueBlocks).not.toHaveBeenCalled(); + + blockchain.dispatch = dispatch; + blockchain.enqueueBlocks = enqueueBlocks; + }); + + it("should disregard block when blockchain is not ready", async () => { + blockchain.state.started = false; + const loggerInfo = jest.spyOn(logger, "info"); + + const mockGetSlotNumber = jest + .spyOn(slots, "getSlotNumber") + .mockReturnValueOnce(1) + .mockReturnValueOnce(1); + + await blockchain.handleIncomingBlock(blocks101to155[54]); + + expect(loggerInfo).toHaveBeenCalledWith("Block disregarded because blockchain is not ready :exclamation:"); + blockchain.state.started = true; + + mockGetSlotNumber.mockRestore(); + }); + }); + + describe("forceWakeup", () => { + it("should dispatch WAKEUP", () => { + expect(() => blockchain.forceWakeup()).toDispatch(blockchain, "WAKEUP"); + }); + }); + + describe("forkBlock", () => { + it("should dispatch FORK and set state.forkedBlock", () => { + const forkedBlock = new Block(blocks2to100[11]); + expect(() => blockchain.forkBlock(forkedBlock)).toDispatch(blockchain, "FORK"); + expect(blockchain.state.forkedBlock).toBe(forkedBlock); + + blockchain.state.forkedBlock = null; // reset + }); + }); + + describe("isSynced", () => { + describe("with a block param", () => { + it("should be ok", () => { + expect( + blockchain.isSynced({ + data: { + timestamp: slots.getTime(), + height: genesisBlock.height, + }, + } as models.IBlock), + ).toBeTrue(); + }); + }); + + describe("without a block param", () => { + it("should use the last block", () => { + jest.spyOn(blockchain.p2p, "hasPeers").mockReturnValueOnce(true); + const getLastBlock = jest.spyOn(blockchain, "getLastBlock").mockReturnValueOnce({ + data: { + timestamp: slots.getTime(), + height: genesisBlock.height, + }, + }); + expect(blockchain.isSynced()).toBeTrue(); + expect(getLastBlock).toHaveBeenCalled(); + }); + }); + }); + + describe("isRebuildSynced", () => { + describe("with a block param", () => { + it("should be ok", () => { + jest.spyOn(blockchain.p2p, "hasPeers").mockReturnValueOnce(true); + expect( + blockchain.isRebuildSynced({ + data: { + timestamp: slots.getTime() - 3600 * 24 * 6, + height: blocks101to155[52].height, + }, + } as models.IBlock), + ).toBeTrue(); + }); + }); + + describe("without a block param", () => { + it("should use the last block", () => { + jest.spyOn(blockchain.p2p, "hasPeers").mockReturnValueOnce(true); + const getLastBlock = jest.spyOn(blockchain, "getLastBlock").mockReturnValueOnce({ + data: { + timestamp: slots.getTime(), + height: genesisBlock.height, + }, + }); + expect(blockchain.isRebuildSynced()).toBeTrue(); + expect(getLastBlock).toHaveBeenCalled(); + }); + }); + + it("should return true when there is no peer", () => { + jest.spyOn(blockchain.p2p, "hasPeers").mockReturnValueOnce(false); + + expect(blockchain.isRebuildSynced()).toBeTrue(); + }); + }); + + describe("getBlockPing", () => { + it("should return state.blockPing", () => { + const blockPing = { + count: 1, + first: new Date().getTime(), + last: new Date().getTime(), + block: {}, + }; + blockchain.state.blockPing = blockPing; + + expect(blockchain.getBlockPing()).toBe(blockPing); + }); + }); + + describe("pingBlock", () => { + it("should call state.pingBlock", () => { + blockchain.state.blockPing = null; + + // returns false if no state.blockPing + expect(blockchain.pingBlock(blocks2to100[3])).toBeFalse(); + }); + }); + + describe("pushPingBlock", () => { + it("should call state.pushPingBlock", () => { + blockchain.state.blockPing = null; + + blockchain.pushPingBlock(blocks2to100[3]); + expect(blockchain.state.blockPing).toBeObject(); + expect(blockchain.state.blockPing.block).toBe(blocks2to100[3]); + }); + }); + + describe("getEvents", () => { + it("should return the events", () => { + expect(blockchain.getEvents()).toEqual([ + "block.applied", + "block.forged", + "block.reverted", + "delegate.registered", + "delegate.resigned", + "forger.failed", + "forger.missing", + "forger.started", + "peer.added", + "peer.removed", + "round.created", + "state:started", + "transaction.applied", + "transaction.expired", + "transaction.forged", + "transaction.reverted", + "wallet.saved", + "wallet.created.cold", + ]); + }); + }); + + describe("__registerQueue", () => { + it("should be ok", () => { + blockchain.__registerQueue(); + + expect(blockchain).toHaveProperty("queue"); + expect(blockchain).toHaveProperty("processQueue"); + expect(blockchain).toHaveProperty("rebuildQueue"); + }); + }); + + describe("stop on emit shutdown", () => { + it("should trigger the stop method when receiving 'shutdown' event", async () => { + const emitter = container.resolvePlugin("event-emitter"); + + const stop = jest.spyOn(blockchain, "stop").mockReturnValue(true); + + emitter.emit("shutdown"); + + await delay(200); + + expect(stop).toHaveBeenCalled(); + }); + }); +}); + +async function __start(networkStart) { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + process.env.CORE_ENV = "false"; + + const plugin = require("../src").plugin; + + blockchain = await plugin.register(container, { + networkStart, + ...defaults, + }); + + await container.register( + "blockchain", + asValue({ + name: "blockchain", + version: "0.1.0", + plugin: blockchain, + options: {}, + }), + ); + + if (networkStart) { + return; + } + + await __resetToHeight1(); + + await blockchain.start(); + await __addBlocks(5); +} + +async function __resetBlocksInCurrentRound() { + await blockchain.database.loadBlocksFromCurrentRound(); +} + +async function __resetToHeight1() { + const lastBlock = await blockchain.database.getLastBlock(); + if (lastBlock) { + // Make sure the wallet manager has been fed or else revertRound + // cannot determine the previous delegates. This is only necessary, because + // the database is not dropped after the unit tests are done. + await blockchain.database.buildWallets(lastBlock.data.height); + + // Index the genesis wallet or else revert block at height 1 fails + const generator = crypto.getAddress(genesisBlock.data.generatorPublicKey); + const genesis = new Wallet(generator); + genesis.publicKey = genesisBlock.data.generatorPublicKey; + genesis.username = "genesis"; + blockchain.database.walletManager.reindex(genesis); + + blockchain.state.clear(); + + blockchain.state.setLastBlock(lastBlock); + await __resetBlocksInCurrentRound(); + await blockchain.removeBlocks(lastBlock.data.height - 1); + } +} + +async function __addBlocks(untilHeight) { + const allBlocks = [...blocks2to100, ...blocks101to155]; + const lastHeight = blockchain.getLastHeight(); + + for (let height = lastHeight + 1; height < untilHeight && height < 155; height++) { + const blockToProcess = new Block(allBlocks[height - 2]); + await blockchain.processBlock(blockToProcess, () => null); + } +} diff --git a/packages/core-blockchain/__tests__/machines/actions/fork.test.js b/packages/core-blockchain/__tests__/machines/actions/fork.test.js deleted file mode 100644 index 324ec1e2ff..0000000000 --- a/packages/core-blockchain/__tests__/machines/actions/fork.test.js +++ /dev/null @@ -1,52 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') // eslint-disable-line no-unused-vars - -const machine = require('../../../lib/machines/blockchain') - -describe('Blockchain machine > Fork', () => { - it('should start with the `analysing` state', () => { - expect(machine.states.fork).toHaveProperty('initial', 'analysing') - }) - - describe('state `analysing`', () => { - it('should execute the `analyseFork` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'fork.analysing', - actions: ['analyseFork'], - }) - }) - - it('should transition to `revertBlocks` on `REBUILD`', () => { - expect(machine).toTransition({ - from: 'fork.analysing', - on: 'REBUILD', - to: 'fork.revertBlocks', - }) - }) - - it('should transition to `exit` on `NOFORK`', () => { - expect(machine).toTransition({ - from: 'fork.analysing', - on: 'NOFORK', - to: 'fork.exit', - }) - }) - }) - - describe('state `network`', () => { - it('should execute the `checkNetwork` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'fork.network', - actions: ['checkNetwork'], - }) - }) - }) - - describe('state `exit`', () => { - it('should execute the `forkRecovered` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'fork.exit', - actions: ['forkRecovered'], - }) - }) - }) -}) diff --git a/packages/core-blockchain/__tests__/machines/actions/fork.test.ts b/packages/core-blockchain/__tests__/machines/actions/fork.test.ts new file mode 100644 index 0000000000..f9566de037 --- /dev/null +++ b/packages/core-blockchain/__tests__/machines/actions/fork.test.ts @@ -0,0 +1,52 @@ +import "@arkecosystem/core-test-utils/"; + +import { blockchainMachine } from "../../../src/machines/blockchain"; + +describe("Blockchain machine > Fork", () => { + it("should start with the `analysing` state", () => { + expect(blockchainMachine.states.fork).toHaveProperty("initial", "analysing"); + }); + + describe("state `analysing`", () => { + it("should execute the `analyseFork` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "fork.analysing", + actions: ["analyseFork"], + }); + }); + + it("should transition to `revertBlocks` on `REBUILD`", () => { + expect(blockchainMachine).toTransition({ + from: "fork.analysing", + on: "REBUILD", + to: "fork.revertBlocks", + }); + }); + + it("should transition to `exit` on `NOFORK`", () => { + expect(blockchainMachine).toTransition({ + from: "fork.analysing", + on: "NOFORK", + to: "fork.exit", + }); + }); + }); + + describe("state `network`", () => { + it("should execute the `checkNetwork` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "fork.network", + actions: ["checkNetwork"], + }); + }); + }); + + describe("state `exit`", () => { + it("should execute the `forkRecovered` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "fork.exit", + actions: ["forkRecovered"], + }); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/machines/actions/rebuild-from-network.test.js b/packages/core-blockchain/__tests__/machines/actions/rebuild-from-network.test.js deleted file mode 100644 index ceb699213c..0000000000 --- a/packages/core-blockchain/__tests__/machines/actions/rebuild-from-network.test.js +++ /dev/null @@ -1,155 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') // eslint-disable-line no-unused-vars - -const machine = require('../../../lib/machines/blockchain') - -describe('Blockchain machine > Rebuilding', () => { - it('should start with the `rebuilding` state', () => { - expect(machine.states.rebuild).toHaveProperty('initial', 'rebuilding') - }) - - describe('state `rebuilding`', () => { - it('should execute the `checkLastDownloadedBlockSynced` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'rebuild.rebuilding', - actions: ['checkLastDownloadedBlockSynced'], - }) - }) - - it('should transition to `waitingFinished` on `SYNCED`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuilding', - on: 'SYNCED', - to: 'rebuild.waitingFinished', - }) - }) - - it('should transition to `revertBlocks` on `NOTSYNCED`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuilding', - on: 'NOTSYNCED', - to: 'rebuild.rebuildBlocks', - }) - }) - - it('should transition to `rebuildPaused` on `PAUSED`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuilding', - on: 'PAUSED', - to: 'rebuild.rebuildPaused', - }) - }) - }) - - describe('state `idle`', () => { - it('should transition to `rebuildBlocks` on `DOWNLOADED`', () => { - expect(machine).toTransition({ - from: 'rebuild.idle', - on: 'DOWNLOADED', - to: 'rebuild.rebuildBlocks', - }) - }) - }) - - describe('state `rebuildBlocks`', () => { - it('should execute the `rebuildBlocks` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'rebuild.rebuildBlocks', - actions: ['rebuildBlocks'], - }) - }) - - it('should transition to `rebuilding` on `DOWNLOADED`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuildBlocks', - on: 'DOWNLOADED', - to: 'rebuild.rebuilding', - }) - }) - - it('should transition to `rebuilding` on `NOBLOCK`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuildBlocks', - on: 'NOBLOCK', - to: 'rebuild.rebuilding', - }) - }) - }) - - describe('state `waitingFinished`', () => { - it('should transition to `rebuildFinished` on `REBUILDFINISHED`', () => { - expect(machine).toTransition({ - from: 'rebuild.waitingFinished', - on: 'REBUILDFINISHED', - to: 'rebuild.rebuildFinished', - }) - }) - }) - - describe('state `processFinished`', () => { - it('should execute the `checkRebuildBlockSynced` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'rebuild.processFinished', - actions: ['checkRebuildBlockSynced'], - }) - }) - - it('should transition to `processFinished` on `SYNCED`', () => { - expect(machine).toTransition({ - from: 'rebuild.processFinished', - on: 'SYNCED', - to: 'rebuild.end', - }) - }) - - it('should transition to `processFinished` on `NOTSYNCED`', () => { - expect(machine).toTransition({ - from: 'rebuild.processFinished', - on: 'NOTSYNCED', - to: 'rebuild.rebuildBlocks', - }) - }) - }) - - describe('state `rebuildPaused`', () => { - it('should execute the `downloadPaused` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'rebuild.rebuildPaused', - actions: ['downloadPaused'], - }) - }) - - it('should transition to `processFinished` on `REBUILDFINISHED`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuildPaused', - on: 'REBUILDFINISHED', - to: 'rebuild.processFinished', - }) - }) - }) - - describe('state `rebuildFinished`', () => { - it('should execute the `rebuildFinished` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'rebuild.rebuildFinished', - actions: ['rebuildFinished'], - }) - }) - - it('should transition to `processFinished` on `PROCESSFINISHED`', () => { - expect(machine).toTransition({ - from: 'rebuild.rebuildFinished', - on: 'PROCESSFINISHED', - to: 'rebuild.processFinished', - }) - }) - }) - - describe('state `end`', () => { - it('should execute the `rebuildingComplete` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'rebuild.end', - actions: ['rebuildingComplete'], - }) - }) - }) -}) diff --git a/packages/core-blockchain/__tests__/machines/actions/rebuild-from-network.test.ts b/packages/core-blockchain/__tests__/machines/actions/rebuild-from-network.test.ts new file mode 100644 index 0000000000..1821a7389b --- /dev/null +++ b/packages/core-blockchain/__tests__/machines/actions/rebuild-from-network.test.ts @@ -0,0 +1,155 @@ +import "@arkecosystem/core-test-utils/"; + +import { blockchainMachine as machine } from "../../../src/machines/blockchain"; + +describe("Blockchain machine > Rebuilding", () => { + it("should start with the `rebuilding` state", () => { + expect(machine.states.rebuild).toHaveProperty("initial", "rebuilding"); + }); + + describe("state `rebuilding`", () => { + it("should execute the `checkLastDownloadedBlockSynced` action when is entered", () => { + expect(machine).toExecuteOnEntry({ + state: "rebuild.rebuilding", + actions: ["checkLastDownloadedBlockSynced"], + }); + }); + + it("should transition to `waitingFinished` on `SYNCED`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuilding", + on: "SYNCED", + to: "rebuild.waitingFinished", + }); + }); + + it("should transition to `revertBlocks` on `NOTSYNCED`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuilding", + on: "NOTSYNCED", + to: "rebuild.rebuildBlocks", + }); + }); + + it("should transition to `rebuildPaused` on `PAUSED`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuilding", + on: "PAUSED", + to: "rebuild.rebuildPaused", + }); + }); + }); + + describe("state `idle`", () => { + it("should transition to `rebuildBlocks` on `DOWNLOADED`", () => { + expect(machine).toTransition({ + from: "rebuild.idle", + on: "DOWNLOADED", + to: "rebuild.rebuildBlocks", + }); + }); + }); + + describe("state `rebuildBlocks`", () => { + it("should execute the `rebuildBlocks` action when is entered", () => { + expect(machine).toExecuteOnEntry({ + state: "rebuild.rebuildBlocks", + actions: ["rebuildBlocks"], + }); + }); + + it("should transition to `rebuilding` on `DOWNLOADED`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuildBlocks", + on: "DOWNLOADED", + to: "rebuild.rebuilding", + }); + }); + + it("should transition to `rebuilding` on `NOBLOCK`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuildBlocks", + on: "NOBLOCK", + to: "rebuild.rebuilding", + }); + }); + }); + + describe("state `waitingFinished`", () => { + it("should transition to `rebuildFinished` on `REBUILDFINISHED`", () => { + expect(machine).toTransition({ + from: "rebuild.waitingFinished", + on: "REBUILDFINISHED", + to: "rebuild.rebuildFinished", + }); + }); + }); + + describe("state `processFinished`", () => { + it("should execute the `checkRebuildBlockSynced` action when is entered", () => { + expect(machine).toExecuteOnEntry({ + state: "rebuild.processFinished", + actions: ["checkRebuildBlockSynced"], + }); + }); + + it("should transition to `processFinished` on `SYNCED`", () => { + expect(machine).toTransition({ + from: "rebuild.processFinished", + on: "SYNCED", + to: "rebuild.end", + }); + }); + + it("should transition to `processFinished` on `NOTSYNCED`", () => { + expect(machine).toTransition({ + from: "rebuild.processFinished", + on: "NOTSYNCED", + to: "rebuild.rebuildBlocks", + }); + }); + }); + + describe("state `rebuildPaused`", () => { + it("should execute the `downloadPaused` action when is entered", () => { + expect(machine).toExecuteOnEntry({ + state: "rebuild.rebuildPaused", + actions: ["downloadPaused"], + }); + }); + + it("should transition to `processFinished` on `REBUILDFINISHED`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuildPaused", + on: "REBUILDFINISHED", + to: "rebuild.processFinished", + }); + }); + }); + + describe("state `rebuildFinished`", () => { + it("should execute the `rebuildFinished` action when is entered", () => { + expect(machine).toExecuteOnEntry({ + state: "rebuild.rebuildFinished", + actions: ["rebuildFinished"], + }); + }); + + it("should transition to `processFinished` on `PROCESSFINISHED`", () => { + expect(machine).toTransition({ + from: "rebuild.rebuildFinished", + on: "PROCESSFINISHED", + to: "rebuild.processFinished", + }); + }); + }); + + describe("state `end`", () => { + it("should execute the `rebuildingComplete` action when is entered", () => { + expect(machine).toExecuteOnEntry({ + state: "rebuild.end", + actions: ["rebuildingComplete"], + }); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/machines/actions/sync-with-network.test.js b/packages/core-blockchain/__tests__/machines/actions/sync-with-network.test.js deleted file mode 100644 index e46a59a922..0000000000 --- a/packages/core-blockchain/__tests__/machines/actions/sync-with-network.test.js +++ /dev/null @@ -1,153 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') // eslint-disable-line no-unused-vars - -const machine = require('../../../lib/machines/blockchain') - -describe('Blockchain machine > SyncWithNetwork', () => { - it('should start with the `syncing` state', () => { - expect(machine.states.syncWithNetwork).toHaveProperty('initial', 'syncing') - }) - - describe('state `syncing`', () => { - it('should execute the `checkLastDownloadedBlockSynced` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'syncWithNetwork.syncing', - actions: ['checkLastDownloadedBlockSynced'], - }) - }) - - it('should transition to `downloadFinished` on `SYNCED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.syncing', - on: 'SYNCED', - to: 'syncWithNetwork.downloadFinished', - }) - }) - - it('should transition to `downloadBlocks` on `NOTSYNCED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.syncing', - on: 'NOTSYNCED', - to: 'syncWithNetwork.downloadBlocks', - }) - }) - - it('should transition to `downloadPaused` on `PAUSED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.syncing', - on: 'PAUSED', - to: 'syncWithNetwork.downloadPaused', - }) - }) - - it('should transition to `end` on `NETWORKHALTED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.syncing', - on: 'NETWORKHALTED', - to: 'syncWithNetwork.end', - }) - }) - }) - - describe('state `idle`', () => { - it('should transition to `downloadBlocks` on `DOWNLOADED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.idle', - on: 'DOWNLOADED', - to: 'syncWithNetwork.downloadBlocks', - }) - }) - }) - - describe('state `downloadBlocks`', () => { - it('should execute the `downloadBlocks` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'syncWithNetwork.downloadBlocks', - actions: ['downloadBlocks'], - }) - }) - - it('should transition to `syncing` on `DOWNLOADED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.downloadBlocks', - on: 'DOWNLOADED', - to: 'syncWithNetwork.syncing', - }) - }) - - it('should transition to `syncing` on `NOBLOCK`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.downloadBlocks', - on: 'NOBLOCK', - to: 'syncWithNetwork.syncing', - }) - }) - }) - - describe('state `downloadFinished`', () => { - it('should execute the `downloadFinished` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'syncWithNetwork.downloadFinished', - actions: ['downloadFinished'], - }) - }) - - it('should transition to `processFinished` on `PROCESSFINISHED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.downloadFinished', - on: 'PROCESSFINISHED', - to: 'syncWithNetwork.processFinished', - }) - }) - }) - - describe('state `downloadPaused`', () => { - it('should execute the `downloadPaused` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'syncWithNetwork.downloadPaused', - actions: ['downloadPaused'], - }) - }) - - it('should transition to `processFinished` on `PROCESSFINISHED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.downloadPaused', - on: 'PROCESSFINISHED', - to: 'syncWithNetwork.processFinished', - }) - }) - }) - - describe('state `processFinished`', () => { - it('should execute the `checkLastBlockSynced` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'syncWithNetwork.processFinished', - actions: ['checkLastBlockSynced'], - }) - }) - - it('should transition to `end` on `SYNCED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.processFinished', - on: 'SYNCED', - to: 'syncWithNetwork.end', - }) - }) - - it('should transition to `downloadBlocks` on `NOTSYNCED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork.processFinished', - on: 'NOTSYNCED', - to: 'syncWithNetwork.downloadBlocks', - }) - }) - }) - - describe('state `end`', () => { - it('should execute the `syncingComplete` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'syncWithNetwork.end', - actions: ['syncingComplete'], - }) - }) - }) -}) diff --git a/packages/core-blockchain/__tests__/machines/actions/sync-with-network.test.ts b/packages/core-blockchain/__tests__/machines/actions/sync-with-network.test.ts new file mode 100644 index 0000000000..ca7d86b7d1 --- /dev/null +++ b/packages/core-blockchain/__tests__/machines/actions/sync-with-network.test.ts @@ -0,0 +1,153 @@ +import "@arkecosystem/core-test-utils/"; + +import { blockchainMachine } from "../../../src/machines/blockchain"; + +describe("Blockchain machine > SyncWithNetwork", () => { + it("should start with the `syncing` state", () => { + expect(blockchainMachine.states.syncWithNetwork).toHaveProperty("initial", "syncing"); + }); + + describe("state `syncing`", () => { + it("should execute the `checkLastDownloadedBlockSynced` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "syncWithNetwork.syncing", + actions: ["checkLastDownloadedBlockSynced"], + }); + }); + + it("should transition to `downloadFinished` on `SYNCED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.syncing", + on: "SYNCED", + to: "syncWithNetwork.downloadFinished", + }); + }); + + it("should transition to `downloadBlocks` on `NOTSYNCED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.syncing", + on: "NOTSYNCED", + to: "syncWithNetwork.downloadBlocks", + }); + }); + + it("should transition to `downloadPaused` on `PAUSED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.syncing", + on: "PAUSED", + to: "syncWithNetwork.downloadPaused", + }); + }); + + it("should transition to `end` on `NETWORKHALTED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.syncing", + on: "NETWORKHALTED", + to: "syncWithNetwork.end", + }); + }); + }); + + describe("state `idle`", () => { + it("should transition to `downloadBlocks` on `DOWNLOADED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.idle", + on: "DOWNLOADED", + to: "syncWithNetwork.downloadBlocks", + }); + }); + }); + + describe("state `downloadBlocks`", () => { + it("should execute the `downloadBlocks` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "syncWithNetwork.downloadBlocks", + actions: ["downloadBlocks"], + }); + }); + + it("should transition to `syncing` on `DOWNLOADED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.downloadBlocks", + on: "DOWNLOADED", + to: "syncWithNetwork.syncing", + }); + }); + + it("should transition to `syncing` on `NOBLOCK`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.downloadBlocks", + on: "NOBLOCK", + to: "syncWithNetwork.syncing", + }); + }); + }); + + describe("state `downloadFinished`", () => { + it("should execute the `downloadFinished` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "syncWithNetwork.downloadFinished", + actions: ["downloadFinished"], + }); + }); + + it("should transition to `processFinished` on `PROCESSFINISHED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.downloadFinished", + on: "PROCESSFINISHED", + to: "syncWithNetwork.processFinished", + }); + }); + }); + + describe("state `downloadPaused`", () => { + it("should execute the `downloadPaused` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "syncWithNetwork.downloadPaused", + actions: ["downloadPaused"], + }); + }); + + it("should transition to `processFinished` on `PROCESSFINISHED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.downloadPaused", + on: "PROCESSFINISHED", + to: "syncWithNetwork.processFinished", + }); + }); + }); + + describe("state `processFinished`", () => { + it("should execute the `checkLastBlockSynced` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "syncWithNetwork.processFinished", + actions: ["checkLastBlockSynced"], + }); + }); + + it("should transition to `end` on `SYNCED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.processFinished", + on: "SYNCED", + to: "syncWithNetwork.end", + }); + }); + + it("should transition to `downloadBlocks` on `NOTSYNCED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork.processFinished", + on: "NOTSYNCED", + to: "syncWithNetwork.downloadBlocks", + }); + }); + }); + + describe("state `end`", () => { + it("should execute the `syncingComplete` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "syncWithNetwork.end", + actions: ["syncingComplete"], + }); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/machines/blockchain.test.js b/packages/core-blockchain/__tests__/machines/blockchain.test.js deleted file mode 100644 index 5e850caaff..0000000000 --- a/packages/core-blockchain/__tests__/machines/blockchain.test.js +++ /dev/null @@ -1,188 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') // eslint-disable-line no-unused-vars - -const machine = require('../../lib/machines/blockchain') - -describe('Blockchain machine', () => { - it('should use `blockchain` as the key', () => { - expect(machine).toHaveProperty('key', 'blockchain') - }) - - it('should start with the `uninitialised` state', () => { - expect(machine.initialState).toHaveProperty('value', 'uninitialised') - }) - - describe('state `uninitialised`', () => { - it('should transition to `init` on `START`', () => { - expect(machine).toTransition({ - from: 'uninitialised', - on: 'START', - to: 'init', - }) - }) - }) - - describe('state `init`', () => { - it('should execute the `init` action when is entered', () => { - expect(machine).toExecuteOnEntry({ state: 'init', actions: ['init'] }) - }) - - it('should transition to `rebuild` on `REBUILD`', () => { - expect(machine).toTransition({ - from: 'init', - on: 'REBUILD', - to: 'rebuild', - }) - }) - - it('should transition to `rebuild` on `NETWORKSTART`', () => { - expect(machine).toTransition({ - from: 'init', - on: 'NETWORKSTART', - to: 'idle', - }) - }) - - it('should transition to `rebuild` on `STARTED`', () => { - expect(machine).toTransition({ - from: 'init', - on: 'STARTED', - to: 'syncWithNetwork', - }) - }) - - it('should transition to `rebuild` on `FAILURE`', () => { - expect(machine).toTransition({ from: 'init', on: 'FAILURE', to: 'exit' }) - }) - }) - - describe('state `rebuild`', () => { - it('should transition to `syncWithNetwork` on `REBUILDCOMPLETE`', () => { - expect(machine).toTransition({ - from: 'rebuild', - on: 'REBUILDCOMPLETE', - to: 'syncWithNetwork', - }) - }) - - it('should transition to `fork` on `FORK`', () => { - expect(machine).toTransition({ from: 'rebuild', on: 'FORK', to: 'fork' }) - }) - }) - - describe('state `syncWithNetwork`', () => { - it('should transition to `idle` on `TEST`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork', - on: 'TEST', - to: 'idle', - }) - }) - - it('should transition to `idle` on `SYNCFINISHED`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork', - on: 'SYNCFINISHED', - to: 'idle', - }) - }) - - it('should transition to `fork` on `FORK`', () => { - expect(machine).toTransition({ - from: 'syncWithNetwork', - on: 'FORK', - to: 'fork', - }) - }) - }) - - describe('state `idle`', () => { - it('should execute the `checkLater` and `blockchainReady` actions when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'idle', - actions: ['checkLater', 'blockchainReady'], - }) - }) - - it('should transition to `syncWithNetwork` on `WAKEUP`', () => { - expect(machine).toTransition({ - from: 'idle', - on: 'WAKEUP', - to: 'syncWithNetwork', - }) - }) - - it('should transition to `newBlock` on `NEWBLOCK`', () => { - expect(machine).toTransition({ - from: 'idle', - on: 'NEWBLOCK', - to: 'newBlock', - }) - }) - - it('should transition to `stopped` on `STOP`', () => { - expect(machine).toTransition({ from: 'idle', on: 'STOP', to: 'stopped' }) - }) - }) - - describe('state `newBlock`', () => { - it('should transition to `idle` on `PROCESSFINISHED`', () => { - expect(machine).toTransition({ - from: 'newBlock', - on: 'PROCESSFINISHED', - to: 'idle', - }) - }) - - it('should transition to `fork` on `FORK`', () => { - expect(machine).toTransition({ - from: 'newBlock', - on: 'FORK', - to: 'fork', - }) - }) - - it('should transition to `stopped` on `STOP`', () => { - expect(machine).toTransition({ - from: 'newBlock', - on: 'STOP', - to: 'stopped', - }) - }) - }) - - describe('state `fork`', () => { - it('should execute the `processBlock` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'fork', - actions: ['startForkRecovery'], - }) - }) - - it('should transition to `idle` on `SUCCESS`', () => { - expect(machine).toTransition({ - from: 'fork', - on: 'SUCCESS', - to: 'syncWithNetwork', - }) - }) - - it('should transition to `fork` on `FAILURE`', () => { - expect(machine).toTransition({ from: 'fork', on: 'FAILURE', to: 'exit' }) - }) - }) - - describe('state `stopped`', () => { - it('should execute the `stopped` action when is entered', () => { - expect(machine).toExecuteOnEntry({ - state: 'stopped', - actions: ['stopped'], - }) - }) - }) - - describe('state `exit`', () => { - it('should execute the `exitApp` action when is entered', () => { - expect(machine).toExecuteOnEntry({ state: 'exit', actions: ['exitApp'] }) - }) - }) -}) diff --git a/packages/core-blockchain/__tests__/machines/blockchain.test.ts b/packages/core-blockchain/__tests__/machines/blockchain.test.ts new file mode 100644 index 0000000000..e7adfef260 --- /dev/null +++ b/packages/core-blockchain/__tests__/machines/blockchain.test.ts @@ -0,0 +1,188 @@ +import "@arkecosystem/core-test-utils/"; + +import { blockchainMachine } from "../../src/machines/blockchain"; + +describe("Blockchain machine", () => { + it("should use `blockchain` as the key", () => { + expect(blockchainMachine).toHaveProperty("key", "blockchain"); + }); + + it("should start with the `uninitialised` state", () => { + expect(blockchainMachine.initialState).toHaveProperty("value", "uninitialised"); + }); + + describe("state `uninitialised`", () => { + it("should transition to `init` on `START`", () => { + expect(blockchainMachine).toTransition({ + from: "uninitialised", + on: "START", + to: "init", + }); + }); + }); + + describe("state `init`", () => { + it("should execute the `init` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ state: "init", actions: ["init"] }); + }); + + it("should transition to `rebuild` on `REBUILD`", () => { + expect(blockchainMachine).toTransition({ + from: "init", + on: "REBUILD", + to: "rebuild", + }); + }); + + it("should transition to `rebuild` on `NETWORKSTART`", () => { + expect(blockchainMachine).toTransition({ + from: "init", + on: "NETWORKSTART", + to: "idle", + }); + }); + + it("should transition to `rebuild` on `STARTED`", () => { + expect(blockchainMachine).toTransition({ + from: "init", + on: "STARTED", + to: "syncWithNetwork", + }); + }); + + it("should transition to `rebuild` on `FAILURE`", () => { + expect(blockchainMachine).toTransition({ from: "init", on: "FAILURE", to: "exit" }); + }); + }); + + describe("state `rebuild`", () => { + it("should transition to `syncWithNetwork` on `REBUILDCOMPLETE`", () => { + expect(blockchainMachine).toTransition({ + from: "rebuild", + on: "REBUILDCOMPLETE", + to: "syncWithNetwork", + }); + }); + + it("should transition to `fork` on `FORK`", () => { + expect(blockchainMachine).toTransition({ from: "rebuild", on: "FORK", to: "fork" }); + }); + }); + + describe("state `syncWithNetwork`", () => { + it("should transition to `idle` on `TEST`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork", + on: "TEST", + to: "idle", + }); + }); + + it("should transition to `idle` on `SYNCFINISHED`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork", + on: "SYNCFINISHED", + to: "idle", + }); + }); + + it("should transition to `fork` on `FORK`", () => { + expect(blockchainMachine).toTransition({ + from: "syncWithNetwork", + on: "FORK", + to: "fork", + }); + }); + }); + + describe("state `idle`", () => { + it("should execute the `checkLater` and `blockchainReady` actions when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "idle", + actions: ["checkLater", "blockchainReady"], + }); + }); + + it("should transition to `syncWithNetwork` on `WAKEUP`", () => { + expect(blockchainMachine).toTransition({ + from: "idle", + on: "WAKEUP", + to: "syncWithNetwork", + }); + }); + + it("should transition to `newBlock` on `NEWBLOCK`", () => { + expect(blockchainMachine).toTransition({ + from: "idle", + on: "NEWBLOCK", + to: "newBlock", + }); + }); + + it("should transition to `stopped` on `STOP`", () => { + expect(blockchainMachine).toTransition({ from: "idle", on: "STOP", to: "stopped" }); + }); + }); + + describe("state `newBlock`", () => { + it("should transition to `idle` on `PROCESSFINISHED`", () => { + expect(blockchainMachine).toTransition({ + from: "newBlock", + on: "PROCESSFINISHED", + to: "idle", + }); + }); + + it("should transition to `fork` on `FORK`", () => { + expect(blockchainMachine).toTransition({ + from: "newBlock", + on: "FORK", + to: "fork", + }); + }); + + it("should transition to `stopped` on `STOP`", () => { + expect(blockchainMachine).toTransition({ + from: "newBlock", + on: "STOP", + to: "stopped", + }); + }); + }); + + describe("state `fork`", () => { + it("should execute the `processBlock` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "fork", + actions: ["startForkRecovery"], + }); + }); + + it("should transition to `idle` on `SUCCESS`", () => { + expect(blockchainMachine).toTransition({ + from: "fork", + on: "SUCCESS", + to: "syncWithNetwork", + }); + }); + + it("should transition to `fork` on `FAILURE`", () => { + expect(blockchainMachine).toTransition({ from: "fork", on: "FAILURE", to: "exit" }); + }); + }); + + describe("state `stopped`", () => { + it("should execute the `stopped` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ + state: "stopped", + actions: ["stopped"], + }); + }); + }); + + describe("state `exit`", () => { + it("should execute the `exitApp` action when is entered", () => { + expect(blockchainMachine).toExecuteOnEntry({ state: "exit", actions: ["exitApp"] }); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/processor/block-processor.test.ts b/packages/core-blockchain/__tests__/processor/block-processor.test.ts new file mode 100644 index 0000000000..f5249322c9 --- /dev/null +++ b/packages/core-blockchain/__tests__/processor/block-processor.test.ts @@ -0,0 +1,293 @@ +import "@arkecosystem/core-test-utils"; +import { fixtures, generators } from "@arkecosystem/core-test-utils"; +import genesisBlockTestnet from "@arkecosystem/core-test-utils/src/config/testnet/genesisBlock.json"; +import { models } from "@arkecosystem/crypto"; +import { Blockchain } from "../../src/blockchain"; +import { BlockProcessor, BlockProcessorResult } from "../../src/processor"; +import * as handlers from "../../src/processor/handlers"; +import { ExceptionHandler, VerificationFailedHandler } from "../../src/processor/handlers"; +import { setUpFull, tearDownFull } from "../__support__/setup"; + +const { Block } = models; +const { delegates } = fixtures; +const { generateTransfers } = generators; + +let app; +let blockchain: Blockchain; +let blockProcessor: BlockProcessor; + +beforeAll(async () => { + app = await setUpFull(); + blockchain = app.resolvePlugin("blockchain"); + blockProcessor = new BlockProcessor(blockchain); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +const resetBlocks = async () => blockchain.removeBlocks(blockchain.getLastHeight() - 1); // reset to block height 1 + +beforeEach(resetBlocks); +afterEach(resetBlocks); + +describe("Block processor", () => { + const blockTemplate = { + id: "17882607875259085966", + version: 0, + timestamp: 46583330, + height: 2, + reward: 0, + previousBlock: genesisBlockTestnet.id, + numberOfTransactions: 1, + transactions: [], + totalAmount: 0, + totalFee: 0, + payloadLength: 0, + payloadHash: genesisBlockTestnet.payloadHash, + generatorPublicKey: delegates[0].publicKey, + blockSignature: + "3045022100e7385c6ea42bd950f7f6ab8c8619cf2f66a41d8f8f185b0bc99af032cb25f30d02200b6210176a6cedfdcbe483167fd91c21d740e0e4011d24d679c601fdd46b0de9", + createdAt: "2019-07-11T16:48:50.550Z", + }; + + describe("getHandler", () => { + it("should return ExceptionHandler if block is an exception", async () => { + const exceptionBlock = new Block(blockTemplate); + exceptionBlock.data.id = "998877"; + + const configManager = app.getConfig(); + + configManager.set("exceptions.blocks", ["998877"]); + + expect(await blockProcessor.getHandler(exceptionBlock)).toBeInstanceOf(ExceptionHandler); + }); + + it("should return VerificationFailedHandler if block failed verification", async () => { + const failedVerifBlock = new Block(blockTemplate); + failedVerifBlock.verification.verified = false; + + expect(await blockProcessor.getHandler(failedVerifBlock)).toBeInstanceOf(VerificationFailedHandler); + }); + }); + + describe("process", () => { + const getBlock = transactions => + Object.assign({}, blockTemplate, { + transactions, + totalAmount: transactions.reduce((acc, curr) => acc + curr.amount, 0), + totalFee: transactions.reduce((acc, curr) => acc + curr.fee, 0), + numberOfTransactions: transactions.length, + }); + const processBlock = async transactions => { + const block = getBlock(transactions); + const blockVerified = new Block(block); + blockVerified.verification.verified = true; + + await blockchain.processBlock(blockVerified, () => null); + + return Object.assign(block, { id: blockVerified.data.id }); + }; + + describe("should not accept replay transactions", () => { + it("should not validate an already forged transaction", async () => { + const transfers = generateTransfers( + "unitnet", + delegates[0].passphrase, + delegates[1].address, + 11, + 1, + true, + ); + const block = await processBlock(transfers); + block.height = 3; + block.previousBlock = block.id; + block.id = "17882607875259085967"; + block.timestamp += 1000; + + const blockVerified = new Block(block); + blockVerified.verification.verified = true; + + const handler = await blockProcessor.getHandler(blockVerified); + expect(handler instanceof handlers.AlreadyForgedHandler).toBeTrue(); + + const result = await blockProcessor.process(blockVerified); + expect(result).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted); + }); + + it("should not validate an already forged transaction - trying to tweak the tx id", async () => { + const transfers = generateTransfers( + "unitnet", + delegates[0].passphrase, + delegates[1].address, + 11, + 1, + true, + ); + const block = await processBlock(transfers); + block.height = 3; + block.previousBlock = block.id; + block.id = "17882607875259085967"; + block.timestamp += 1000; + block.transactions[0].id = "123456"; // change the tx id to try to make it accept as a new transaction + + const blockVerified = new Block(block); + blockVerified.verification.verified = true; + + const handler = await blockProcessor.getHandler(blockVerified); + expect(handler instanceof handlers.AlreadyForgedHandler).toBeTrue(); + + const result = await blockProcessor.process(blockVerified); + expect(result).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted); + }); + }); + + describe("lastDownloadedBlock", () => { + it.each([ + "AlreadyForgedHandler", + "InvalidGeneratorHandler", + "UnchainedHandler", + "VerificationFailedHandler", + ])( + "should not increment lastDownloadedBlock or lastBlock when processing block fails with %s", + async handler => { + const lastBlock = blockchain.getLastBlock(); + const lastDownloadedBlock = blockchain.getLastDownloadedBlock(); + const blockToProcess = new Block(blockTemplate); + + const getHanderBackup = blockProcessor.getHandler; // save for restoring afterwards + blockProcessor.getHandler = jest.fn(() => new handlers[handler](blockchain, blockToProcess)); + + await blockProcessor.process(blockToProcess); + + expect(blockchain.getLastBlock()).toEqual(lastBlock); + expect(blockchain.getLastDownloadedBlock()).toEqual(lastDownloadedBlock); + + blockProcessor.getHandler = getHanderBackup; // restore original function + }, + ); + }); + + describe("Forging delegates", () => { + it("should use InvalidGeneratorHandler if forging delegate is invalid", async () => { + const database = app.resolvePlugin("database"); + const getActiveDelegatesBackup = database.getActiveDelegates; // save for restoring afterwards + database.getActiveDelegates = jest.fn(() => [delegates[50]]); + + const blockVerified = new Block(getBlock([])); + blockVerified.verification.verified = true; + + const handler = await blockProcessor.getHandler(blockVerified); + expect(handler instanceof handlers.InvalidGeneratorHandler).toBeTrue(); + + const result = await blockProcessor.process(blockVerified); + expect(result).toBe(BlockProcessorResult.Rejected); + + database.getActiveDelegates = getActiveDelegatesBackup; // restore the original function + }); + }); + + describe("Unchained blocks", () => { + it("should 'discard but broadcast' when same block comes again", async () => { + /* We process a valid block then try processing the same block again. + Should detect as "double-forging" and reject the duplicate block. */ + const blockVerified = new Block(getBlock([])); + blockVerified.verification.verified = true; + + // accept a valid first block + const accepted = await blockProcessor.process(blockVerified); + expect(accepted).toBe(BlockProcessorResult.Accepted); + + // get handler on same block, should be handled by UnchainedHandler + const handler = await blockProcessor.getHandler(blockVerified); + expect(handler instanceof handlers.UnchainedHandler).toBeTrue(); + + // if we try to process the block, it should be discarded but broadcasted + const rejected = await blockProcessor.process(blockVerified); + expect(rejected).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted); + }); + + it("should reject a double-forging block", async () => { + /* We process a valid block then try processing the same block again. + Should detect as "double-forging" and reject the duplicate block. */ + const blockVerified = new Block(getBlock([])); + blockVerified.verification.verified = true; + + // accept a valid first block + const accepted = await blockProcessor.process(blockVerified); + expect(accepted).toBe(BlockProcessorResult.Accepted); + + // new block for double-forging : same height different id + const blockDoubleForging = new Block(getBlock([])); + blockDoubleForging.verification.verified = true; + blockDoubleForging.data.id = "123456"; + + // get handler on the "new" block, should be handled by UnchainedHandler + const handler = await blockProcessor.getHandler(blockDoubleForging); + expect(handler instanceof handlers.UnchainedHandler).toBeTrue(); + + // if we try to process the block, it should be rejected + const rejected = await blockProcessor.process(blockDoubleForging); + expect(rejected).toBe(BlockProcessorResult.Rejected); + }); + + it("should reject a block with invalid timestamp", async () => { + const database = app.resolvePlugin("database"); + const getActiveDelegatesBackup = database.getActiveDelegates; + database.getActiveDelegates = jest.fn(() => [delegates[0]]); + + const forkBlockBackup = blockchain.forkBlock; + blockchain.forkBlock = jest.fn(); + + const block = new Block(getBlock([])); + block.verification.verified = true; + block.data.timestamp = 46582922; + + blockchain.getLastBlock().data.timestamp = 46583330; + + const rejected = await blockProcessor.process(block); + expect(blockchain.forkBlock).not.toHaveBeenCalled(); + expect(rejected).toBe(BlockProcessorResult.Rejected); + + blockchain.getLastBlock().data.timestamp = 0; + blockchain.forkBlock = forkBlockBackup; + database.getActiveDelegates = getActiveDelegatesBackup; + }); + + it("should 'discard but broadcast' a block higher than current height + 1", async () => { + const blockVerified = new Block(getBlock([])); + blockVerified.verification.verified = true; + blockVerified.data.height = 3; + + const handler = await blockProcessor.getHandler(blockVerified); + expect(handler instanceof handlers.UnchainedHandler).toBeTrue(); + + const result = await blockProcessor.process(blockVerified); + expect(result).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted); + }); + + it("should 'discard but broadcast' a block lower than current height", async () => { + const blockVerified = new Block(getBlock([])); + blockVerified.verification.verified = true; + + // accept a valid first block + const accepted = await blockProcessor.process(blockVerified); + expect(accepted).toBe(BlockProcessorResult.Accepted); + + // new block with height < current + const blockLowerHeight = new Block(getBlock([])); + blockLowerHeight.verification.verified = true; + blockLowerHeight.data.id = "123456"; + blockLowerHeight.data.height = 1; + + // get handler on the "new" block, should be handled by UnchainedHandler + const handler = await blockProcessor.getHandler(blockLowerHeight); + expect(handler instanceof handlers.UnchainedHandler).toBeTrue(); + + // if we try to process the block, it should be 'discarded but can be broadcasted' + const result = await blockProcessor.process(blockLowerHeight); + expect(result).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted); + }); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts b/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts new file mode 100644 index 0000000000..710be16e12 --- /dev/null +++ b/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts @@ -0,0 +1,68 @@ +import "@arkecosystem/core-test-utils"; +import { AcceptBlockHandler } from "../../../src/processor/handlers"; + +import { models } from "@arkecosystem/crypto"; +import { blocks2to100 } from "../../../../core-test-utils/src/fixtures/testnet/blocks2to100"; +import { Blockchain } from "../../../src/blockchain"; +import { BlockProcessorResult } from "../../../src/processor"; +import { setUpFull, tearDownFull } from "../../__support__/setup"; + +const { Block } = models; +let app; +let blockchain: Blockchain; +let logger; + +beforeAll(async () => { + app = await setUpFull(); + blockchain = app.resolvePlugin("blockchain"); + logger = app.resolvePlugin("logger"); + + // mock apply / saveBlock - we dont want to actually do anything to the db + jest.spyOn(blockchain.database, "applyBlock").mockReturnValue(true); + jest.spyOn(blockchain.database, "saveBlock").mockReturnValue(true); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +describe("Accept handler", () => { + describe("execute", () => { + it("should log message if we recovered from fork and update state.forkedBlock", async () => { + const handler = new AcceptBlockHandler(blockchain, new Block(blocks2to100[0])); + + const loggerInfo = jest.spyOn(logger, "info"); + blockchain.state.forkedBlock = new Block(blocks2to100[0]); + + expect(await handler.execute()).toBe(BlockProcessorResult.Accepted); + expect(loggerInfo).toHaveBeenCalledWith("Successfully recovered from fork :star2:"); + expect(blockchain.state.forkedBlock).toBe(null); + }); + + it("should log warning message if transactionPool accepChainedBlock threw an exception", async () => { + const handler = new AcceptBlockHandler(blockchain, new Block(blocks2to100[0])); + + const loggerWarn = jest.spyOn(logger, "warn"); + jest.spyOn(blockchain.transactionPool, "acceptChainedBlock").mockImplementationOnce(() => { + throw new Error("¯_(ツ)_/¯"); + }); + + expect(await handler.execute()).toBe(BlockProcessorResult.Accepted); + expect(loggerWarn).toHaveBeenCalledWith("Issue applying block to transaction pool"); + }); + + it("should log error message if an exception was thrown", async () => { + const block = new Block(blocks2to100[0]); + const handler = new AcceptBlockHandler(blockchain, block); + + jest.restoreAllMocks(); + const loggerError = jest.spyOn(logger, "error"); + jest.spyOn(blockchain.database, "applyBlock").mockImplementationOnce(() => { + throw new Error("¯_(ツ)_/¯"); + }); + + expect(await handler.execute()).toBe(BlockProcessorResult.Rejected); + expect(loggerError).toHaveBeenCalledWith(`Refused new block ${JSON.stringify(block.data)}`); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/processor/handlers/exception-handler.test.ts b/packages/core-blockchain/__tests__/processor/handlers/exception-handler.test.ts new file mode 100644 index 0000000000..034083c5bb --- /dev/null +++ b/packages/core-blockchain/__tests__/processor/handlers/exception-handler.test.ts @@ -0,0 +1,39 @@ +import "@arkecosystem/core-test-utils"; +import { ExceptionHandler } from "../../../src/processor/handlers"; + +import { models } from "@arkecosystem/crypto"; +import { blocks2to100 } from "../../../../core-test-utils/src/fixtures/testnet/blocks2to100"; +import { Blockchain } from "../../../src/blockchain"; +import { BlockProcessorResult } from "../../../src/processor"; +import { setUpFull, tearDownFull } from "../../__support__/setup"; + +const { Block } = models; +let app; +let blockchain: Blockchain; + +beforeAll(async () => { + app = await setUpFull(); + blockchain = app.resolvePlugin("blockchain"); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +describe("Exception handler", () => { + describe("execute", () => { + it("should reject if block has already been forged", async () => { + const handler = new ExceptionHandler(blockchain, new Block(blocks2to100[0])); + + jest.spyOn(blockchain.database, "getBlock").mockReturnValueOnce(true); + + expect(await handler.execute()).toBe(BlockProcessorResult.Rejected); + }); + + it("should accept if block has not already been forged", async () => { + const handler = new ExceptionHandler(blockchain, new Block(blocks2to100[0])); + + expect(await handler.execute()).toBe(BlockProcessorResult.Accepted); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/processor/handlers/unchained-handler.test.ts b/packages/core-blockchain/__tests__/processor/handlers/unchained-handler.test.ts new file mode 100644 index 0000000000..e69061f43c --- /dev/null +++ b/packages/core-blockchain/__tests__/processor/handlers/unchained-handler.test.ts @@ -0,0 +1,55 @@ +import "@arkecosystem/core-test-utils"; +import { UnchainedHandler } from "../../../src/processor/handlers"; + +import { models } from "@arkecosystem/crypto"; +import { blocks2to100 } from "../../../../core-test-utils/src/fixtures/testnet/blocks2to100"; +import { Blockchain } from "../../../src/blockchain"; +import { BlockProcessorResult } from "../../../src/processor"; +import { setUpFull, tearDownFull } from "../../__support__/setup"; + +const { Block } = models; +let app; +let blockchain: Blockchain; + +beforeAll(async () => { + app = await setUpFull(); + blockchain = app.resolvePlugin("blockchain"); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +describe("Exception handler", () => { + describe("execute", () => { + it("should fork if double forging is detected", async () => { + jest.spyOn(blockchain, "getLastBlock").mockReturnValue(new Block(blocks2to100[0])); + jest.spyOn(blockchain.database, "getActiveDelegates").mockReturnValue([ + { + publicKey: blocks2to100[0].generatorPublicKey, + }, + ]); + const forkBlock = jest.spyOn(blockchain, "forkBlock").mockReturnValue(true); + + const sameBlockDifferentId = new Block(blocks2to100[0]); + sameBlockDifferentId.data.id = "7536951"; + + const handler = new UnchainedHandler(blockchain, sameBlockDifferentId, true); + + expect(await handler.execute()).toBe(BlockProcessorResult.Rejected); + expect(forkBlock).toHaveBeenCalled(); + }); + + it("should log that blocks are being discarded when discarding blocks with height > current + 1", async () => { + jest.spyOn(blockchain, "getLastBlock").mockReturnValue(new Block(blocks2to100[0])); + blockchain.processQueue.length = () => 5; + + const loggerDebug = jest.spyOn(app.resolvePlugin("logger"), "debug"); + + const handler = new UnchainedHandler(blockchain, new Block(blocks2to100[5]), true); + + expect(await handler.execute()).toBe(BlockProcessorResult.DiscardedButCanBeBroadcasted); + expect(loggerDebug).toHaveBeenCalledWith("Discarded 5 downloaded blocks."); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/queue/interface.test.ts b/packages/core-blockchain/__tests__/queue/interface.test.ts new file mode 100644 index 0000000000..9ed581b1f6 --- /dev/null +++ b/packages/core-blockchain/__tests__/queue/interface.test.ts @@ -0,0 +1,71 @@ +import "@arkecosystem/core-test-utils"; +import async from "async"; +import { asValue } from "awilix"; +import delay from "delay"; +import { Blockchain } from "../../src/blockchain"; +import { QueueInterface } from "../../src/queue/interface"; +import { setUp, tearDown } from "../__support__/setup"; + +let fakeQueue; +let container; +let blockchain: Blockchain; + +class FakeQueue extends QueueInterface { + /** + * Create an instance of the process queue. + */ + constructor(readonly blockchainInstance: Blockchain, readonly event: string) { + super(blockchainInstance, event); + + this.queue = async.queue(async (item: any, cb) => { + await delay(1000); + return cb(); + }, 1); + } +} + +beforeAll(async () => { + container = await setUp(); + + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + + // Manually register the blockchain + const plugin = require("../../src").plugin; + + blockchain = await plugin.register(container, { + networkStart: false, + }); + + await container.register( + "blockchain", + asValue({ + name: "blockchain", + version: "0.1.0", + plugin: blockchain, + options: {}, + }), + ); +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(async () => { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + + fakeQueue = new FakeQueue(blockchain, "fake"); +}); + +describe("FakeQueue", () => { + it("should remove successfully an item from the queue", async () => { + const cb = jest.fn(); + fakeQueue.push(cb); + + expect(fakeQueue.queue.length()).toBe(1); + + fakeQueue.remove(obj => true); // removes everything, see async queue doc + + expect(fakeQueue.queue.length()).toBe(0); + }); +}); diff --git a/packages/core-blockchain/__tests__/queue/process.test.ts b/packages/core-blockchain/__tests__/queue/process.test.ts new file mode 100644 index 0000000000..3406f71ff7 --- /dev/null +++ b/packages/core-blockchain/__tests__/queue/process.test.ts @@ -0,0 +1,73 @@ +import "@arkecosystem/core-test-utils"; +import { asValue } from "awilix"; +import delay from "delay"; +import { blocks2to100 } from "../../../core-test-utils/src/fixtures/testnet/blocks2to100"; +import { Blockchain } from "../../src/blockchain"; +import { setUp, tearDown } from "../__support__/setup"; + +let processQueue; +let container; +let blockchain: Blockchain; + +beforeAll(async () => { + container = await setUp(); + + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + + // Manually register the blockchain + const plugin = require("../../src").plugin; + + blockchain = await plugin.register(container, { + networkStart: false, + }); + + await container.register( + "blockchain", + asValue({ + name: "blockchain", + version: "0.1.0", + plugin: blockchain, + options: {}, + }), + ); +}); + +afterAll(async () => { + jest.restoreAllMocks(); + await tearDown(); +}); + +beforeEach(async () => { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + jest.restoreAllMocks(); + + const ProcessQueue = require("../../src/queue").ProcessQueue; + processQueue = new ProcessQueue(blockchain, "processEvent"); +}); + +describe("ProcessQueue", () => { + it("should call blockchain processBlock when pushing a block to the queue", async () => { + const processBlock = jest.spyOn(blockchain, "processBlock").mockReturnValue(true); + + const cb = jest.fn(); + processQueue.push(blocks2to100[3], cb); + + await delay(200); + expect(processBlock).toHaveBeenCalled(); + }); + + it("should log error and call callback when blockchain processBlock throws", async () => { + const processBlock = jest.spyOn(blockchain, "processBlock").mockImplementation(() => { + throw new Error("wooo"); + }); + + const loggerError = jest.spyOn(container.resolvePlugin("logger"), "error"); + + const cb = jest.fn(); + processQueue.push(blocks2to100[3], cb); + + await delay(200); + expect(processBlock).toHaveBeenCalled(); + expect(loggerError).toHaveBeenCalledWith(`Failed to process block in ProcessQueue: ${blocks2to100[3].height}`); + }); +}); diff --git a/packages/core-blockchain/__tests__/queue/rebuild.test.ts b/packages/core-blockchain/__tests__/queue/rebuild.test.ts new file mode 100644 index 0000000000..419c2782ac --- /dev/null +++ b/packages/core-blockchain/__tests__/queue/rebuild.test.ts @@ -0,0 +1,88 @@ +import "@arkecosystem/core-test-utils"; +import { asValue } from "awilix"; +import delay from "delay"; +import { blocks2to100 } from "../../../core-test-utils/src/fixtures/testnet/blocks2to100"; +import { Blockchain } from "../../src/blockchain"; +import { setUp, tearDown } from "../__support__/setup"; + +let rebuildQueue; +let container; +let blockchain: Blockchain; + +beforeAll(async () => { + container = await setUp(); + + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + + // Manually register the blockchain + const plugin = require("../../src").plugin; + + blockchain = await plugin.register(container, { + networkStart: false, + }); + + await container.register( + "blockchain", + asValue({ + name: "blockchain", + version: "0.1.0", + plugin: blockchain, + options: {}, + }), + ); +}); + +afterAll(async () => { + jest.restoreAllMocks(); + await tearDown(); +}); + +beforeEach(async () => { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + jest.restoreAllMocks(); + + const RebuildQueue = require("../../src/queue").RebuildQueue; + rebuildQueue = new RebuildQueue(blockchain, "processEvent"); +}); + +describe("RebuildQueue", () => { + it("should call blockchain rebuildBlock when pushing a block to the queue", async () => { + const rebuildBlock = jest.spyOn(blockchain, "rebuildBlock").mockReturnValue(true); + + const cb = jest.fn(); + rebuildQueue.push(blocks2to100[3], cb); + + await delay(200); + expect(rebuildBlock).toHaveBeenCalled(); + }); + + it.skip("should just call callback if queue is paused when pushing a block to the queue", async () => { + // should call callback, but doesn't seem so... TODO + const rebuildBlock = jest.spyOn(blockchain, "rebuildBlock").mockReturnValue(true); + + const cb = jest.fn(() => { + throw new Error("uuuui"); + }); + rebuildQueue.queue.paused = true; + rebuildQueue.queue.push(blocks2to100[3], cb); + + await delay(200); + expect(rebuildBlock).not.toHaveBeenCalled(); + expect(cb).toHaveBeenCalled(); + }); + + it("should log error and call callback when blockchain rebuildBlock throws", async () => { + const rebuildBlock = jest.spyOn(blockchain, "rebuildBlock").mockImplementation(() => { + throw new Error("wooo"); + }); + + const loggerError = jest.spyOn(container.resolvePlugin("logger"), "error"); + + const cb = jest.fn(() => true); + rebuildQueue.push(blocks2to100[3], cb); + + await delay(200); + expect(rebuildBlock).toHaveBeenCalled(); + expect(loggerError).toHaveBeenCalledWith(`Failed to rebuild block in RebuildQueue: ${blocks2to100[3].height}`); + }); +}); diff --git a/packages/core-blockchain/__tests__/state-machine.test.js b/packages/core-blockchain/__tests__/state-machine.test.js deleted file mode 100644 index c0ebdedff1..0000000000 --- a/packages/core-blockchain/__tests__/state-machine.test.js +++ /dev/null @@ -1,228 +0,0 @@ -require('@arkecosystem/core-test-utils/lib/matchers') // eslint-disable-line no-unused-vars - -const { asValue } = require('awilix') - -const app = require('./__support__/setup') - -let stateMachine -let container -let blockchain - -beforeAll(async () => { - container = await app.setUp() - - process.env.ARK_SKIP_BLOCKCHAIN = true - - // Manually register the blockchain - const plugin = require('../lib').plugin - - blockchain = await plugin.register(container, { - networkStart: false, - }) - - await container.register( - 'blockchain', - asValue({ - name: 'blockchain', - version: '0.1.0', - plugin: blockchain, - options: {}, - }), - ) - - stateMachine = require('../lib/state-machine') -}) - -afterAll(async () => { - // Manually stop the blockchain - await blockchain.stop() - - await app.tearDown() -}) - -beforeEach(async () => { - process.env.ARK_SKIP_BLOCKCHAIN = false - blockchain.resetState() -}) - -describe('State Machine', () => { - it('should be an object', () => { - expect(stateMachine).toBeObject() - }) - - describe('actionMap', () => { - let actionMap - - beforeEach(() => { - actionMap = stateMachine.actionMap(blockchain) - }) - - describe('checkLater', () => { - it('should be a function', () => { - expect(actionMap.checkLater).toBeFunction() - }) - - it('should dispatch the event "WAKEUP" after a delay', async () => { - jest.useFakeTimers() - blockchain.dispatch = jest.fn() - - actionMap.checkLater() - expect(blockchain.dispatch).not.toBeCalled() - - jest.runAllTimers() - expect(blockchain.dispatch).toHaveBeenCalled() - expect(blockchain.dispatch).toHaveBeenCalledWith('WAKEUP') - - jest.useRealTimers() // restore standard timers - }) - }) - - describe('checkLastBlockSynced', () => { - it('should be a function', () => { - expect(actionMap.checkLastBlockSynced).toBeFunction() - }) - - it('should dispatch the event "SYNCED" if the blockchain is synced', () => { - blockchain.isSynced = jest.fn(() => true) - expect(actionMap.checkLastBlockSynced).toDispatch(blockchain, 'SYNCED') - }) - - it('should dispatch the event "NOTSYNCED" if the blockchain is not synced', () => { - blockchain.isSynced = jest.fn(() => false) - expect(() => actionMap.checkLastBlockSynced()).toDispatch( - blockchain, - 'NOTSYNCED', - ) - }) - }) - - describe('checkRebuildBlockSynced', () => { - it('should be a function', () => { - expect(actionMap.checkRebuildBlockSynced).toBeFunction() - }) - - it('should dispatch the event "SYNCED" if the blockchain is synced after a rebuild', () => { - blockchain.isRebuildSynced = jest.fn(() => true) - expect(() => actionMap.checkRebuildBlockSynced()).toDispatch( - blockchain, - 'SYNCED', - ) - }) - - it('should dispatch the event "NOTSYNCED" if the blockchain is not synced after a rebuild', () => { - blockchain.isRebuildSynced = jest.fn(() => false) - expect(() => actionMap.checkRebuildBlockSynced()).toDispatch( - blockchain, - 'NOTSYNCED', - ) - }) - }) - - describe('checkLastDownloadedBlockSynced', () => { - it('should be a function', () => { - expect(actionMap.checkLastDownloadedBlockSynced).toBeFunction() - }) - }) - - describe('downloadFinished', () => { - it('should be a function', () => { - expect(actionMap.downloadFinished).toBeFunction() - }) - - describe('if the network has started', () => { - it('should dispatch the event "SYNCFINISHED"', () => { - stateMachine.state.networkStart = true - expect(actionMap.downloadFinished).toDispatch( - blockchain, - 'SYNCFINISHED', - ) - }) - - it('should toggle its state', () => { - stateMachine.state.networkStart = true - actionMap.downloadFinished() - expect(stateMachine.state.networkStart).toBe(false) - }) - }) - - describe('if the network has not started', () => { - it('should not do anything', () => { - stateMachine.state.networkStart = false - expect(() => actionMap.downloadFinished()).not.toDispatch([ - blockchain, - 'SYNCFINISHED', - ]) - expect(stateMachine.state.networkStart).toBe(false) - }) - }) - }) - - describe('rebuildFinished', () => { - it('should be a function', () => { - expect(actionMap.rebuildFinished).toBeFunction() - }) - }) - - describe('downloadPaused', () => { - it('should be a function', () => { - expect(actionMap.downloadPaused).toBeFunction() - }) - - it('should dispatch the event "SYNCFINISHED"', () => { - expect(() => actionMap.syncingComplete()).toDispatch( - blockchain, - 'SYNCFINISHED', - ) - }) - }) - - describe('rebuildingComplete', () => { - it('should be a function', () => { - expect(actionMap.rebuildingComplete).toBeFunction() - }) - - it('should dispatch the event "REBUILDCOMPLETE"', () => { - expect(() => actionMap.rebuildingComplete()).toDispatch( - blockchain, - 'REBUILDCOMPLETE', - ) - }) - }) - - describe('exitApp', () => { - it('should be a function', () => { - expect(actionMap.exitApp).toBeFunction() - }) - }) - - describe('init', () => { - it('should be a function', () => { - expect(actionMap.init).toBeFunction() - }) - }) - - describe('rebuildBlocks', () => { - it('should be a function', () => { - expect(actionMap.rebuildBlocks).toBeFunction() - }) - }) - - describe('downloadBlocks', () => { - it('should be a function', () => { - expect(actionMap.downloadBlocks).toBeFunction() - }) - }) - - describe('analyseFork', () => { - it('should be a function', () => { - expect(actionMap.analyseFork).toBeFunction() - }) - }) - - describe('startForkRecovery', () => { - it('should be a function', () => { - expect(actionMap.startForkRecovery).toBeFunction() - }) - }) - }) -}) diff --git a/packages/core-blockchain/__tests__/state-machine.test.ts b/packages/core-blockchain/__tests__/state-machine.test.ts new file mode 100644 index 0000000000..d9b37d4606 --- /dev/null +++ b/packages/core-blockchain/__tests__/state-machine.test.ts @@ -0,0 +1,623 @@ +import "@arkecosystem/core-test-utils"; +import { roundCalculator } from "@arkecosystem/core-utils"; +import { slots } from "@arkecosystem/crypto"; +import { Block } from "@arkecosystem/crypto/dist/models"; +import { asValue } from "awilix"; +import { Blockchain } from "../src/blockchain"; +import { stateStorage } from "../src/state-storage"; +import { config as localConfig } from "./../src/config"; +import { setUp, tearDown } from "./__support__/setup"; + +let stateMachine; +let container; +let blockchain: Blockchain; + +beforeAll(async () => { + container = await setUp(); + + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + process.env.CORE_ENV = ""; + + // Manually register the blockchain + const plugin = require("../src").plugin; + + blockchain = await plugin.register(container, { + networkStart: false, + }); + + await container.register( + "blockchain", + asValue({ + name: "blockchain", + version: "0.1.0", + plugin: blockchain, + options: {}, + }), + ); + + stateMachine = require("../src/state-machine").stateMachine; +}); + +afterAll(async () => { + // Manually stop the blockchain + await blockchain.stop(); + + await tearDown(); +}); + +beforeEach(async () => { + process.env.CORE_SKIP_BLOCKCHAIN = "false"; + blockchain.resetState(); +}); + +describe("State Machine", () => { + describe("actionMap", () => { + let actionMap; + + beforeEach(() => { + actionMap = stateMachine.actionMap(blockchain); + }); + + describe("checkLater", () => { + it('should dispatch the event "WAKEUP" after a delay', async () => { + jest.useFakeTimers(); + blockchain.dispatch = jest.fn(); + + actionMap.checkLater(); + expect(blockchain.dispatch).not.toBeCalled(); + + jest.runAllTimers(); + expect(blockchain.dispatch).toHaveBeenCalled(); + expect(blockchain.dispatch).toHaveBeenCalledWith("WAKEUP"); + + jest.useRealTimers(); // restore standard timers + }); + }); + + describe("checkLastBlockSynced", () => { + it('should dispatch the event "SYNCED" if the blockchain is synced', () => { + blockchain.isSynced = jest.fn(() => true); + expect(actionMap.checkLastBlockSynced).toDispatch(blockchain, "SYNCED"); + }); + + it('should dispatch the event "NOTSYNCED" if the blockchain is not synced', () => { + blockchain.isSynced = jest.fn(() => false); + expect(() => actionMap.checkLastBlockSynced()).toDispatch(blockchain, "NOTSYNCED"); + }); + }); + + describe("checkRebuildBlockSynced", () => { + it('should dispatch the event "SYNCED" if the blockchain is synced after a rebuild', () => { + blockchain.isRebuildSynced = jest.fn(() => true); + expect(() => actionMap.checkRebuildBlockSynced()).toDispatch(blockchain, "SYNCED"); + }); + + it('should dispatch the event "NOTSYNCED" if the blockchain is not synced after a rebuild', () => { + blockchain.isRebuildSynced = jest.fn(() => false); + expect(() => actionMap.checkRebuildBlockSynced()).toDispatch(blockchain, "NOTSYNCED"); + }); + }); + + describe("checkLastDownloadedBlockSynced", () => { + it('should dispatch the event "NOTSYNCED" by default', async () => { + blockchain.isSynced = jest.fn(() => false); + blockchain.processQueue.length = jest.fn(() => 1); + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "NOTSYNCED"); + }); + + it('should dispatch the event "PAUSED" if the blockchain rebuild / process queue is more than 10000 long', async () => { + blockchain.isSynced = jest.fn(() => false); + blockchain.rebuildQueue.length = jest.fn(() => 10001); + blockchain.processQueue.length = jest.fn(() => 1); + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "PAUSED"); + + blockchain.rebuildQueue.length = jest.fn(() => 1); + blockchain.processQueue.length = jest.fn(() => 10001); + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "PAUSED"); + }); + + it('should dispatch the event "NETWORKHALTED" if stateStorage.noBlockCounter > 5 and process queue is empty', async () => { + blockchain.isSynced = jest.fn(() => false); + blockchain.processQueue.length = jest.fn(() => 0); + stateStorage.noBlockCounter = 6; + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "NETWORKHALTED"); + }); + + it(`should dispatch the event "FORK" if + - stateStorage.noBlockCounter > 5 and process queue is empty + - stateStorage.p2pUpdateCounter + 1 > 3 (network keeps missing blocks) + - blockchain.p2p.updatePeersOnMissingBlocks() returns "rollback"`, async () => { + blockchain.isSynced = jest.fn(() => false); + blockchain.processQueue.length = jest.fn(() => 0); + stateStorage.noBlockCounter = 6; + stateStorage.p2pUpdateCounter = 3; + jest.spyOn(blockchain.p2p, "updatePeersOnMissingBlocks").mockImplementation(() => "rollback"); + + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "FORK"); + }); + + it('should dispatch the event "SYNCED" if stateStorage.networkStart is true', async () => { + blockchain.isSynced = jest.fn(() => false); + stateStorage.noBlockCounter = 0; + stateStorage.networkStart = true; + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "SYNCED"); + }); + + it('should dispatch the event "TEST" if process.env.CORE_ENV === "test"', async () => { + const coreEnv = process.env.CORE_ENV; + process.env.CORE_ENV = "test"; + blockchain.isSynced = jest.fn(() => false); + await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "TEST"); + + process.env.CORE_ENV = coreEnv; + }); + }); + + describe("downloadFinished", () => { + describe("if the network has started", () => { + it('should dispatch the event "SYNCFINISHED"', () => { + stateMachine.state.networkStart = true; + expect(actionMap.downloadFinished).toDispatch(blockchain, "SYNCFINISHED"); + }); + + it("should toggle its state", () => { + stateMachine.state.networkStart = true; + actionMap.downloadFinished(); + expect(stateMachine.state.networkStart).toBe(false); + }); + }); + + describe("if the network has not started", () => { + it("should not do anything", () => { + stateMachine.state.networkStart = false; + expect(() => actionMap.downloadFinished()).not.toDispatch(blockchain, "SYNCFINISHED"); + expect(stateMachine.state.networkStart).toBe(false); + }); + }); + }); + + describe("rebuildFinished", () => { + it('should dispatch the event "PROCESSFINISHED"', async () => { + localConfig.set("state.maxLastBlocks", 50); + const config = container.getConfig(); + const genesisBlock = config.get("genesisBlock"); + + stateStorage.setLastBlock(new Block(genesisBlock)); + + await expect(actionMap.rebuildFinished).toDispatch(blockchain, "PROCESSFINISHED"); + }); + + it('should dispatch the event "FAILURE" when some called method threw an exception', async () => { + jest.spyOn(blockchain.database, "commitQueuedQueries").mockImplementationOnce(() => { + throw new Error("oops"); + }); + await expect(actionMap.rebuildFinished).toDispatch(blockchain, "FAILURE"); + }); + }); + + describe("downloadPaused", () => { + it('should log the info message "Blockchain download paused"', () => { + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + actionMap.downloadPaused(); + expect(loggerInfo).lastCalledWith("Blockchain download paused :clock1030:"); + }); + }); + + describe("syncingComplete", () => { + it('should dispatch the event "SYNCFINISHED"', () => { + expect(() => actionMap.syncingComplete()).toDispatch(blockchain, "SYNCFINISHED"); + }); + }); + + describe("rebuildingComplete", () => { + it('should dispatch the event "REBUILDCOMPLETE"', () => { + expect(() => actionMap.rebuildingComplete()).toDispatch(blockchain, "REBUILDCOMPLETE"); + }); + }); + + describe("stopped", () => { + it('should log the info message "The blockchain has been stopped"', () => { + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + actionMap.stopped(); + expect(loggerInfo).lastCalledWith("The blockchain has been stopped :guitar:"); + }); + }); + + describe("exitApp", () => { + it("should call container forceExit with error message", () => { + const forceExit = jest.spyOn(container, "forceExit").mockImplementationOnce(() => null); + actionMap.exitApp(); + expect(forceExit).lastCalledWith("Failed to startup blockchain. Exiting Ark Core! :rotating_light:"); + }); + }); + + describe("init", () => { + let databaseMocks: any = {}; + let loggerInfo; + let loggerError; + let loggerWarn; + + beforeAll(() => { + const logger = container.resolvePlugin("logger"); + loggerInfo = jest.spyOn(logger, "info"); + loggerError = jest.spyOn(logger, "error"); + loggerWarn = jest.spyOn(logger, "warn"); + }); + + beforeEach(() => { + databaseMocks = { + getLastBlock: jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue({ + data: { + height: 1, + timestamp: slots.getTime(), + }, + }), + saveBlock: jest.spyOn(blockchain.database, "saveBlock").mockReturnValue(true), + verifyBlockchain: jest.spyOn(blockchain.database, "verifyBlockchain").mockReturnValue({ + valid: true, + }), + deleteRound: jest.spyOn(blockchain.database, "deleteRound").mockReturnValue(true), + buildWallets: jest.spyOn(blockchain.database, "buildWallets").mockReturnValue(true), + saveWallets: jest.spyOn(blockchain.database, "saveWallets").mockReturnValue(true), + applyRound: jest.spyOn(blockchain.database, "applyRound").mockReturnValue(true), + getActiveDelegates: jest.spyOn(blockchain.database, "getActiveDelegates").mockReturnValue(true), + }; + }); + + afterEach(() => jest.resetAllMocks()); + afterAll(() => { + jest.restoreAllMocks(); + + process.env.NODE_ENV = "TEST"; + }); + + it("should get genesis block from config if there is no last block in database", async () => { + jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue(null); + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(databaseMocks.saveBlock).toHaveBeenCalled(); + }); + + it("should dispatch FAILURE if there is no last block in database and genesis block payload hash != configured nethash", async () => { + jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue(null); + const config = container.getConfig(); + const genesisBlock = config.get("genesisBlock"); + const mockConfigGet = jest + .spyOn(config, "get") + .mockImplementation(key => (key === "genesisBlock" ? genesisBlock : "")); + + await expect(() => actionMap.init()).toDispatch(blockchain, "FAILURE"); + + mockConfigGet.mockRestore(); + }); + + it("should verify database integrity if database recovery was not successful (!restoredDatabaseIntegrity)", async () => { + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(loggerInfo).nthCalledWith(1, "Verifying database integrity :hourglass_flowing_sand:"); + expect(loggerInfo).nthCalledWith(2, "Verified database integrity :smile_cat:"); + }); + + it("should dispatch ROLLBACK if database recovery was not successful and verifyBlockchain failed", async () => { + jest.spyOn(blockchain.database, "verifyBlockchain").mockReturnValue({ + valid: false, + }); + + await expect(() => actionMap.init()).toDispatch(blockchain, "ROLLBACK"); + expect(loggerError).nthCalledWith(1, "FATAL: The database is corrupted :fire:"); + }); + + it("should skip database integrity check if database recovery was successful (restoredDatabaseIntegrity)", async () => { + blockchain.database.restoredDatabaseIntegrity = true; + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(loggerInfo).nthCalledWith( + 1, + "Skipping database integrity check after successful database recovery :smile_cat:", + ); + }); + + it("should dispatch STARTED if networkStart is enabled", async () => { + stateStorage.networkStart = true; + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(databaseMocks.buildWallets).toHaveBeenCalledWith(1); + expect(databaseMocks.saveWallets).toHaveBeenCalledWith(true); + expect(databaseMocks.applyRound).toHaveBeenCalledWith(1); + + stateStorage.networkStart = false; // reset to default value + }); + + it('should dispatch STARTED if NODE_ENV === "test"', async () => { + process.env.NODE_ENV = "test"; + const logger = container.resolvePlugin("logger"); + const loggerVerbose = jest.spyOn(logger, "verbose"); + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(databaseMocks.buildWallets).toHaveBeenCalledWith(1); + expect(loggerVerbose).toHaveBeenCalledWith( + "TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY. :bangbang:", + ); + }); + + it("should dispatch REBUILD if stateStorage.fastRebuild", async () => { + process.env.NODE_ENV = ""; + + // mock getLastBlock() timestamp and fastRebuild config to trigger stateStorage.fastRebuild = true + jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue({ + data: { + height: 1, + timestamp: 0, + }, + }); + const mockConfigGet = jest + .spyOn(localConfig, "get") + .mockImplementation(key => (key === "fastRebuild" ? true : "")); + + await expect(() => actionMap.init()).toDispatch(blockchain, "REBUILD"); + + mockConfigGet.mockRestore(); + }); + + it("should rollbackCurrentRound and dispatch STARTED if couldnt get activeDelegates", async () => { + process.env.NODE_ENV = ""; + jest.spyOn(blockchain.database, "getActiveDelegates").mockReturnValue(undefined); + const spyRollbackCurrentRound = jest.spyOn(blockchain, "rollbackCurrentRound").mockReturnThis(); + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(spyRollbackCurrentRound).toHaveBeenCalled(); + }); + + it("should rebuild wallets table and dispatch STARTED if database.buildWallets() failed", async () => { + process.env.NODE_ENV = ""; + jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue({ + data: { + height: 2, + timestamp: slots.getTime(), + }, + }); + jest.spyOn(blockchain.database, "buildWallets").mockReturnValue(false); + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(loggerWarn).toHaveBeenCalledWith( + "Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown. :hammer:", + ); + expect(databaseMocks.saveWallets).toHaveBeenCalledWith(true); + }); + + it("should clean round data if new round starts at block.height + 1 (and dispatch STARTED)", async () => { + process.env.NODE_ENV = ""; + const spyIsNewRound = jest.spyOn(roundCalculator, "isNewRound").mockReturnValue(true); + + await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); + expect(databaseMocks.deleteRound).toHaveBeenCalled(); + expect(loggerInfo).toHaveBeenCalledWith( + "New round 1 detected. Cleaning calculated data before restarting!", + ); + + spyIsNewRound.mockRestore(); + }); + + it("should log error and dispatch FAILURE if an exception was thrown", async () => { + jest.spyOn(blockchain.database, "getLastBlock").mockImplementation(() => { + throw new Error("oops"); + }); + + await expect(() => actionMap.init()).toDispatch(blockchain, "FAILURE"); + expect(loggerError.mock.calls[0][0]).toContain("Error: oops"); + }); + }); + + describe("rebuildBlocks", () => { + let genesisBlock; + + beforeAll(() => { + const config = container.getConfig(); + genesisBlock = config.get("genesisBlock"); + }); + + it("should dispatch NOBLOCK if no new blocks were downloaded from peer", async () => { + stateStorage.lastDownloadedBlock = new Block(genesisBlock); + + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + + jest.spyOn(blockchain.p2p, "downloadBlocks").mockReturnValue([]); + await expect(() => actionMap.rebuildBlocks()).toDispatch(blockchain, "NOBLOCK"); + expect(loggerInfo).toHaveBeenCalledWith("No new blocks found on this peer"); + }); + + it("should dispatch DOWNLOADED if new blocks were successfully downloaded from peer", async () => { + stateStorage.lastDownloadedBlock = new Block(genesisBlock); + + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + + jest.spyOn(blockchain.p2p, "downloadBlocks").mockReturnValue([ + { + numberOfTransactions: 2, + previousBlock: genesisBlock.id, + }, + ]); + await expect(() => actionMap.rebuildBlocks()).toDispatch(blockchain, "DOWNLOADED"); + expect(loggerInfo).toHaveBeenCalledWith( + "Downloaded 1 new block accounting for a total of 2 transactions", + ); + }); + + it("should dispatch NOBLOCK if new blocks were downloaded from peer but didnt match last known block", async () => { + stateStorage.lastDownloadedBlock = new Block(genesisBlock); + + const logger = container.resolvePlugin("logger"); + const loggerWarn = jest.spyOn(logger, "warn"); + + const downloadedBlock = { + numberOfTransactions: 2, + previousBlock: "123456", + }; + jest.spyOn(blockchain.p2p, "downloadBlocks").mockReturnValue([downloadedBlock]); + await expect(() => actionMap.rebuildBlocks()).toDispatch(blockchain, "NOBLOCK"); + expect(loggerWarn).toHaveBeenCalledWith( + `Downloaded block not accepted: ${JSON.stringify(downloadedBlock)}`, + ); + }); + }); + + describe("downloadBlocks", () => { + let genesisBlock; + let loggerInfo; + let loggerWarn; + + beforeAll(() => { + const config = container.getConfig(); + genesisBlock = config.get("genesisBlock"); + + const logger = container.resolvePlugin("logger"); + loggerInfo = jest.spyOn(logger, "info"); + loggerWarn = jest.spyOn(logger, "warn"); + }); + + beforeEach(() => { + stateStorage.lastDownloadedBlock = new Block(genesisBlock); + }); + + afterEach(() => jest.resetAllMocks()); + + it("should just return if blockchain isStopped", async () => { + blockchain.isStopped = true; + expect(await actionMap.downloadBlocks()).toBe(undefined); + + blockchain.isStopped = false; // reset to original value + }); + + it("should dispatch DOWNLOADED if new blocks downloaded are chained", async () => { + jest.spyOn(blockchain.p2p, "downloadBlocks").mockReturnValue([ + { + numberOfTransactions: 2, + previousBlock: genesisBlock.id, + height: 2, + timestamp: genesisBlock.timestamp + 115, + }, + ]); + const enQueueBlocks = jest.spyOn(blockchain, "enqueueBlocks").mockReturnValue(true); + + await expect(() => actionMap.downloadBlocks()).toDispatch(blockchain, "DOWNLOADED"); + expect(loggerInfo).toHaveBeenCalledWith( + "Downloaded 1 new block accounting for a total of 2 transactions", + ); + expect(enQueueBlocks).toHaveBeenCalled(); + + enQueueBlocks.mockRestore(); + }); + + it("should dispatch NOBLOCK if new blocks downloaded are not chained", async () => { + const downloadedBlock = { + numberOfTransactions: 2, + previousBlock: genesisBlock.id, + height: 3, + timestamp: genesisBlock.timestamp + 115, + }; + jest.spyOn(blockchain.p2p, "downloadBlocks").mockReturnValue([downloadedBlock]); + await expect(() => actionMap.downloadBlocks()).toDispatch(blockchain, "NOBLOCK"); + expect(loggerWarn).toHaveBeenCalledWith( + `Downloaded block not accepted: ${JSON.stringify(downloadedBlock)}`, + ); + }); + + it("should dispatch NOBLOCK if new blocks downloaded are empty", async () => { + jest.spyOn(blockchain.p2p, "downloadBlocks").mockReturnValue([]); + await expect(() => actionMap.downloadBlocks()).toDispatch(blockchain, "NOBLOCK"); + expect(loggerInfo).toHaveBeenCalledWith("No new block found on this peer"); + }); + }); + + describe("analyseFork", () => { + it("should log 'analysing fork' message", () => { + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + + actionMap.analyseFork(); + + expect(loggerInfo).toHaveBeenCalledWith("Analysing fork :mag:"); + }); + }); + + describe("startForkRecovery", () => { + it("should proceed to fork recovery and dispatch SUCCESS", async () => { + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + + const methodsCalled = [ + jest.spyOn(blockchain.database, "commitQueuedQueries").mockReturnValue(true), + jest.spyOn(blockchain.transactionPool, "buildWallets").mockReturnValue(true), + jest.spyOn(blockchain.p2p, "refreshPeersAfterFork").mockReturnValue(true), + jest.spyOn(blockchain, "clearAndStopQueue"), + jest.spyOn(blockchain, "removeBlocks").mockReturnValue(true), + ]; + await expect(() => actionMap.startForkRecovery()).toDispatch(blockchain, "SUCCESS"); + + expect(loggerInfo).toHaveBeenCalledWith("Starting fork recovery :fork_and_knife:"); + methodsCalled.forEach(method => { + expect(method).toHaveBeenCalled(); + }); + }); + }); + + describe("rollbackDatabase", () => { + afterEach(() => jest.restoreAllMocks()); + + it("should try to remove X blocks based on databaseRollback config until database.verifyBlockchain() passes - and dispatch SUCCESS", async () => { + const logger = container.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + + jest.spyOn(localConfig, "get").mockReturnValue({ + maxBlockRewind: 14, + steps: 3, + }); + const removeTopBlocks = jest.spyOn(blockchain, "removeTopBlocks").mockReturnValue(true); + jest.spyOn(blockchain.database, "verifyBlockchain") + .mockReturnValue({ valid: true }) // default + .mockReturnValueOnce({ valid: false }) // first call + .mockReturnValueOnce({ valid: false }); // 2nd call + jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue({ + data: { + height: 1, + }, + }); + + await expect(() => actionMap.rollbackDatabase()).toDispatch(blockchain, "SUCCESS"); + + expect(loggerInfo).toHaveBeenCalledWith( + "Database integrity verified again after rollback to height 1 :green_heart:", + ); + expect(removeTopBlocks).toHaveBeenCalledTimes(3); // because the 3rd time verifyBlockchain returned true + }); + + it(`should try to remove X blocks based on databaseRollback config until database.verifyBlockchain() passes + and dispatch FAILURE as verifyBlockchain never passed`, async () => { + const logger = container.resolvePlugin("logger"); + const loggerError = jest.spyOn(logger, "error"); + + jest.spyOn(localConfig, "get").mockReturnValue({ + maxBlockRewind: 14, + steps: 3, + }); + const removeTopBlocks = jest.spyOn(blockchain, "removeTopBlocks").mockReturnValue(true); + jest.spyOn(blockchain.database, "verifyBlockchain").mockReturnValue({ valid: false }); + jest.spyOn(blockchain.database, "getLastBlock").mockReturnValue({ + data: { + height: 1, + }, + }); + + await expect(() => actionMap.rollbackDatabase()).toDispatch(blockchain, "FAILURE"); + + expect(loggerError).toHaveBeenCalledWith( + "FATAL: Failed to restore database integrity :skull: :skull: :skull:", + ); + expect(removeTopBlocks).toHaveBeenCalledTimes(5); // because after 5 times we get past maxBlockRewind + }); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/state-storage.test.js b/packages/core-blockchain/__tests__/state-storage.test.js deleted file mode 100644 index 8ea7aa96b9..0000000000 --- a/packages/core-blockchain/__tests__/state-storage.test.js +++ /dev/null @@ -1,326 +0,0 @@ -const { Block } = require('@arkecosystem/crypto').models -const blocks1to100 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.2-100') -const blocks101to155 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.101-155') - -const state = require('../lib/state-storage') -const app = require('./__support__/setup') - -const blocks = blocks1to100 - .concat(blocks101to155) - .map(block => new Block(block)) - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(() => { - state.reset() -}) - -describe('State Storage', () => { - it('should be an object', () => { - expect(state).toBeObject() - }) - - describe('getLastBlock', () => { - it('should be a function', () => { - expect(state.getLastBlock).toBeFunction() - }) - - it('should return null when no last block', () => { - expect(state.getLastBlock()).toBeNull() - }) - - it('should return the last block', () => { - state.setLastBlock(blocks[0]) - state.setLastBlock(blocks[1]) - - expect(state.getLastBlock()).toBe(blocks[1]) - }) - }) - - describe('setLastBlock', () => { - it('should be a function', () => { - expect(state.setLastBlock).toBeFunction() - }) - - it('should set the last block', () => { - state.setLastBlock(blocks[0]) - expect(state.getLastBlock()).toBe(blocks[0]) - }) - - it('should not exceed the max last blocks', () => { - for (let i = 0; i < 100; i++) { - // 100 is default - state.setLastBlock(blocks[i]) - } - - expect(state.getLastBlocks()).toHaveLength(100) - expect(state.getLastBlock()).toBe(blocks[99]) - expect(state.getLastBlocks().slice(-1)[0]).toBe(blocks[0]) - - // Push one more to remove the first last block. - state.setLastBlock(blocks[100]) - - expect(state.getLastBlocks()).toHaveLength(100) - expect(state.getLastBlock()).toBe(blocks[100]) - expect(state.getLastBlocks().slice(-1)[0]).toBe(blocks[1]) - }) - - it('should remove last blocks when going to lower height', () => { - for (let i = 0; i < 100; i++) { - // 100 is default - state.setLastBlock(blocks[i]) - } - - expect(state.getLastBlocks()).toHaveLength(100) - expect(state.getLastBlock()).toBe(blocks[99]) - - // Set last height - 1 - state.setLastBlock(blocks[98]) - - expect(state.getLastBlocks()).toHaveLength(99) - expect(state.getLastBlock()).toBe(blocks[98]) - - // Set to first block - state.setLastBlock(blocks[0]) - expect(state.getLastBlocks()).toHaveLength(1) - expect(state.getLastBlock()).toBe(blocks[0]) - }) - }) - - describe('getLastBlocks', () => { - it('should be a function', () => { - expect(state.getLastBlocks).toBeFunction() - }) - - it('should return the last blocks', () => { - for (let i = 0; i < 5; i++) { - state.setLastBlock(blocks[i]) - } - - const lastBlocks = state.getLastBlocks() - expect(lastBlocks).toHaveLength(5) - - for (let i = 0; i < 5; i++) { - expect(lastBlocks[i]).toBeInstanceOf(Block) - expect(lastBlocks[i].data.height).toBe(6 - i) // Height started at 2 - expect(lastBlocks[i]).toBe(blocks[4 - i]) - } - }) - }) - - describe('getLastBlocksData', () => { - it('should be a function', () => { - expect(state.getLastBlocksData).toBeFunction() - }) - - it('should return the last blocks data', () => { - for (let i = 0; i < 5; i++) { - state.setLastBlock(blocks[i]) - } - - const lastBlocksData = state.getLastBlocksData().toArray() - expect(lastBlocksData).toHaveLength(5) - - for (let i = 0; i < 5; i++) { - expect(lastBlocksData[0]).not.toBeInstanceOf(Block) - expect(lastBlocksData[i].height).toBe(6 - i) // Height started at 2 - expect(lastBlocksData[i]).toHaveProperty('transactions') - delete lastBlocksData[i].transactions - expect(lastBlocksData[i]).toEqual(blocks[4 - i].data) - } - }) - }) - - describe('getLastBlockIds', () => { - it('should be a function', () => { - expect(state.getLastBlockIds).toBeFunction() - }) - - it('should return the last blocks data', () => { - for (let i = 0; i < 5; i++) { - state.setLastBlock(blocks[i]) - } - - const lastBlockIds = state.getLastBlockIds() - expect(lastBlockIds).toHaveLength(5) - - for (let i = 0; i < 5; i++) { - expect(lastBlockIds[i]).toBe(blocks[4 - i].data.id) - } - }) - }) - - describe('getLastBlocksByHeight', () => { - it('should be a function', () => { - expect(state.getLastBlocksByHeight).toBeFunction() - }) - - it('should return the last blocks data', () => { - for (let i = 0; i < 100; i++) { - state.setLastBlock(blocks[i]) - } - - const lastBlocksByHeight = state.getLastBlocksByHeight(0, 101) - expect(lastBlocksByHeight).toHaveLength(100) - expect(lastBlocksByHeight[0].height).toBe(blocks[0].data.height) - }) - - it('should return one last block if no end height', () => { - for (let i = 0; i < 100; i++) { - state.setLastBlock(blocks[i]) - } - - const lastBlocksByHeight = state.getLastBlocksByHeight(50) - expect(lastBlocksByHeight).toHaveLength(1) - expect(lastBlocksByHeight[0].height).toBe(50) - }) - }) - - describe('getCommonBlocks', () => { - it('should be a function', () => { - expect(state.getCommonBlocks).toBeFunction() - }) - - it('should get common blocks', () => { - for (let i = 0; i < 100; i++) { - state.setLastBlock(blocks[i]) - } - - // Heights 90 - 100 - const ids = blocks.slice(89, 99).map(block => block.data.id) - const commonBlocks = state.getCommonBlocks(ids) - expect(ids).toHaveLength(10) - expect(commonBlocks).toHaveLength(10) - - for (let i = 0; i < commonBlocks.length; i++) { - expect(commonBlocks[i].height).toBe(blocks[98 - i].data.height) - } - }) - }) - - describe('cacheTransactions', () => { - it('should be a function', () => { - expect(state.cacheTransactions).toBeFunction() - }) - - it('should add transaction id', () => { - expect(state.cacheTransactions([{ id: '1' }])).toEqual({ - added: [{ id: '1' }], - notAdded: [], - }) - expect(state.getCachedTransactionIds()).toHaveLength(1) - }) - - it('should not add duplicate transaction ids', () => { - expect(state.cacheTransactions([{ id: '1' }])).toEqual({ - added: [{ id: '1' }], - notAdded: [], - }) - expect(state.cacheTransactions([{ id: '1' }])).toEqual({ - added: [], - notAdded: [{ id: '1' }], - }) - expect(state.getCachedTransactionIds()).toHaveLength(1) - }) - - it('should not add more than 10000 unique transaction ids', () => { - const transactions = [] - for (let i = 0; i < 10000; i++) { - transactions.push({ id: i.toString() }) - } - - expect(state.cacheTransactions(transactions)).toEqual({ - added: transactions, - notAdded: [], - }) - - expect(state.getCachedTransactionIds()).toHaveLength(10000) - expect(state.getCachedTransactionIds()[0]).toEqual('0') - - expect(state.cacheTransactions([{ id: '10000' }])).toEqual({ - added: [{ id: '10000' }], - notAdded: [], - }) - expect(state.getCachedTransactionIds()).toHaveLength(10000) - expect(state.getCachedTransactionIds()[0]).toEqual('1') - }) - }) - - describe('removeCachedTransactionIds', () => { - it('should be a function', () => { - expect(state.removeCachedTransactionIds).toBeFunction() - }) - - it('should remove cached transaction ids', () => { - const transactions = [] - for (let i = 0; i < 10; i++) { - transactions.push({ id: i.toString() }) - } - - expect(state.cacheTransactions(transactions)).toEqual({ - added: transactions, - notAdded: [], - }) - - expect(state.getCachedTransactionIds()).toHaveLength(10) - state.removeCachedTransactionIds(transactions.map(tx => tx.id)) - expect(state.getCachedTransactionIds()).toHaveLength(0) - }) - }) - - describe('getCachedTransactionIds', () => { - it('should be a function', () => { - expect(state.getCachedTransactionIds).toBeFunction() - }) - }) - - describe('pingBlock', () => { - it('should be a function', () => { - expect(state.pingBlock).toBeFunction() - }) - }) - - describe('pushPingBlock', () => { - it('should be a function', () => { - expect(state.pushPingBlock).toBeFunction() - }) - }) - - describe('reset', () => { - it('should be a function', () => { - expect(state.reset).toBeFunction() - }) - - it('should reset the state', () => { - for (let i = 0; i < 100; i++) { - state.setLastBlock(blocks[i]) - } - - expect(state.getLastBlocks()).toHaveLength(100) - state.reset() - expect(state.getLastBlocks()).toHaveLength(0) - }) - }) - - describe('clear', () => { - it('should be a function', () => { - expect(state.clear).toBeFunction() - }) - - it('should clear the last blocks', () => { - for (let i = 0; i < 100; i++) { - state.setLastBlock(blocks[i]) - } - - expect(state.getLastBlocks()).toHaveLength(100) - state.clear() - expect(state.getLastBlocks()).toHaveLength(0) - }) - }) -}) diff --git a/packages/core-blockchain/__tests__/state-storage.test.ts b/packages/core-blockchain/__tests__/state-storage.test.ts new file mode 100644 index 0000000000..4be0ba851e --- /dev/null +++ b/packages/core-blockchain/__tests__/state-storage.test.ts @@ -0,0 +1,338 @@ +import "@arkecosystem/core-test-utils"; +import { blocks101to155 } from "@arkecosystem/core-test-utils/src/fixtures/testnet/blocks101to155"; +import { blocks2to100 } from "@arkecosystem/core-test-utils/src/fixtures/testnet/blocks2to100"; +import { models } from "@arkecosystem/crypto"; +import delay from "delay"; +import { config } from "../src/config"; +import { defaults } from "../src/defaults"; +import { setUp, tearDown } from "./__support__/setup"; + +const { Block } = models; +const blocks = blocks2to100.concat(blocks101to155).map(block => new Block(block)); +let app; +let stateStorage; + +beforeAll(async () => { + app = await setUp(); + config.init(defaults); + + stateStorage = require("../src").stateStorage; +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(() => { + stateStorage.reset(); +}); + +describe("State Storage", () => { + describe("getLastBlock", () => { + it("should return null when no last block", () => { + expect(stateStorage.getLastBlock()).toBeNull(); + }); + + it("should return the last block", () => { + stateStorage.setLastBlock(blocks[0]); + stateStorage.setLastBlock(blocks[1]); + + expect(stateStorage.getLastBlock()).toBe(blocks[1]); + }); + }); + + describe("setLastBlock", () => { + it("should set the last block", () => { + stateStorage.setLastBlock(blocks[0]); + expect(stateStorage.getLastBlock()).toBe(blocks[0]); + }); + + it("should not exceed the max last blocks", () => { + for (let i = 0; i < 100; i++) { + // 100 is default + stateStorage.setLastBlock(blocks[i]); + } + + expect(stateStorage.getLastBlocks()).toHaveLength(100); + expect(stateStorage.getLastBlock()).toBe(blocks[99]); + expect(stateStorage.getLastBlocks().slice(-1)[0]).toBe(blocks[0]); + + // Push one more to remove the first last block. + stateStorage.setLastBlock(blocks[100]); + + expect(stateStorage.getLastBlocks()).toHaveLength(100); + expect(stateStorage.getLastBlock()).toBe(blocks[100]); + expect(stateStorage.getLastBlocks().slice(-1)[0]).toBe(blocks[1]); + }); + + it("should remove last blocks when going to lower height", () => { + for (let i = 0; i < 100; i++) { + // 100 is default + stateStorage.setLastBlock(blocks[i]); + } + + expect(stateStorage.getLastBlocks()).toHaveLength(100); + expect(stateStorage.getLastBlock()).toBe(blocks[99]); + + // Set last height - 1 + stateStorage.setLastBlock(blocks[98]); + + expect(stateStorage.getLastBlocks()).toHaveLength(99); + expect(stateStorage.getLastBlock()).toBe(blocks[98]); + + // Set to first block + stateStorage.setLastBlock(blocks[0]); + expect(stateStorage.getLastBlocks()).toHaveLength(1); + expect(stateStorage.getLastBlock()).toBe(blocks[0]); + }); + }); + + describe("getLastBlocks", () => { + it("should return the last blocks", () => { + for (let i = 0; i < 5; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + const lastBlocks = stateStorage.getLastBlocks(); + expect(lastBlocks).toHaveLength(5); + + for (let i = 0; i < 5; i++) { + expect(lastBlocks[i]).toBeInstanceOf(Block); + expect(lastBlocks[i].data.height).toBe(6 - i); // Height started at 2 + expect(lastBlocks[i]).toBe(blocks[4 - i]); + } + }); + }); + + describe("getLastBlocksData", () => { + it("should return the last blocks data", () => { + for (let i = 0; i < 5; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + const lastBlocksData = stateStorage.getLastBlocksData().toArray() as models.IBlockData[]; + expect(lastBlocksData).toHaveLength(5); + + for (let i = 0; i < 5; i++) { + expect(lastBlocksData[0]).not.toBeInstanceOf(Block); + expect(lastBlocksData[i].height).toBe(6 - i); // Height started at 2 + expect(lastBlocksData[i]).toHaveProperty("transactions"); + delete lastBlocksData[i].transactions; + expect(lastBlocksData[i]).toEqual(blocks[4 - i].data); + } + }); + }); + + describe("getLastBlockIds", () => { + it("should return the last blocks data", () => { + for (let i = 0; i < 5; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + const lastBlockIds = stateStorage.getLastBlockIds(); + expect(lastBlockIds).toHaveLength(5); + + for (let i = 0; i < 5; i++) { + expect(lastBlockIds[i]).toBe(blocks[4 - i].data.id); + } + }); + }); + + describe("getLastBlocksByHeight", () => { + it("should return the last blocks data", () => { + for (let i = 0; i < 100; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + const lastBlocksByHeight = stateStorage.getLastBlocksByHeight(0, 101); + expect(lastBlocksByHeight).toHaveLength(100); + expect(lastBlocksByHeight[0].height).toBe(blocks[0].data.height); + }); + + it("should return one last block if no end height", () => { + for (let i = 0; i < 100; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + const lastBlocksByHeight = stateStorage.getLastBlocksByHeight(50); + expect(lastBlocksByHeight).toHaveLength(1); + expect(lastBlocksByHeight[0].height).toBe(50); + }); + }); + + describe("getCommonBlocks", () => { + it("should get common blocks", () => { + for (let i = 0; i < 100; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + // Heights 90 - 100 + const ids = blocks.slice(89, 99).map(block => block.data.id); + const commonBlocks = stateStorage.getCommonBlocks(ids); + expect(ids).toHaveLength(10); + expect(commonBlocks).toHaveLength(10); + + for (let i = 0; i < commonBlocks.length; i++) { + expect(commonBlocks[i].height).toBe(blocks[98 - i].data.height); + } + }); + }); + + describe("cacheTransactions", () => { + it("should add transaction id", () => { + expect(stateStorage.cacheTransactions([{ id: "1" } as models.ITransactionData])).toEqual({ + added: [{ id: "1" }], + notAdded: [], + }); + expect(stateStorage.getCachedTransactionIds()).toHaveLength(1); + }); + + it("should not add duplicate transaction ids", () => { + expect(stateStorage.cacheTransactions([{ id: "1" } as models.ITransactionData])).toEqual({ + added: [{ id: "1" }], + notAdded: [], + }); + expect(stateStorage.cacheTransactions([{ id: "1" } as models.ITransactionData])).toEqual({ + added: [], + notAdded: [{ id: "1" }], + }); + expect(stateStorage.getCachedTransactionIds()).toHaveLength(1); + }); + + it("should not add more than 10000 unique transaction ids", () => { + const transactions = []; + for (let i = 0; i < 10000; i++) { + transactions.push({ id: i.toString() }); + } + + expect(stateStorage.cacheTransactions(transactions)).toEqual({ + added: transactions, + notAdded: [], + }); + + expect(stateStorage.getCachedTransactionIds()).toHaveLength(10000); + expect(stateStorage.getCachedTransactionIds()[0]).toEqual("0"); + + expect(stateStorage.cacheTransactions([{ id: "10000" } as any])).toEqual({ + added: [{ id: "10000" }], + notAdded: [], + }); + expect(stateStorage.getCachedTransactionIds()).toHaveLength(10000); + expect(stateStorage.getCachedTransactionIds()[0]).toEqual("1"); + }); + }); + + describe("removeCachedTransactionIds", () => { + it("should remove cached transaction ids", () => { + const transactions = []; + for (let i = 0; i < 10; i++) { + transactions.push({ id: i.toString() }); + } + + expect(stateStorage.cacheTransactions(transactions)).toEqual({ + added: transactions, + notAdded: [], + }); + + expect(stateStorage.getCachedTransactionIds()).toHaveLength(10); + stateStorage.removeCachedTransactionIds(transactions.map(tx => tx.id)); + expect(stateStorage.getCachedTransactionIds()).toHaveLength(0); + }); + }); + + describe("reset", () => { + it("should reset the state", () => { + for (let i = 0; i < 100; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + expect(stateStorage.getLastBlocks()).toHaveLength(100); + stateStorage.reset(); + expect(stateStorage.getLastBlocks()).toHaveLength(0); + }); + }); + + describe("clear", () => { + it("should clear the last blocks", () => { + for (let i = 0; i < 100; i++) { + stateStorage.setLastBlock(blocks[i]); + } + + expect(stateStorage.getLastBlocks()).toHaveLength(100); + stateStorage.clear(); + expect(stateStorage.getLastBlocks()).toHaveLength(0); + }); + }); + + describe("pingBlock", () => { + it("should return false if there is no blockPing", () => { + stateStorage.blockPing = null; + expect(stateStorage.pingBlock(blocks2to100[5])).toBeFalse(); + }); + + it("should return true if block pinged == current blockPing and should update stats", async () => { + const currentTime = new Date().getTime(); + stateStorage.blockPing = { + count: 1, + first: currentTime, + last: currentTime, + block: blocks2to100[5], + }; + await delay(20); + + expect(stateStorage.pingBlock(blocks2to100[5])).toBeTrue(); + expect(stateStorage.blockPing.count).toBe(2); + expect(stateStorage.blockPing.block).toBe(blocks2to100[5]); + expect(stateStorage.blockPing.last).toBeGreaterThan(currentTime); + expect(stateStorage.blockPing.first).toBe(currentTime); + }); + + it("should return false if block pinged != current blockPing", () => { + const currentTime = new Date().getTime(); + stateStorage.blockPing = { + count: 1, + first: currentTime, + last: currentTime, + block: blocks2to100[3], + }; + expect(stateStorage.pingBlock(blocks2to100[5])).toBeFalse(); + expect(stateStorage.blockPing.count).toBe(1); + expect(stateStorage.blockPing.block).toBe(blocks2to100[3]); + expect(stateStorage.blockPing.last).toBe(currentTime); + expect(stateStorage.blockPing.first).toBe(currentTime); + }); + }); + + describe("pushPingBlock", () => { + it("should push the block provided as blockPing", () => { + stateStorage.blockPing = null; + + stateStorage.pushPingBlock(blocks2to100[5]); + + expect(stateStorage.blockPing).toBeObject(); + expect(stateStorage.blockPing.block).toBe(blocks2to100[5]); + expect(stateStorage.blockPing.count).toBe(1); + }); + + it("should log info message if there is already a blockPing", async () => { + stateStorage.blockPing = { + count: 1, + first: new Date().getTime(), + last: new Date().getTime(), + block: blocks2to100[3], + }; + + const logger = app.resolvePlugin("logger"); + const loggerInfo = jest.spyOn(logger, "info"); + + stateStorage.pushPingBlock(blocks2to100[5]); + + expect(loggerInfo).toHaveBeenCalledWith( + `Block ${blocks2to100[3].height.toLocaleString()} pinged blockchain 1 times`, + ); + expect(stateStorage.blockPing).toBeObject(); + expect(stateStorage.blockPing.block).toBe(blocks2to100[5]); + expect(stateStorage.blockPing.count).toBe(1); + }); + }); +}); diff --git a/packages/core-blockchain/__tests__/utils/is-blocked-chained.test.ts b/packages/core-blockchain/__tests__/utils/is-blocked-chained.test.ts new file mode 100644 index 0000000000..295e567452 --- /dev/null +++ b/packages/core-blockchain/__tests__/utils/is-blocked-chained.test.ts @@ -0,0 +1,116 @@ +import "jest-extended"; + +import { models, slots } from "@arkecosystem/crypto"; +import { isBlockChained } from "../../src/utils"; + +describe("isChained", () => { + it("should be ok", () => { + const previousBlock = { + data: { + id: "1", + timestamp: slots.getSlotTime(0), + height: 1, + previousBlock: null, + }, + } as models.IBlock; + + const nextBlock = { + data: { + id: "2", + timestamp: slots.getSlotTime(1), + height: 2, + previousBlock: "1", + }, + } as models.IBlock; + + expect(isBlockChained(previousBlock, nextBlock)).toBeTrue(); + }); + + it("should not chain when previous block does not match", () => { + const previousBlock = { + data: { + id: "2", + timestamp: slots.getSlotTime(0), + height: 2, + previousBlock: null, + }, + } as models.IBlock; + + const nextBlock = { + data: { + id: "1", + timestamp: slots.getSlotTime(1), + height: 3, + previousBlock: "1", + }, + } as models.IBlock; + + expect(isBlockChained(previousBlock, nextBlock)).toBeFalse(); + }); + + it("should not chain when next height is not plus 1", () => { + const previousBlock = { + data: { + id: "1", + timestamp: slots.getSlotTime(0), + height: 1, + previousBlock: null, + }, + } as models.IBlock; + + const nextBlock = { + data: { + id: "2", + timestamp: slots.getSlotTime(1), + height: 3, + previousBlock: "1", + }, + } as models.IBlock; + + expect(isBlockChained(previousBlock, nextBlock)).toBeFalse(); + }); + + it("should not chain when same slot", () => { + const previousBlock = { + data: { + id: "1", + timestamp: slots.getSlotTime(0), + height: 1, + previousBlock: null, + }, + } as models.IBlock; + + const nextBlock = { + data: { + id: "2", + timestamp: slots.getSlotTime(0), + height: 3, + previousBlock: "1", + }, + } as models.IBlock; + + expect(isBlockChained(previousBlock, nextBlock)).toBeFalse(); + }); + + it("should not chain when lower slot", () => { + const previousBlock = { + data: { + id: "1", + timestamp: slots.getSlotTime(1), + height: 1, + previousBlock: null, + }, + } as models.IBlock; + + const nextBlock = { + data: { + id: "2", + timestamp: slots.getSlotTime(0), + height: 3, + previousBlock: "1", + }, + } as models.IBlock; + + expect(isBlockChained(previousBlock, nextBlock)).toBeFalse(); + }); +}); diff --git a/packages/core-blockchain/__tests__/utils/tick-sync-tracker.test.ts b/packages/core-blockchain/__tests__/utils/tick-sync-tracker.test.ts new file mode 100644 index 0000000000..6c41cf973a --- /dev/null +++ b/packages/core-blockchain/__tests__/utils/tick-sync-tracker.test.ts @@ -0,0 +1,63 @@ +import "jest-extended"; + +const info = jest.fn(); +jest.mock("@arkecosystem/core-container", () => { + return { + app: { + resolvePlugin: name => ({ + info, + getNetworkHeight: () => 120, + }), + }, + }; +}); + +let tickSyncTracker; +const DateBackup = Date; + +describe("tickSyncTracker", () => { + beforeEach(() => { + global.Date = DateBackup; + tickSyncTracker = require("../../src/utils").tickSyncTracker; + }); + + it("print tracker stats when percent < 100", () => { + const now = new Date(); + const nowMinus8Ms = new Date(); + nowMinus8Ms.setMilliseconds(now.getMilliseconds() - 8); + + // mocking Date to return nowMinus8Ms then now + global.Date = jest + .fn() + .mockImplementationOnce(() => nowMinus8Ms) + .mockImplementationOnce(() => now) as any; + global.Date.UTC = DateBackup.UTC; + global.Date.parse = DateBackup.parse; + global.Date.now = DateBackup.now; + + tickSyncTracker(8, 52); + + expect(info).toHaveBeenLastCalledWith("Synchronising In Progress (60 of 120 blocks - Est. 60ms)"); + }); + + it("should stop tracker when percent == 100", () => { + tickSyncTracker(60, 60); // should set tracker = null, so when we call again tickSyncTracker it is reset + + const now = new Date(); + const nowMinus8Ms = new Date(); + nowMinus8Ms.setMilliseconds(now.getMilliseconds() - 8); + + // mocking Date to return nowMinus8Ms then now + global.Date = jest + .fn() + .mockImplementationOnce(() => nowMinus8Ms) + .mockImplementationOnce(() => now) as any; + global.Date.UTC = DateBackup.UTC; + global.Date.parse = DateBackup.parse; + global.Date.now = DateBackup.now; + + tickSyncTracker(8, 12); + + expect(info).toHaveBeenLastCalledWith("Synchronising In Progress (20 of 120 blocks - Est. 100ms)"); + }); +}); diff --git a/packages/core-blockchain/jest.config.js b/packages/core-blockchain/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-blockchain/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-blockchain/lib/blockchain.js b/packages/core-blockchain/lib/blockchain.js deleted file mode 100644 index 880e7916ae..0000000000 --- a/packages/core-blockchain/lib/blockchain.js +++ /dev/null @@ -1,742 +0,0 @@ -/* eslint max-len: "off" */ -/* eslint no-await-in-loop: "off" */ - -const { slots } = require('@arkecosystem/crypto') -const { Block } = require('@arkecosystem/crypto').models -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const config = app.resolvePlugin('config') -const emitter = app.resolvePlugin('event-emitter') -const delay = require('delay') -const pluralize = require('pluralize') -const stateMachine = require('./state-machine') -const Queue = require('./queue') - -module.exports = class Blockchain { - /** - * Create a new blockchain manager instance. - * @param {Boolean} networkStart - * @return {void} - */ - constructor(networkStart) { - // flag to force a network start - this.state.networkStart = !!networkStart - - if (this.state.networkStart) { - logger.warn( - 'Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong. :warning:', - ) - logger.info('Starting Ark Core for a new world, welcome aboard :rocket:') - } - - this.actions = stateMachine.actionMap(this) - - this.__registerQueue() - } - - /** - * Dispatch an event to transition the state machine. - * @param {String} event - * @return {void} - */ - dispatch(event) { - const nextState = stateMachine.transition(this.state.blockchain, event) - - if (nextState.actions.length > 0) { - logger.debug( - `event '${event}': ${JSON.stringify( - this.state.blockchain.value, - )} -> ${JSON.stringify( - nextState.value, - )} -> actions: [${nextState.actions.map(a => a.type).join(', ')}]`, - ) - } - - this.state.blockchain = nextState - - nextState.actions.forEach(actionKey => { - const action = this.actions[actionKey] - - if (action) { - return setTimeout(() => action.call(this, event), 0) - } - - logger.error(`No action '${actionKey}' found :interrobang:`) - }) - - return nextState - } - - /** - * Start the blockchain and wait for it to be ready. - * @return {void} - */ - async start(skipStartedCheck = false) { - logger.info('Starting Blockchain Manager :chains:') - - this.dispatch('START') - - emitter.once('shutdown', () => { - this.stop() - }) - - if (skipStartedCheck || process.env.ARK_SKIP_BLOCKCHAIN_STARTED_CHECK) { - return true - } - - // TODO: this state needs to be set after state.getLastBlock() is available if ARK_ENV=test - while (!this.state.started && !this.isStopped) { - await delay(1000) - } - - return true - } - - async stop() { - if (!this.isStopped) { - logger.info('Stopping Blockchain Manager :chains:') - - this.isStopped = true - this.state.clearCheckLater() - - this.dispatch('STOP') - - this.queue.destroy() - } - } - - checkNetwork() { - throw new Error('Method [checkNetwork] not implemented!') - } - - /** - * Update network status. - * @return {void} - */ - async updateNetworkStatus() { - return this.p2p.updateNetworkStatus() - } - - /** - * Rebuild N blocks in the blockchain. - * @param {Number} nblocks - * @return {void} - */ - rebuild(nblocks) { - throw new Error('Method [rebuild] not implemented!') - } - - /** - * Reset the state of the blockchain. - * @return {void} - */ - resetState() { - this.queue.pause() - this.queue.clear() - - this.state.reset() - } - - /** - * Hand the given transactions to the transaction handler. - * @param {Array} transactions - * @return {void} - */ - async postTransactions(transactions) { - logger.info( - `Received ${transactions.length} new ${pluralize( - 'transaction', - transactions.length, - )} :moneybag:`, - ) - - await this.transactionPool.addTransactions(transactions) - } - - /** - * Push a block to the process queue. - * @param {Block} block - * @return {void} - */ - queueBlock(block) { - logger.info( - `Received new block at height ${block.height.toLocaleString()} with ${pluralize( - 'transaction', - block.numberOfTransactions, - true, - )} from ${block.ip}`, - ) - - const currentSlot = slots.getSlotNumber(); - const blockSlot = slots.getSlotNumber(block.timestamp); - - if (blockSlot > currentSlot) { - logger.info(`Block disregarded because the block takes a future slot.`) - return; - } - - if ( - this.state.started && - this.state.blockchain.value === 'idle' && - !this.state.forked - ) { - this.dispatch('NEWBLOCK') - - this.processQueue.push(block) - this.state.lastDownloadedBlock = new Block(block) - } else { - logger.info( - `Block disregarded because blockchain is ${ - this.state.forked ? 'forked' : 'not ready' - } :exclamation:`, - ) - } - } - - /** - * Rollback all blocks up to the previous round. - * @return {void} - */ - async rollbackCurrentRound() { - const height = this.state.getLastBlock().data.height - const maxDelegates = config.getConstants(height).activeDelegates - const previousRound = Math.floor((height - 1) / maxDelegates) - - if (previousRound < 2) { - return - } - - const newHeight = previousRound * maxDelegates - const blocksToRemove = await this.database.getBlocks( - newHeight, - height - newHeight - 1, - ) - const deleteLastBlock = async () => { - const lastBlock = this.state.getLastBlock() - await this.database.enqueueDeleteBlock(lastBlock) - - const newLastBlock = new Block(blocksToRemove.pop()) - - this.state.setLastBlock(newLastBlock) - this.state.lastDownloadedBlock = newLastBlock - } - - logger.info( - `Removing ${pluralize( - 'block', - height - newHeight, - true, - )} to reset current round :warning:`, - ) - - let count = 0 - const max = this.state.getLastBlock().data.height - newHeight - - while (this.state.getLastBlock().data.height >= newHeight + 1) { - const removalBlockId = this.state.getLastBlock().data.id - const removalBlockHeight = this.state - .getLastBlock() - .data.height.toLocaleString() - logger.printTracker( - 'Removing block', - count++, - max, - `ID: ${removalBlockId}, height: ${removalBlockHeight}`, - ) - - await deleteLastBlock() - } - - // Commit delete blocks - await this.database.commitQueuedQueries() - - logger.stopTracker(`${pluralize('block', max, true)} removed`, count, max) - - await this.database.deleteRound(previousRound + 1) - } - - /** - * Remove N number of blocks. - * @param {Number} nblocks - * @return {void} - */ - async removeBlocks(nblocks) { - const blocksToRemove = await this.database.getBlocks( - this.state.getLastBlock().data.height - nblocks, - nblocks - 1, - ) - - const revertLastBlock = async () => { - const lastBlock = this.state.getLastBlock() - - // TODO: if revertBlock Failed, it might corrupt the database because one block could be left stored - await this.database.revertBlock(lastBlock) - this.database.enqueueDeleteBlock(lastBlock) - - if (this.transactionPool) { - await this.transactionPool.addTransactions(lastBlock.transactions) - } - - const newLastBlock = new Block(blocksToRemove.pop()) - - this.state.setLastBlock(newLastBlock) - this.state.lastDownloadedBlock = newLastBlock - } - - const __removeBlocks = async numberOfBlocks => { - if (numberOfBlocks < 1) { - return - } - - logger.info( - `Undoing block ${this.state - .getLastBlock() - .data.height.toLocaleString()}`, - ) - - await revertLastBlock() - await __removeBlocks(numberOfBlocks - 1) - } - - const lastBlock = this.state.getLastBlock() - if (nblocks >= lastBlock.data.height) { - nblocks = lastBlock.data.height - 1 - } - - const resetHeight = lastBlock.data.height - nblocks - logger.info( - `Removing ${pluralize( - 'block', - nblocks, - true, - )}. Reset to height ${resetHeight.toLocaleString()}`, - ) - - this.queue.pause() - this.queue.clear() - - this.state.lastDownloadedBlock = lastBlock - - await __removeBlocks(nblocks) - - // Commit delete blocks - await this.database.commitQueuedQueries() - - this.queue.resume() - } - - /** - * Remove the top blocks from database. - * NOTE: Only used when trying to restore database integrity. - * @param {Number} count - * @return {void} - */ - async removeTopBlocks(count) { - const blocks = await this.database.getTopBlocks(count) - - logger.info( - `Removing ${pluralize( - 'block', - blocks.length, - true, - )} from height ${blocks[0].height.toLocaleString()}`, - ) - - for (let block of blocks) { - block = new Block(block) - - this.database.enqueueDeleteRound(block.data.height) - this.database.enqueueDeleteBlock(block) - } - - await this.database.commitQueuedQueries() - } - - /** - * Hande a block during a rebuild. - * NOTE: We should be sure this is fail safe (ie callback() is being called only ONCE) - * @param {Block} block - * @param {Function} callback - * @return {Object} - */ - async rebuildBlock(block, callback) { - const lastBlock = this.state.getLastBlock() - - if (block.verification.verified) { - if (this.__isChained(lastBlock, block)) { - // save block on database - this.database.enqueueSaveBlock(block) - - // committing to db every 20,000 blocks - if (block.data.height % 20000 === 0) { - await this.database.commitQueuedQueries() - } - - this.state.setLastBlock(block) - - return callback() - } - if (block.data.height > lastBlock.data.height + 1) { - this.state.lastDownloadedBlock = lastBlock - return callback() - } - if ( - block.data.height < lastBlock.data.height || - (block.data.height === lastBlock.data.height && - block.data.id === lastBlock.data.id) - ) { - this.state.lastDownloadedBlock = lastBlock - return callback() - } - this.state.lastDownloadedBlock = lastBlock - logger.info( - `Block ${block.data.height.toLocaleString()} disregarded because on a fork :knife_fork_plate:`, - ) - return callback() - } - logger.warn( - `Block ${block.data.height.toLocaleString()} disregarded because verification failed :scroll:`, - ) - logger.warn(block.verification) - return callback() - } - - /** - * Process the given block. - * NOTE: We should be sure this is fail safe (ie callback() is being called only ONCE) - * @param {Block} block - * @param {Function} callback - * @return {(Function|void)} - */ - async processBlock(block, callback) { - if (!block.verification.verified) { - logger.warn( - `Block ${block.data.height.toLocaleString()} disregarded because verification failed :scroll:`, - ) - - this.transactionPool.purgeSendersWithInvalidTransactions(block) - this.state.lastDownloadedBlock = this.state.getLastBlock() - return callback() - } - - try { - if (this.__isChained(this.state.getLastBlock(), block)) { - await this.acceptChainedBlock(block) - this.state.setLastBlock(block) - } else { - await this.manageUnchainedBlock(block) - } - } catch (error) { - logger.error(`Refused new block ${JSON.stringify(block.data)}`) - logger.debug(error.stack) - - this.transactionPool.purgeBlock(block) - - // Only fork when the block generator is an active delegate - if (error.message !== "inactive generator") { - this.dispatch('FORK'); - } - - return callback() - } - - try { - // broadcast only current block - const blocktime = config.getConstants(block.data.height).blocktime - if (slots.getSlotNumber() * blocktime <= block.data.timestamp) { - this.p2p.broadcastBlock(block) - } - } catch (error) { - logger.warn( - `Can't properly broadcast block ${block.data.height.toLocaleString()}`, - ) - logger.debug(error.stack) - } - - return callback() - } - - /** - * Accept a new chained block. - * @param {Block} block - * @param {Object} state - * @return {void} - */ - async acceptChainedBlock(block) { - await this.database.applyBlock(block) - await this.database.saveBlock(block) - - // Check if we recovered from a fork - if ( - this.state.forked && - this.state.forkedBlock.height === block.data.height - ) { - logger.info('Successfully recovered from fork :star2:') - this.state.forked = false - this.state.forkedBlock = null - } - - if (this.transactionPool) { - try { - this.transactionPool.acceptChainedBlock(block) - } catch (error) { - logger.warn('Issue applying block to transaction pool') - logger.debug(error.stack) - } - } - } - - /** - * Manage a block that is out of order. - * @param {Block} block - * @param {Object} state - * @return {void} - */ - async manageUnchainedBlock(block) { - const lastBlock = this.state.getLastBlock() - - if (block.data.height > lastBlock.data.height + 1) { - logger.debug( - `Blockchain not ready to accept new block at height ${block.data.height.toLocaleString()}. Last block: ${lastBlock.data.height.toLocaleString()} :warning:`, - ) - this.state.lastDownloadedBlock = lastBlock - } else if (block.data.height < lastBlock.data.height) { - logger.debug( - `Block ${block.data.height.toLocaleString()} disregarded because already in blockchain :warning:`, - ) - } else if ( - block.data.height === lastBlock.data.height && - block.data.id === lastBlock.data.id - ) { - logger.debug( - `Block ${block.data.height.toLocaleString()} just received :chains:`, - ) - } else if (block.data.timestamp < lastBlock.data.timestamp) { - logger.debug( - `Block ${block.data.height.toLocaleString()} disregarded because it has a lower timestamp than the last block :bangbang:` - ) - } else { - const isValid = await this.database.validateForkedBlock(block) - - if (isValid) { - this.dispatch('FORK') - } else { - this.state.lastDownloadedBlock = lastBlock; - - logger.info( - `Forked block disregarded because it is not allowed to forge. Caused by delegate: ${ - block.data.generatorPublicKey - } :bangbang:`, - ) - } - } - } - - /** - * Called by forger to wake up and sync with the network. - * It clears the checkLaterTimeout if set. - * @param {Number} blockSize - * @param {Boolean} forForging - * @return {Object} - */ - forceWakeup() { - this.state.clearCheckLater() - this.dispatch('WAKEUP') - } - - /** - * Get unconfirmed transactions for the specified block size. - * @param {Number} blockSize - * @param {Boolean} forForging - * @return {Object} - */ - getUnconfirmedTransactions(blockSize) { - const transactions = this.transactionPool.getTransactionsForForging( - blockSize, - ) - - return { - transactions, - poolSize: this.transactionPool.getPoolSize(), - count: transactions ? transactions.length : -1, - } - } - - /** - * Determine if the blockchain is synced. - * @param {Block} [block=getLastBlock()] block - * @return {Boolean} - */ - isSynced(block) { - if (!this.p2p.hasPeers()) { - return true - } - - block = block || this.getLastBlock() - - return ( - slots.getTime() - block.data.timestamp < - 3 * config.getConstants(block.data.height).blocktime - ) - } - - /** - * Determine if the blockchain is synced after a rebuild. - * @param {Block} block - * @return {Boolean} - */ - isRebuildSynced(block) { - if (!this.p2p.hasPeers()) { - return true - } - - block = block || this.getLastBlock() - - const remaining = slots.getTime() - block.data.timestamp - logger.info(`Remaining block timestamp ${remaining} :hourglass:`) - - // stop fast rebuild 7 days before the last network block - return slots.getTime() - block.data.timestamp < 3600 * 24 * 7 - // return slots.getTime() - block.data.timestamp < 100 * config.getConstants(block.data.height).blocktime - } - - /** - * Get the last block of the blockchain. - * @return {Object} - */ - getLastBlock() { - return this.state.getLastBlock() - } - - /** - * Get the last height of the blockchain. - * @return {Object} - */ - getLastHeight() { - return this.state.getLastBlock().data.height - } - - /** - * Get the last downloaded block of the blockchain. - * @return {Object} - */ - getLastDownloadedBlock() { - return this.state.lastDownloadedBlock - } - - /** - * Get the block ping. - * @return {Object} - */ - getBlockPing() { - return this.state.blockPing - } - - /** - * Ping a block. - * @return {Object} - */ - pingBlock(incomingBlock) { - return this.state.pingBlock(incomingBlock) - } - - /** - * Push ping block. - * @return {Object} - */ - pushPingBlock(block) { - this.state.pushPingBlock(block) - } - - /** - * Get the list of events that are available. - * @return {Array} - */ - getEvents() { - return [ - 'block.applied', - 'block.forged', - 'block.reverted', - 'delegate.registered', - 'delegate.resigned', - 'forger.failed', - 'forger.missing', - 'forger.started', - 'peer.added', - 'peer.removed', - 'round.created', - 'state:started', - 'transaction.applied', - 'transaction.expired', - 'transaction.forged', - 'transaction.reverted', - 'wallet.saved', - 'wallet.created.cold', - ] - } - - /** - * Get the state of the blockchain. - * @return {StateStorage} - */ - get state() { - return stateMachine.state - } - - /** - * Get the network (p2p) interface. - * @return {P2PInterface} - */ - get p2p() { - return app.resolvePlugin('p2p') - } - - /** - * Get the transaction handler. - * @return {TransactionPool} - */ - get transactionPool() { - return app.resolvePlugin('transactionPool') - } - - /** - * Get the database connection. - * @return {ConnectionInterface} - */ - get database() { - return app.resolvePlugin('database') - } - - /** - * Check if the given block is in order. - * @param {Block} previousBlock - * @param {Block} nextBlock - * @return {Boolean} - */ - __isChained(previousBlock, nextBlock) { - const followsPrevious = - nextBlock.data.previousBlock === previousBlock.data.id - const isPlusOne = nextBlock.data.height === previousBlock.data.height + 1 - - const previousSlot = slots.getSlotNumber(previousBlock.data.timestamp); - const nextSlot = slots.getSlotNumber(nextBlock.data.timestamp); - const isAfterLastSlot = previousSlot < nextSlot; - - return followsPrevious && isPlusOne && isAfterLastSlot - } - - /** - * Register the block queue. - * @return {void} - */ - __registerQueue() { - this.queue = new Queue(this, { - process: 'PROCESSFINISHED', - rebuild: 'REBUILDFINISHED', - }) - - this.processQueue = this.queue.process - this.rebuildQueue = this.queue.rebuild - } -} diff --git a/packages/core-blockchain/lib/defaults.js b/packages/core-blockchain/lib/defaults.js deleted file mode 100644 index 24f90de245..0000000000 --- a/packages/core-blockchain/lib/defaults.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - fastRebuild: false, - databaseRollback: { - maxBlockRewind: 10000, - steps: 1000, - }, - state: { - maxLastBlocks: 100, - maxLastTransactionIds: 10000, - }, -} diff --git a/packages/core-blockchain/lib/index.js b/packages/core-blockchain/lib/index.js deleted file mode 100644 index 54d667b686..0000000000 --- a/packages/core-blockchain/lib/index.js +++ /dev/null @@ -1,32 +0,0 @@ -const { asValue } = require('awilix') -const Blockchain = require('./blockchain') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'blockchain', - async register(container, options) { - const blockchain = new Blockchain(options.networkStart) - - container.register('state', asValue(require('./state-storage'))) - - if (!process.env.ARK_SKIP_BLOCKCHAIN) { - await blockchain.start() - } - - return blockchain - }, - async deregister(container, options) { - await container.resolvePlugin('blockchain').stop() - }, -} - -/** - * Access to the state. - * @type {StateStorage} - */ -exports.state = require('./state-storage') diff --git a/packages/core-blockchain/lib/machines/actions/fork.js b/packages/core-blockchain/lib/machines/actions/fork.js deleted file mode 100644 index 87d90d65f1..0000000000 --- a/packages/core-blockchain/lib/machines/actions/fork.js +++ /dev/null @@ -1,61 +0,0 @@ -module.exports = { - initial: 'analysing', - states: { - analysing: { - onEntry: ['analyseFork'], - on: { - REBUILD: 'revertBlocks', - NOFORK: 'exit', - }, - }, - network: { - onEntry: ['checkNetwork'], - /* these transitions are not used yet (TODO?) - on: { - SUCCESS: 'blockchain', - FAILURE: 'reset' - } - */ - }, - revertBlocks: {}, - exit: { - onEntry: ['forkRecovered'], - }, - }, -} - -// const fork = { -// initial: 'network', -// states: { -// network: { -// onEntry: ['checkNetwork'], -// on: { -// SUCCESS: 'blockchain', -// FAILURE: 'reset' -// } -// }, -// blockchain: { -// onEntry: ['removeBlocks'], -// on: { -// SUCCESS: 'wallets', -// FAILURE: 'reset' -// } -// }, -// wallets: { -// onEntry: ['rebuildWallets'], -// on: { -// SUCCESS: 'success', -// FAILURE: 'reset' -// } -// }, -// reset: { -// onEntry: ['resetNode'], -// on: { -// RESET: 'success', -// FAILURE: 'reset' -// } -// }, -// success: { -// } -// } -// } diff --git a/packages/core-blockchain/lib/machines/actions/rebuild-from-network.js b/packages/core-blockchain/lib/machines/actions/rebuild-from-network.js deleted file mode 100644 index afd3749fc4..0000000000 --- a/packages/core-blockchain/lib/machines/actions/rebuild-from-network.js +++ /dev/null @@ -1,52 +0,0 @@ -module.exports = { - initial: 'rebuilding', - states: { - rebuilding: { - onEntry: ['checkLastDownloadedBlockSynced'], - on: { - SYNCED: 'waitingFinished', - NOTSYNCED: 'rebuildBlocks', - PAUSED: 'rebuildPaused', - }, - }, - idle: { - on: { - DOWNLOADED: 'rebuildBlocks', - }, - }, - rebuildBlocks: { - onEntry: ['rebuildBlocks'], - on: { - DOWNLOADED: 'rebuilding', - NOBLOCK: 'rebuilding', - }, - }, - waitingFinished: { - on: { - REBUILDFINISHED: 'rebuildFinished', - }, - }, - rebuildFinished: { - onEntry: ['rebuildFinished'], - on: { - PROCESSFINISHED: 'processFinished', - }, - }, - rebuildPaused: { - onEntry: ['downloadPaused'], - on: { - REBUILDFINISHED: 'processFinished', - }, - }, - processFinished: { - onEntry: ['checkRebuildBlockSynced'], - on: { - SYNCED: 'end', - NOTSYNCED: 'rebuildBlocks', - }, - }, - end: { - onEntry: ['rebuildingComplete'], - }, - }, -} diff --git a/packages/core-blockchain/lib/machines/actions/sync-with-network.js b/packages/core-blockchain/lib/machines/actions/sync-with-network.js deleted file mode 100644 index eb81479e88..0000000000 --- a/packages/core-blockchain/lib/machines/actions/sync-with-network.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = { - initial: 'syncing', - states: { - syncing: { - onEntry: ['checkLastDownloadedBlockSynced'], - on: { - SYNCED: 'downloadFinished', - NOTSYNCED: 'downloadBlocks', - PAUSED: 'downloadPaused', - NETWORKHALTED: 'end', - }, - }, - idle: { - on: { - DOWNLOADED: 'downloadBlocks', - }, - }, - downloadBlocks: { - onEntry: ['downloadBlocks'], - on: { - DOWNLOADED: 'syncing', - NOBLOCK: 'syncing', - }, - }, - downloadFinished: { - onEntry: ['downloadFinished'], - on: { - PROCESSFINISHED: 'processFinished', - }, - }, - downloadPaused: { - onEntry: ['downloadPaused'], - on: { - PROCESSFINISHED: 'processFinished', - }, - }, - processFinished: { - onEntry: ['checkLastBlockSynced'], - on: { - SYNCED: 'end', - NOTSYNCED: 'downloadBlocks', - }, - }, - end: { - onEntry: ['syncingComplete'], - }, - }, -} diff --git a/packages/core-blockchain/lib/machines/blockchain.js b/packages/core-blockchain/lib/machines/blockchain.js deleted file mode 100644 index ed8a167e7a..0000000000 --- a/packages/core-blockchain/lib/machines/blockchain.js +++ /dev/null @@ -1,89 +0,0 @@ -const Machine = require('xstate').Machine -const syncWithNetwork = require('./actions/sync-with-network') -const rebuildFromNetwork = require('./actions/rebuild-from-network') -const fork = require('./actions/fork') - -module.exports = Machine({ - key: 'blockchain', - initial: 'uninitialised', - states: { - uninitialised: { - on: { - START: 'init', - STOP: 'stopped', - }, - }, - init: { - onEntry: ['init'], - on: { - REBUILD: 'rebuild', - NETWORKSTART: 'idle', - STARTED: 'syncWithNetwork', - ROLLBACK: 'rollback', - FAILURE: 'exit', - STOP: 'stopped', - }, - }, - rebuild: { - on: { - REBUILDCOMPLETE: 'syncWithNetwork', - FORK: 'fork', - TEST: 'syncWithNetwork', - STOP: 'stopped', - }, - ...rebuildFromNetwork, - }, - syncWithNetwork: { - on: { - TEST: 'idle', - SYNCFINISHED: 'idle', - FORK: 'fork', - STOP: 'stopped', - }, - ...syncWithNetwork, - }, - idle: { - onEntry: ['checkLater', 'blockchainReady'], - on: { - WAKEUP: 'syncWithNetwork', - NEWBLOCK: 'newBlock', - STOP: 'stopped', - }, - }, - newBlock: { - on: { - PROCESSFINISHED: 'idle', - FORK: 'fork', - STOP: 'stopped', - }, - }, - fork: { - onEntry: ['startForkRecovery'], - on: { - SUCCESS: 'syncWithNetwork', - FAILURE: 'exit', - STOP: 'stopped', - }, - ...fork, - }, - rollback: { - onEntry: ['rollbackDatabase'], - on: { - SUCCESS: 'init', - FAILURE: 'exit', - STOP: 'stopped', - }, - }, - /** - * This state should be used for stopping the blockchain on purpose, not as - * a result of critical errors. In those cases, using the `exit` state would - * be a better option - */ - stopped: { - onEntry: ['stopped'], - }, - exit: { - onEntry: ['exitApp'], - }, - }, -}) diff --git a/packages/core-blockchain/lib/queue/index.js b/packages/core-blockchain/lib/queue/index.js deleted file mode 100644 index e132cfebee..0000000000 --- a/packages/core-blockchain/lib/queue/index.js +++ /dev/null @@ -1,47 +0,0 @@ -const ProcessQueue = require('./process') -const RebuildQueue = require('./rebuild') - -module.exports = class Queue { - /** - * Create an instance of the queue. - * @param {Blockchain} blockchain - * @param {Object} events - * @return {void} - */ - constructor(blockchain, events) { - this.process = new ProcessQueue(blockchain, events.process) - this.rebuild = new RebuildQueue(blockchain, events.rebuild) - } - - /** - * Pause all queues. - * @return {void} - */ - pause() { - this.rebuild.pause() - this.process.pause() - } - - /** - * Flush all queues. - * @return {void} - */ - clear() { - this.rebuild.clear() - this.process.clear() - } - - /** - * Resue all queues. - * @return {void} - */ - resume() { - this.rebuild.resume() - this.process.resume() - } - - destroy() { - this.rebuild.destroy() - this.process.destroy() - } -} diff --git a/packages/core-blockchain/lib/queue/interface.js b/packages/core-blockchain/lib/queue/interface.js deleted file mode 100644 index e2731b7498..0000000000 --- a/packages/core-blockchain/lib/queue/interface.js +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = class QueueInterface { - /** - * Create an instance of the process queue. - * @param {Blockchain} blockchain - * @param {String} event - * @return {void} - */ - constructor(blockchain, event) { - this.blockchain = blockchain - this.event = event - } - - /** - * Drain the queue. - * @return {void} - */ - drain() { - this.queue.drain = () => this.blockchain.dispatch(this.event) - } - - /** - * Pause the queue. - * @return {void} - */ - pause() { - return this.queue.pause() - } - - /** - * Flush the queue. - * @return {void} - */ - clear() { - return this.queue.remove(() => true) - } - - /** - * Resume the queue. - * @return {void} - */ - resume() { - return this.queue.resume() - } - - /** - * Remove the item from the queue. - * @return {void} - */ - remove(item) { - return this.queue.remove(item) - } - - /** - * Push the item to the queue. - * @param {Function} callback - * @return {void} - */ - push(callback) { - return this.queue.push(callback) - } - - /** - * Get the length of the queue. - * @return {void} - */ - length() { - return this.queue.length() - } - - destroy() { - return this.queue.kill() - } -} diff --git a/packages/core-blockchain/lib/queue/process.js b/packages/core-blockchain/lib/queue/process.js deleted file mode 100644 index b2e3fc8d13..0000000000 --- a/packages/core-blockchain/lib/queue/process.js +++ /dev/null @@ -1,29 +0,0 @@ -const async = require('async') -const logger = require('@arkecosystem/core-container').resolvePlugin('logger') -const { Block } = require('@arkecosystem/crypto').models -const QueueInterface = require('./interface') - -module.exports = class ProcessQueue extends QueueInterface { - /** - * Create an instance of the process queue. - * @param {Blockchain} blockchain - * @return {void} - */ - constructor(blockchain, event) { - super(blockchain, event) - - this.queue = async.queue((block, cb) => { - try { - return blockchain.processBlock(new Block(block), cb) - } catch (error) { - logger.error( - `Failed to process block in ProcessQueue: ${block.height.toLocaleString()}`, - ) - logger.error(error.stack) - return cb() - } - }, 1) - - this.drain() - } -} diff --git a/packages/core-blockchain/lib/queue/rebuild.js b/packages/core-blockchain/lib/queue/rebuild.js deleted file mode 100644 index 5d75f81ec3..0000000000 --- a/packages/core-blockchain/lib/queue/rebuild.js +++ /dev/null @@ -1,29 +0,0 @@ -const async = require('async') -const logger = require('@arkecosystem/core-container').resolvePlugin('logger') -const { Block } = require('@arkecosystem/crypto').models -const QueueInterface = require('./interface') - -module.exports = class RebuildQueue extends QueueInterface { - /** - * Create an instance of the process queue. - * @param {Blockchain} blockchain - * @return {void} - */ - constructor(blockchain, event) { - super(blockchain, event) - - this.queue = async.queue((block, cb) => { - if (this.queue.paused) return cb() - try { - return blockchain.rebuildBlock(new Block(block), cb) - } catch (error) { - logger.error( - `Failed to rebuild block in RebuildQueue: ${block.height.toLocaleString()}`, - ) - return cb() - } - }, 1) - - this.drain() - } -} diff --git a/packages/core-blockchain/lib/state-machine.js b/packages/core-blockchain/lib/state-machine.js deleted file mode 100644 index 2e7ccf5979..0000000000 --- a/packages/core-blockchain/lib/state-machine.js +++ /dev/null @@ -1,448 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const emitter = app.resolvePlugin('event-emitter') -const logger = app.resolvePlugin('logger') - -const { slots } = require('@arkecosystem/crypto') -const { Block } = require('@arkecosystem/crypto').models -const { roundCalculator } = require('@arkecosystem/core-utils') - -const pluralize = require('pluralize') -const tickSyncTracker = require('./utils/tick-sync-tracker') -const blockchainMachine = require('./machines/blockchain') -const state = require('./state-storage') - -/** - * @type {StateStorage} - */ -blockchainMachine.state = state - -/** - * The blockchain actions. - * @param {Blockchain} blockchain - * @return {Object} - */ -blockchainMachine.actionMap = blockchain => ({ - blockchainReady: () => { - if (!state.started) { - state.started = true - emitter.emit('state:started', true) - } - }, - - checkLater() { - if (!blockchain.isStopped && !state.checkLaterTimeout) { - state.checkLaterTimeout = setTimeout(() => { - state.checkLaterTimeout = null - return blockchain.dispatch('WAKEUP') - }, 60000) - } - }, - - checkLastBlockSynced() { - return blockchain.dispatch(blockchain.isSynced() ? 'SYNCED' : 'NOTSYNCED') - }, - - checkRebuildBlockSynced() { - return blockchain.dispatch( - blockchain.isRebuildSynced() ? 'SYNCED' : 'NOTSYNCED', - ) - }, - - async checkLastDownloadedBlockSynced() { - let event = 'NOTSYNCED' - logger.debug( - `Queued blocks (rebuild: ${blockchain.rebuildQueue.length()} process: ${blockchain.processQueue.length()})`, - ) - - if ( - blockchain.rebuildQueue.length() > 10000 || - blockchain.processQueue.length() > 10000 - ) { - event = 'PAUSED' - } - - // tried to download but no luck after 5 tries (looks like network missing blocks) - if (state.noBlockCounter > 5) { - // TODO: make this dynamic in 2.1 - logger.info( - 'Tried to sync 5 times to different nodes, looks like the network is missing blocks :umbrella:', - ) - - state.noBlockCounter = 0 - event = 'NETWORKHALTED' - - if (state.p2pUpdateCounter + 1 > 3) { - logger.info('Network keeps missing blocks. :umbrella:') - - const result = await blockchain.p2p.updatePeersOnMissingBlocks() - if (result === 'rollback') { - event = 'FORK' - } - - state.p2pUpdateCounter = 0 - } else { - state.p2pUpdateCounter++ - } - } - - if (blockchain.isSynced(state.lastDownloadedBlock)) { - state.noBlockCounter = 0 - state.p2pUpdateCounter = 0 - - event = 'SYNCED' - } - - if (state.networkStart) { - event = 'SYNCED' - } - - if (process.env.ARK_ENV === 'test') { - event = 'TEST' - } - - blockchain.dispatch(event) - }, - - downloadFinished() { - logger.info('Block download finished :rocket:') - - if (state.networkStart) { - // next time we will use normal behaviour - state.networkStart = false - - blockchain.dispatch('SYNCFINISHED') - } else if (blockchain.rebuildQueue.length() === 0) { - blockchain.dispatch('PROCESSFINISHED') - } - }, - - async rebuildFinished() { - try { - logger.info('Blockchain rebuild finished :chains:') - - state.rebuild = false - - await blockchain.database.commitQueuedQueries() - await blockchain.rollbackCurrentRound() - await blockchain.database.buildWallets(state.getLastBlock().data.height) - await blockchain.database.saveWallets(true) - await blockchain.transactionPool.buildWallets() - - return blockchain.dispatch('PROCESSFINISHED') - } catch (error) { - logger.error(error.stack) - return blockchain.dispatch('FAILURE') - } - }, - - downloadPaused: () => logger.info('Blockchain download paused :clock1030:'), - - syncingComplete() { - logger.info('Blockchain 100% in sync :100:') - blockchain.dispatch('SYNCFINISHED') - }, - - rebuildingComplete() { - logger.info('Blockchain rebuild complete :unicorn_face:') - blockchain.dispatch('REBUILDCOMPLETE') - }, - - stopped() { - logger.info('The blockchain has been stopped :guitar:') - }, - - exitApp() { - app.forceExit( - 'Failed to startup blockchain. Exiting Ark Core! :rotating_light:', - ) - }, - - async init() { - try { - let block = await blockchain.database.getLastBlock() - - if (!block) { - logger.warn('No block found in database :hushed:') - - block = new Block(config.genesisBlock) - - if (block.data.payloadHash !== config.network.nethash) { - logger.error( - 'FATAL: The genesis block payload hash is different from configured the nethash :rotating_light:', - ) - - return blockchain.dispatch('FAILURE') - } - - await blockchain.database.saveBlock(block) - } - - if (!blockchain.restoredDatabaseIntegrity) { - logger.info('Verifying database integrity :hourglass_flowing_sand:') - - const blockchainAudit = await blockchain.database.verifyBlockchain() - if (!blockchainAudit.valid) { - logger.error('FATAL: The database is corrupted :fire:') - logger.error(JSON.stringify(blockchainAudit.errors, null, 4)) - - return blockchain.dispatch('ROLLBACK') - } - - logger.info('Verified database integrity :smile_cat:') - } else { - logger.info( - 'Skipping database integrity check after successful database recovery :smile_cat:', - ) - } - - // only genesis block? special case of first round needs to be dealt with - if (block.data.height === 1) { - await blockchain.database.deleteRound(1) - } - - /** ******************************* - * state machine data init * - ******************************* */ - const constants = config.getConstants(block.data.height) - state.setLastBlock(block) - state.lastDownloadedBlock = block - - if (state.networkStart) { - await blockchain.database.buildWallets(block.data.height) - await blockchain.database.saveWallets(true) - await blockchain.database.applyRound(block.data.height) - await blockchain.transactionPool.buildWallets() - - return blockchain.dispatch('STARTED') - } - - state.rebuild = - slots.getTime() - block.data.timestamp > - (constants.activeDelegates + 1) * constants.blocktime - // no fast rebuild if in last week - state.fastRebuild = - slots.getTime() - block.data.timestamp > 3600 * 24 * 7 && - !!app.resolveOptions('blockchain').fastRebuild - - if (process.env.NODE_ENV === 'test') { - logger.verbose( - 'TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY. :bangbang:', - ) - - state.setLastBlock(new Block(config.genesisBlock)) - await blockchain.database.buildWallets(block.data.height) - - return blockchain.dispatch('STARTED') - } - - logger.info(`Fast rebuild: ${state.fastRebuild}`) - logger.info( - `Last block in database: ${block.data.height.toLocaleString()}`, - ) - - if (state.fastRebuild) { - return blockchain.dispatch('REBUILD') - } - - // removing blocks up to the last round to compute active delegate list later if needed - const activeDelegates = await blockchain.database.getActiveDelegates( - block.data.height, - ) - - if (!activeDelegates) { - await blockchain.rollbackCurrentRound() - } - - /** ******************************* - * database init * - ******************************* */ - // SPV rebuild - const verifiedWalletsIntegrity = await blockchain.database.buildWallets( - block.data.height, - ) - if (!verifiedWalletsIntegrity && block.data.height > 1) { - logger.warn( - 'Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown. :hammer:', - ) - await blockchain.database.saveWallets(true) - } - - // NOTE: if the node is shutdown between round, the round has already been applied - if (roundCalculator.isNewRound(block.data.height + 1)) { - const { round } = roundCalculator.calculateRound(block.data.height + 1) - - logger.info( - `New round ${round.toLocaleString()} detected. Cleaning calculated data before restarting!`, - ) - - await blockchain.database.deleteRound(round) - } - - await blockchain.database.applyRound(block.data.height) - await blockchain.transactionPool.buildWallets() - - return blockchain.dispatch('STARTED') - } catch (error) { - logger.error(error.stack) - - return blockchain.dispatch('FAILURE') - } - }, - - async rebuildBlocks() { - const lastBlock = state.lastDownloadedBlock || state.getLastBlock() - const blocks = await blockchain.p2p.downloadBlocks(lastBlock.data.height) - - tickSyncTracker(blocks.length, lastBlock.data.height) - - if (!blocks || blocks.length === 0) { - logger.info('No new blocks found on this peer') - - blockchain.dispatch('NOBLOCK') - } else { - logger.info( - `Downloaded ${blocks.length} new ${pluralize( - 'block', - blocks.length, - )} accounting for a total of ${pluralize( - 'transaction', - blocks.reduce((sum, b) => sum + b.numberOfTransactions, 0), - true, - )}`, - ) - - if (blocks.length && blocks[0].previousBlock === lastBlock.data.id) { - state.lastDownloadedBlock = { data: blocks.slice(-1)[0] } - blockchain.rebuildQueue.push(blocks) - blockchain.dispatch('DOWNLOADED') - } else { - logger.warn( - `Downloaded block not accepted: ${JSON.stringify(blocks[0])}`, - ) - logger.warn(`Last block: ${JSON.stringify(lastBlock.data)}`) - - // disregard the whole block list - blockchain.dispatch('NOBLOCK') - } - } - }, - - async downloadBlocks() { - const lastBlock = state.lastDownloadedBlock || state.getLastBlock() - const blocks = await blockchain.p2p.downloadBlocks(lastBlock.data.height) - - if (blockchain.isStopped) { - return - } - - if (!blocks || blocks.length === 0) { - logger.info('No new block found on this peer') - - state.noBlockCounter++ - - blockchain.dispatch('NOBLOCK') - } else { - logger.info( - `Downloaded ${blocks.length} new ${pluralize( - 'block', - blocks.length, - )} accounting for a total of ${pluralize( - 'transaction', - blocks.reduce((sum, b) => sum + b.numberOfTransactions, 0), - true, - )}`, - ) - - if (blocks.length && blocks[0].previousBlock === lastBlock.data.id) { - state.noBlockCounter = 0 - state.p2pUpdateCounter = 0 - state.lastDownloadedBlock = { data: blocks.slice(-1)[0] } - - blockchain.processQueue.push(blocks) - - blockchain.dispatch('DOWNLOADED') - } else { - state.lastDownloadedBlock = lastBlock - - logger.warn( - `Downloaded block not accepted: ${JSON.stringify(blocks[0])}`, - ) - logger.warn(`Last block: ${JSON.stringify(lastBlock.data)}`) - - state.forked = true - state.forkedBlock = blocks[0] - - // disregard the whole block list - blockchain.dispatch('FORK') - } - } - }, - - async analyseFork() { - logger.info('Analysing fork :mag:') - }, - - async startForkRecovery() { - logger.info('Starting fork recovery :fork_and_knife:') - - await blockchain.database.commitQueuedQueries() - - let random = Math.floor(4 / Math.random()) - - if (random > 102) { - random = 102 - } - - await blockchain.removeBlocks(random) - - logger.info(`Removed ${pluralize('block', random, true)} :wastebasket:`) - - await blockchain.transactionPool.buildWallets() - await blockchain.p2p.refreshPeersAfterFork() - - blockchain.dispatch('SUCCESS') - }, - - async rollbackDatabase() { - logger.info('Trying to restore database integrity :fire_engine:') - - const { maxBlockRewind, steps } = app.resolveOptions( - 'blockchain', - ).databaseRollback - let blockchainAudit - - for (let i = maxBlockRewind; i >= 0; i -= steps) { - await blockchain.removeTopBlocks(steps) - - blockchainAudit = await blockchain.database.verifyBlockchain() - if (blockchainAudit.valid) { - break - } - } - - if (!blockchainAudit.valid) { - // TODO: multiple attempts? rewind further? restore snapshot? - logger.error( - 'FATAL: Failed to restore database integrity :skull: :skull: :skull:', - ) - logger.error(JSON.stringify(blockchainAudit.errors, null, 4)) - blockchain.dispatch('FAILURE') - return - } - - blockchain.restoredDatabaseIntegrity = true - - const lastBlock = await blockchain.database.getLastBlock() - logger.info( - `Database integrity verified again after rollback to height ${lastBlock.data.height.toLocaleString()} :green_heart:`, - ) - - blockchain.dispatch('SUCCESS') - }, -}) - -module.exports = blockchainMachine diff --git a/packages/core-blockchain/lib/state-storage.js b/packages/core-blockchain/lib/state-storage.js deleted file mode 100644 index 92e9ddc0e3..0000000000 --- a/packages/core-blockchain/lib/state-storage.js +++ /dev/null @@ -1,254 +0,0 @@ -/* eslint max-len: "off" */ - -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const immutable = require('immutable') -const assert = require('assert') -const blockchainMachine = require('./machines/blockchain') - -// Stores the last n blocks in ascending height. The amount of last blocks -// can be configured with the option `state.maxLastBlocks`. -let _lastBlocks = immutable.OrderedMap() - -// Stores the last n incoming transaction ids. The amount of transaction ids -// can be configred with the option `state.maxLastTransactionIds`. -let _cachedTransactionIds = immutable.OrderedSet() - -// Map Block instances to block data. -const _mapToBlockData = blocks => - blocks.map(block => ({ ...block.data, transactions: block.transactions })) - -/** - * Represents an in-memory storage for state machine data. - */ -class StateStorage { - constructor() { - this.reset() - } - - /** - * Resets the state. - * @returns {void} - */ - reset() { - this.blockchain = blockchainMachine.initialState - this.lastDownloadedBlock = null - this.blockPing = null - this.started = false - this.forked = false - this.forkedBlock = null - this.rebuild = true - this.fastRebuild = false - this.checkLaterTimeout = null - this.noBlockCounter = 0 - this.p2pUpdateCounter = 0 - this.networkStart = false - - this.clear() - } - - /** - * Clear last blocks. - * @returns {void} - */ - clear() { - _lastBlocks = _lastBlocks.clear() - _cachedTransactionIds = _cachedTransactionIds.clear() - } - - /** - * Clear check later timeout. - * @returns {void} - */ - clearCheckLater() { - if (this.checkLaterTimeout) { - clearTimeout(this.checkLaterTimeout) - this.checkLaterTimeout = null - } - } - - /** - * Get the last block. - * @returns {Block|null} - */ - getLastBlock() { - return _lastBlocks.last() || null - } - - /** - * Sets the last block. - * @returns {void} - */ - setLastBlock(block) { - // Only keep blocks which are below the new block height (i.e. rollback) - if ( - _lastBlocks.last() && - _lastBlocks.last().data.height !== block.data.height - 1 - ) { - assert(block.data.height - 1 <= _lastBlocks.last().data.height) - _lastBlocks = _lastBlocks.filter(b => b.data.height < block.data.height) - } - - _lastBlocks = _lastBlocks.set(block.data.height, block) - - // Delete oldest block if size exceeds the maximum - if ( - _lastBlocks.size > - app.resolveOptions('blockchain').state.maxLastBlocks - ) { - _lastBlocks = _lastBlocks.delete(_lastBlocks.first().data.height) - } - } - - /** - * Get the last blocks. - * @returns {Array} - */ - getLastBlocks() { - return _lastBlocks - .valueSeq() - .reverse() - .toArray() - } - - /** - * Get the last blocks data. - * @returns {Seq} - */ - getLastBlocksData() { - return _mapToBlockData(_lastBlocks.valueSeq().reverse()) - } - - /** - * Get the last block ids. - * @returns {Array} - */ - getLastBlockIds() { - return _lastBlocks - .valueSeq() - .reverse() - .map(b => b.data.id) - .toArray() - } - - /** - * Get last blocks in the given height range in ascending order. - * @param {Number} start - * @param {Number} end - */ - getLastBlocksByHeight(start, end) { - end = end || start - - const blocks = _lastBlocks - .valueSeq() - .filter(block => block.data.height >= start && block.data.height <= end) - - return _mapToBlockData(blocks).toArray() - } - - /** - * Get common blocks for the given IDs. - * @returns {Array} - */ - getCommonBlocks(ids) { - return this.getLastBlocksData() - .filter(block => ids.includes(block.id)) - .toArray() - } - - /** - * Cache the ids of the given transactions. - * @param {Array} transactions - * @return Object { - * added: array of added transactions, - * notAdded: array of previously added transactions - * } - */ - cacheTransactions(transactions) { - const notAdded = [] - const added = transactions.filter(tx => { - if (_cachedTransactionIds.has(tx.id)) { - notAdded.push(tx) - return false - } - return true - }) - - _cachedTransactionIds = _cachedTransactionIds.withMutations(cache => { - added.forEach(tx => cache.add(tx.id)) - }) - - // Cap the Set of last transaction ids to maxLastTransactionIds - const limit = app.resolveOptions('blockchain').state - .maxLastTransactionIds - if (_cachedTransactionIds.size > limit) { - _cachedTransactionIds = _cachedTransactionIds.takeLast(limit) - } - - return { added, notAdded } - } - - /** - * Remove the given transaction ids from the cache. - * @param {Array} transactionIds - * @returns {void} - */ - removeCachedTransactionIds(transactionIds) { - _cachedTransactionIds = _cachedTransactionIds.subtract(transactionIds) - } - - /** - * Get cached transaction ids. - * @returns {Array} - */ - getCachedTransactionIds() { - return _cachedTransactionIds.toArray() - } - - /** - * Ping a block. - * @param {Block} incomingBlock - * @returns {Boolean} - */ - pingBlock(incomingBlock) { - if (!this.blockPing) return false - - if ( - this.blockPing.block.height === incomingBlock.height && - this.blockPing.block.id === incomingBlock.id - ) { - this.blockPing.count++ - this.blockPing.last = new Date().getTime() - - return true - } - - return false - } - - /** - * Push ping block - * @param {Block} block - * @returns {void} - */ - pushPingBlock(block) { - // logging for stats about network health - if (this.blockPing) { - logger.info( - `Block ${this.blockPing.block.height.toLocaleString()} pinged blockchain ${ - this.blockPing.count - } times`, - ) - } - - this.blockPing = { - count: 1, - first: new Date().getTime(), - last: new Date().getTime(), - block, - } - } -} - -module.exports = Object.seal(new StateStorage()) diff --git a/packages/core-blockchain/lib/utils/tick-sync-tracker.js b/packages/core-blockchain/lib/utils/tick-sync-tracker.js deleted file mode 100644 index e80ed908e8..0000000000 --- a/packages/core-blockchain/lib/utils/tick-sync-tracker.js +++ /dev/null @@ -1,65 +0,0 @@ -const prettyMs = require('pretty-ms') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -let tracker = null - -module.exports = async (blockCount, count) => { - if (!tracker) { - tracker = { - start: new Date().getTime(), - networkHeight: app.resolvePlugin('p2p').getNetworkHeight(), - blocksInitial: +count, - blocksDownloaded: +count, - blocksSession: 0, - blocksPerMillisecond: 0, - remainingInMilliseconds: 0, - percent: 0, - } - } - - // The total amount of downloaded blocks equals the current height - tracker.blocksDownloaded += +blockCount - - // The total amount of downloaded blocks downloaded since start of the current session - tracker.blocksSession = tracker.blocksDownloaded - tracker.blocksInitial - - // The number of blocks the node can download per millisecond - const diffSinceStart = new Date().getTime() - tracker.start - tracker.blocksPerMillisecond = tracker.blocksSession / diffSinceStart - - // The time left to download the missing blocks in milliseconds - tracker.remainingInMilliseconds = - (tracker.networkHeight - tracker.blocksDownloaded) / - tracker.blocksPerMillisecond - tracker.remainingInMilliseconds = Math.abs( - Math.trunc(tracker.remainingInMilliseconds), - ) - - // The percentage of total blocks that has been downloaded - tracker.percent = (tracker.blocksDownloaded * 100) / tracker.networkHeight - - if ( - tracker.percent < 100 && - Number.isFinite(tracker.remainingInMilliseconds) - ) { - const blocksDownloaded = tracker.blocksDownloaded.toLocaleString() - const networkHeight = tracker.networkHeight.toLocaleString() - const timeLeft = prettyMs(tracker.remainingInMilliseconds, { - secDecimalDigits: 0, - }) - - logger.printTracker( - 'Fast Sync', - tracker.percent, - 100, - `(${blocksDownloaded} of ${networkHeight} blocks - Est. ${timeLeft})`, - ) - } - - if (tracker.percent === 100) { - tracker = null - - logger.stopTracker('Fast Sync', 100, 100) - } -} diff --git a/packages/core-blockchain/package.json b/packages/core-blockchain/package.json index ed37563365..ed89405356 100644 --- a/packages/core-blockchain/package.json +++ b/packages/core-blockchain/package.json @@ -1,44 +1,65 @@ { - "name": "@arkecosystem/core-blockchain", - "description": "Blockchain Manager for Ark Core", - "version": "0.2.1", - "contributors": [ - "François-Xavier Thoorens ", - "Kristjan Košič ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "async": "^2.6.1", - "awilix": "^4.0.1", - "delay": "^4.1.0", - "pretty-ms": "^4.0.0", - "xstate": "^4.2.1", - "immutable": "^4.0.0-rc.12", - "pluralize": "^7.0.0" - }, - "devDependencies": { - "@arkecosystem/core-p2p": "~0.2", - "@arkecosystem/core-test-utils": "~0.2", - "axios": "^0.18.0", - "axios-mock-adapter": "^1.15.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-blockchain", + "description": "Blockchain Manager for Ark Core", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Kristjan Košič ", + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/lodash.get": "^4.4.4", + "async": "^2.6.1", + "awilix": "^4.0.1", + "delay": "^4.1.0", + "immutable": "^4.0.0-rc.12", + "lodash.get": "^4.4.2", + "pluralize": "^7.0.0", + "pretty-ms": "^4.0.0", + "xstate": "^4.2.2" + }, + "devDependencies": { + "@arkecosystem/core-p2p": "^2.1.0", + "@arkecosystem/core-test-utils": "^2.1.0", + "@types/async": "^2.0.50", + "@types/pluralize": "^0.0.29", + "@types/pretty-ms": "^4.0.0", + "axios": "^0.18.0", + "axios-mock-adapter": "^1.15.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-blockchain/src/blockchain.ts b/packages/core-blockchain/src/blockchain.ts new file mode 100644 index 0000000000..9e2dea2f8e --- /dev/null +++ b/packages/core-blockchain/src/blockchain.ts @@ -0,0 +1,619 @@ +/* tslint:disable:max-line-length */ +import { app } from "@arkecosystem/core-container"; +import { + Blockchain as blockchain, + Database, + EventEmitter, + Logger, + P2P, + TransactionPool, +} from "@arkecosystem/core-interfaces"; +import { models, slots } from "@arkecosystem/crypto"; + +import delay from "delay"; +import pluralize from "pluralize"; +import { BlockProcessor, BlockProcessorResult } from "./processor"; +import { ProcessQueue, Queue, RebuildQueue } from "./queue"; +import { stateMachine } from "./state-machine"; +import { StateStorage } from "./state-storage"; +import { isBlockChained } from "./utils"; + +const logger = app.resolvePlugin("logger"); +const config = app.getConfig(); +const emitter = app.resolvePlugin("event-emitter"); +const { Block } = models; + +export class Blockchain implements blockchain.IBlockchain { + /** + * Get the state of the blockchain. + * @return {IStateStorage} + */ + get state(): StateStorage { + return stateMachine.state; + } + + /** + * Get the network (p2p) interface. + * @return {P2PInterface} + */ + get p2p() { + return app.resolvePlugin("p2p"); + } + + /** + * Get the transaction handler. + * @return {TransactionPool} + */ + get transactionPool() { + return app.resolvePlugin("transactionPool"); + } + + /** + * Get the database connection. + * @return {ConnectionInterface} + */ + get database() { + return app.resolvePlugin("database"); + } + + public isStopped: boolean; + public options: any; + public processQueue: ProcessQueue; + public rebuildQueue: RebuildQueue; + private actions: any; + private queue: Queue; + private blockProcessor: BlockProcessor; + + /** + * Create a new blockchain manager instance. + * @param {Object} options + * @return {void} + */ + constructor(options) { + // flag to force a network start + this.state.networkStart = !!options.networkStart; + + if (this.state.networkStart) { + logger.warn( + "Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong. :warning:", + ); + logger.info("Starting Ark Core for a new world, welcome aboard :rocket:"); + } + + this.actions = stateMachine.actionMap(this); + this.blockProcessor = new BlockProcessor(this); + + this.__registerQueue(); + } + + /** + * Dispatch an event to transition the state machine. + * @param {String} event + * @return {void} + */ + public dispatch(event) { + const nextState = stateMachine.transition(this.state.blockchain, event); + + if (nextState.actions.length > 0) { + logger.debug( + `event '${event}': ${JSON.stringify(this.state.blockchain.value)} -> ${JSON.stringify( + nextState.value, + )} -> actions: [${nextState.actions.map(a => a.type).join(", ")}]`, + ); + } + + this.state.blockchain = nextState; + + nextState.actions.forEach(actionKey => { + const action = this.actions[actionKey]; + + if (action) { + setTimeout(() => action.call(this, event), 0); + } else { + logger.error(`No action '${actionKey}' found :interrobang:`); + } + }); + + return nextState; + } + + /** + * Start the blockchain and wait for it to be ready. + * @return {void} + */ + public async start(skipStartedCheck = false) { + logger.info("Starting Blockchain Manager :chains:"); + + this.dispatch("START"); + + emitter.once("shutdown", () => { + this.stop(); + }); + + if (skipStartedCheck || process.env.CORE_SKIP_BLOCKCHAIN_STARTED_CHECK) { + return true; + } + + // TODO: this state needs to be set after state.getLastBlock() is available if CORE_ENV=test + while (!this.state.started && !this.isStopped) { + await delay(1000); + } + + return true; + } + + public async stop() { + if (!this.isStopped) { + logger.info("Stopping Blockchain Manager :chains:"); + + this.isStopped = true; + this.state.clearWakeUpTimeout(); + + this.dispatch("STOP"); + + this.queue.destroy(); + } + } + + public checkNetwork() { + throw new Error("Method [checkNetwork] not implemented!"); + } + + /** + * Set wakeup timeout to check the network for new blocks. + */ + public setWakeUp() { + this.state.wakeUpTimeout = setTimeout(() => { + this.state.wakeUpTimeout = null; + return this.dispatch("WAKEUP"); + }, 60000); + } + + /** + * Reset the wakeup timeout. + */ + public resetWakeUp() { + this.state.clearWakeUpTimeout(); + this.setWakeUp(); + } + + /** + * Update network status. + * @return {void} + */ + public async updateNetworkStatus() { + return this.p2p.updateNetworkStatus(); + } + + /** + * Rebuild N blocks in the blockchain. + * @param {Number} nblocks + * @return {void} + */ + public rebuild(nblocks?: number) { + throw new Error("Method [rebuild] not implemented!"); + } + + /** + * Reset the state of the blockchain. + * @return {void} + */ + public resetState() { + this.clearAndStopQueue(); + this.state.reset(); + } + + /** + * Clear and stop the queue. + * @return {void} + */ + public clearAndStopQueue() { + this.queue.pause(); + this.queue.clear(); + } + + /** + * Hand the given transactions to the transaction handler. + * @param {Array} transactions + * @return {void} + */ + public async postTransactions(transactions) { + logger.info(`Received ${transactions.length} new ${pluralize("transaction", transactions.length)} :moneybag:`); + + await this.transactionPool.addTransactions(transactions); + } + + /** + * Push a block to the process queue. + * @param {Block} block + * @return {void} + */ + public handleIncomingBlock(block) { + logger.info( + `Received new block at height ${block.height.toLocaleString()} with ${pluralize( + "transaction", + block.numberOfTransactions, + true, + )} from ${block.ip}`, + ); + + const currentSlot = slots.getSlotNumber(); + const receivedSlot = slots.getSlotNumber(block.timestamp); + if (receivedSlot > currentSlot) { + logger.info(`Discarded block ${block.height.toLocaleString()} because it takes a future slot.`); + return; + } + + if (this.state.started && this.state.blockchain.value === "idle") { + this.dispatch("NEWBLOCK"); + this.enqueueBlocks([block]); + } else { + logger.info(`Block disregarded because blockchain is not ready :exclamation:`); + } + } + + /** + * Enqueue blocks in process queue and set last downloaded block to last item in list. + */ + public enqueueBlocks(blocks: any[]) { + if (blocks.length === 0) { + return; + } + + this.processQueue.push(blocks); + this.state.lastDownloadedBlock = new Block(blocks.slice(-1)[0]); + } + + /** + * Rollback all blocks up to the previous round. + * @return {void} + */ + public async rollbackCurrentRound() { + const height = this.state.getLastBlock().data.height; + const maxDelegates = config.getMilestone(height).activeDelegates; + const previousRound = Math.floor((height - 1) / maxDelegates); + + if (previousRound < 2) { + return; + } + + const newHeight = previousRound * maxDelegates; + // If the current chain height is H and we will be removing blocks [N, H], + // then blocksToRemove[] will contain blocks [N - 1, H - 1]. + const blocksToRemove = await this.database.getBlocks(newHeight, height - newHeight); + const deleteLastBlock = async () => { + const lastBlock = this.state.getLastBlock(); + await this.database.enqueueDeleteBlock(lastBlock); + + const newLastBlock = new Block(blocksToRemove.pop()); + + this.state.setLastBlock(newLastBlock); + this.state.lastDownloadedBlock = newLastBlock; + }; + + logger.info(`Removing ${pluralize("block", height - newHeight, true)} to reset current round :warning:`); + + let count = 0; + const max = this.state.getLastBlock().data.height - newHeight; + + while (this.state.getLastBlock().data.height >= newHeight + 1) { + const removalBlockId = this.state.getLastBlock().data.id; + const removalBlockHeight = this.state.getLastBlock().data.height.toLocaleString(); + + logger.info(`Removing block ${count++} of ${max} - ID: ${removalBlockId}, height: ${removalBlockHeight}`); + + await deleteLastBlock(); + } + + // Commit delete blocks + await this.database.commitQueuedQueries(); + + logger.info(`Removed ${count} ${pluralize("block", max, true)}`); + + await this.database.deleteRound(previousRound + 1); + } + + /** + * Remove N number of blocks. + * @param {Number} nblocks + * @return {void} + */ + public async removeBlocks(nblocks) { + this.clearAndStopQueue(); + + // If the current chain height is H and we will be removing blocks [N, H], + // then blocksToRemove[] will contain blocks [N - 1, H - 1]. + const blocksToRemove = await this.database.getBlocks(this.state.getLastBlock().data.height - nblocks, nblocks); + + const revertLastBlock = async () => { + // tslint:disable-next-line:no-shadowed-variable + const lastBlock = this.state.getLastBlock(); + + // TODO: if revertBlock Failed, it might corrupt the database because one block could be left stored + await this.database.revertBlock(lastBlock); + this.database.enqueueDeleteBlock(lastBlock); + + if (this.transactionPool) { + await this.transactionPool.addTransactions(lastBlock.transactions); + } + + const newLastBlock = new Block(blocksToRemove.pop()); + + this.state.setLastBlock(newLastBlock); + this.state.lastDownloadedBlock = newLastBlock; + }; + + // tslint:disable-next-line:variable-name + const __removeBlocks = async numberOfBlocks => { + if (numberOfBlocks < 1) { + return; + } + + logger.info(`Undoing block ${this.state.getLastBlock().data.height.toLocaleString()}`); + + await revertLastBlock(); + await __removeBlocks(numberOfBlocks - 1); + }; + + const lastBlock = this.state.getLastBlock(); + if (nblocks >= lastBlock.data.height) { + nblocks = lastBlock.data.height - 1; + } + + const resetHeight = lastBlock.data.height - nblocks; + logger.info(`Removing ${pluralize("block", nblocks, true)}. Reset to height ${resetHeight.toLocaleString()}`); + + this.state.lastDownloadedBlock = lastBlock; + + await __removeBlocks(nblocks); + + // Commit delete blocks + await this.database.commitQueuedQueries(); + + this.queue.resume(); + } + + /** + * Remove the top blocks from database. + * NOTE: Only used when trying to restore database integrity. + * @param {Number} count + * @return {void} + */ + public async removeTopBlocks(count) { + const blocks = await this.database.getTopBlocks(count); + + logger.info( + `Removing ${pluralize( + "block", + blocks.length, + true, + )} from height ${(blocks[0] as any).height.toLocaleString()}`, + ); + + for (let block of blocks) { + block = new Block(block); + + this.database.enqueueDeleteRound(block.data.height); + this.database.enqueueDeleteBlock(block); + } + + await this.database.commitQueuedQueries(); + await this.database.loadBlocksFromCurrentRound(); + } + + /** + * Hande a block during a rebuild. + * NOTE: We should be sure this is fail safe (ie callback() is being called only ONCE) + * @param {Block} block + * @param {Function} callback + * @return {Object} + */ + public async rebuildBlock(block, callback) { + const lastBlock = this.state.getLastBlock(); + + if (block.verification.verified) { + if (isBlockChained(lastBlock, block)) { + // save block on database + this.database.enqueueSaveBlock(block); + + // committing to db every 20,000 blocks + if (block.data.height % 20000 === 0) { + await this.database.commitQueuedQueries(); + } + + this.state.setLastBlock(block); + + return callback(); + } + if (block.data.height > lastBlock.data.height + 1) { + this.state.lastDownloadedBlock = lastBlock; + return callback(); + } + if ( + block.data.height < lastBlock.data.height || + (block.data.height === lastBlock.data.height && block.data.id === lastBlock.data.id) + ) { + this.state.lastDownloadedBlock = lastBlock; + return callback(); + } + this.state.lastDownloadedBlock = lastBlock; + logger.info(`Block ${block.data.height.toLocaleString()} disregarded because on a fork :knife_fork_plate:`); + return callback(); + } + logger.warn(`Block ${block.data.height.toLocaleString()} disregarded because verification failed :scroll:`); + logger.warn(JSON.stringify(block.verification, null, 4)); + return callback(); + } + + /** + * Process the given block. + */ + public async processBlock(block: models.Block, callback) { + const result = await this.blockProcessor.process(block); + + if (result === BlockProcessorResult.Accepted || result === BlockProcessorResult.DiscardedButCanBeBroadcasted) { + // broadcast only current block + const blocktime = config.getMilestone(block.data.height).blocktime; + if (slots.getSlotNumber() * blocktime <= block.data.timestamp) { + this.p2p.broadcastBlock(block); + } + } + + return callback(); + } + + /** + * Reset the last downloaded block to last chained block. + */ + public resetLastDownloadedBlock() { + this.state.lastDownloadedBlock = this.getLastBlock(); + } + + /** + * Called by forger to wake up and sync with the network. + * It clears the wakeUpTimeout if set. + */ + public forceWakeup() { + this.state.clearWakeUpTimeout(); + this.dispatch("WAKEUP"); + } + + /** + * Fork the chain at the given block. + */ + public forkBlock(block: models.Block): void { + this.state.forkedBlock = block; + + this.dispatch("FORK"); + } + + /** + * Get unconfirmed transactions for the specified block size. + * @param {Number} blockSize + * @param {Boolean} forForging + * @return {Object} + */ + public getUnconfirmedTransactions(blockSize) { + const transactions = this.transactionPool.getTransactionsForForging(blockSize); + + return { + transactions, + poolSize: this.transactionPool.getPoolSize(), + count: transactions ? transactions.length : -1, + }; + } + + /** + * Determine if the blockchain is synced. + */ + public isSynced(block?: models.IBlock): boolean { + if (!this.p2p.hasPeers()) { + return true; + } + + block = block || this.getLastBlock(); + + return slots.getTime() - block.data.timestamp < 3 * config.getMilestone(block.data.height).blocktime; + } + + /** + * Determine if the blockchain is synced after a rebuild. + */ + public isRebuildSynced(block?: models.IBlock): boolean { + if (!this.p2p.hasPeers()) { + return true; + } + + block = block || this.getLastBlock(); + + const remaining = slots.getTime() - block.data.timestamp; + logger.info(`Remaining block timestamp ${remaining} :hourglass:`); + + // stop fast rebuild 7 days before the last network block + return slots.getTime() - block.data.timestamp < 3600 * 24 * 7; + // return slots.getTime() - block.data.timestamp < 100 * config.getMilestone(block.data.height).blocktime + } + + /** + * Get the last block of the blockchain. + */ + public getLastBlock(): models.Block { + return this.state.getLastBlock(); + } + + /** + * Get the last height of the blockchain. + */ + public getLastHeight(): number { + return this.state.getLastBlock().data.height; + } + + /** + * Get the last downloaded block of the blockchain. + */ + public getLastDownloadedBlock(): { data: models.IBlockData } { + return this.state.lastDownloadedBlock; + } + + /** + * Get the block ping. + */ + public getBlockPing() { + return this.state.blockPing; + } + + /** + * Ping a block. + */ + public pingBlock(incomingBlock: models.IBlockData): boolean { + return this.state.pingBlock(incomingBlock); + } + + /** + * Push ping block. + */ + public pushPingBlock(block: models.IBlockData) { + this.state.pushPingBlock(block); + } + + /** + * Get the list of events that are available. + * @return {Array} + */ + public getEvents() { + return [ + "block.applied", + "block.forged", + "block.reverted", + "delegate.registered", + "delegate.resigned", + "forger.failed", + "forger.missing", + "forger.started", + "peer.added", + "peer.removed", + "round.created", + "state:started", + "transaction.applied", + "transaction.expired", + "transaction.forged", + "transaction.reverted", + "wallet.saved", + "wallet.created.cold", + ]; + } + + /** + * Register the block queue. + * @return {void} + */ + public __registerQueue() { + this.queue = new Queue(this, { + process: "PROCESSFINISHED", + rebuild: "REBUILDFINISHED", + }); + + this.processQueue = this.queue.process; + this.rebuildQueue = this.queue.rebuild; + } +} diff --git a/packages/core-blockchain/src/config.ts b/packages/core-blockchain/src/config.ts new file mode 100644 index 0000000000..1b95990a82 --- /dev/null +++ b/packages/core-blockchain/src/config.ts @@ -0,0 +1,2 @@ +import { Shared } from "@arkecosystem/core-interfaces"; +export const config = new Shared.Config(); diff --git a/packages/core-blockchain/src/defaults.ts b/packages/core-blockchain/src/defaults.ts new file mode 100644 index 0000000000..2838157dd8 --- /dev/null +++ b/packages/core-blockchain/src/defaults.ts @@ -0,0 +1,11 @@ +export const defaults = { + fastRebuild: false, + databaseRollback: { + maxBlockRewind: 10000, + steps: 1000, + }, + state: { + maxLastBlocks: 100, + maxLastTransactionIds: 10000, + }, +}; diff --git a/packages/core-blockchain/src/index.ts b/packages/core-blockchain/src/index.ts new file mode 100644 index 0000000000..727dae7ba7 --- /dev/null +++ b/packages/core-blockchain/src/index.ts @@ -0,0 +1,5 @@ +export * from "./defaults"; +export * from "./config"; +export * from "./blockchain"; +export * from "./state-storage"; +export * from "./plugin"; diff --git a/packages/core-blockchain/src/machines/actions/fork.ts b/packages/core-blockchain/src/machines/actions/fork.ts new file mode 100644 index 0000000000..0b205c99e4 --- /dev/null +++ b/packages/core-blockchain/src/machines/actions/fork.ts @@ -0,0 +1,61 @@ +export const fork = { + initial: "analysing", + states: { + analysing: { + onEntry: ["analyseFork"], + on: { + REBUILD: "revertBlocks", + NOFORK: "exit", + }, + }, + network: { + onEntry: ["checkNetwork"], + /* these transitions are not used yet (TODO?) + on: { + SUCCESS: 'blockchain', + FAILURE: 'reset' + } + */ + }, + revertBlocks: {}, + exit: { + onEntry: ["forkRecovered"], + }, + }, +}; + +// const fork = { +// initial: 'network', +// states: { +// network: { +// onEntry: ['checkNetwork'], +// on: { +// SUCCESS: 'blockchain', +// FAILURE: 'reset' +// } +// }, +// blockchain: { +// onEntry: ['removeBlocks'], +// on: { +// SUCCESS: 'wallets', +// FAILURE: 'reset' +// } +// }, +// wallets: { +// onEntry: ['rebuildWallets'], +// on: { +// SUCCESS: 'success', +// FAILURE: 'reset' +// } +// }, +// reset: { +// onEntry: ['resetNode'], +// on: { +// RESET: 'success', +// FAILURE: 'reset' +// } +// }, +// success: { +// } +// } +// } diff --git a/packages/core-blockchain/src/machines/actions/rebuild-from-network.ts b/packages/core-blockchain/src/machines/actions/rebuild-from-network.ts new file mode 100644 index 0000000000..70ca9e2a7e --- /dev/null +++ b/packages/core-blockchain/src/machines/actions/rebuild-from-network.ts @@ -0,0 +1,52 @@ +export const rebuildFromNetwork = { + initial: "rebuilding", + states: { + rebuilding: { + onEntry: ["checkLastDownloadedBlockSynced"], + on: { + SYNCED: "waitingFinished", + NOTSYNCED: "rebuildBlocks", + PAUSED: "rebuildPaused", + }, + }, + idle: { + on: { + DOWNLOADED: "rebuildBlocks", + }, + }, + rebuildBlocks: { + onEntry: ["rebuildBlocks"], + on: { + DOWNLOADED: "rebuilding", + NOBLOCK: "rebuilding", + }, + }, + waitingFinished: { + on: { + REBUILDFINISHED: "rebuildFinished", + }, + }, + rebuildFinished: { + onEntry: ["rebuildFinished"], + on: { + PROCESSFINISHED: "processFinished", + }, + }, + rebuildPaused: { + onEntry: ["downloadPaused"], + on: { + REBUILDFINISHED: "processFinished", + }, + }, + processFinished: { + onEntry: ["checkRebuildBlockSynced"], + on: { + SYNCED: "end", + NOTSYNCED: "rebuildBlocks", + }, + }, + end: { + onEntry: ["rebuildingComplete"], + }, + }, +}; diff --git a/packages/core-blockchain/src/machines/actions/sync-with-network.ts b/packages/core-blockchain/src/machines/actions/sync-with-network.ts new file mode 100644 index 0000000000..f8683ad344 --- /dev/null +++ b/packages/core-blockchain/src/machines/actions/sync-with-network.ts @@ -0,0 +1,48 @@ +export const syncWithNetwork = { + initial: "syncing", + states: { + syncing: { + onEntry: ["checkLastDownloadedBlockSynced"], + on: { + SYNCED: "downloadFinished", + NOTSYNCED: "downloadBlocks", + PAUSED: "downloadPaused", + NETWORKHALTED: "end", + }, + }, + idle: { + on: { + DOWNLOADED: "downloadBlocks", + }, + }, + downloadBlocks: { + onEntry: ["downloadBlocks"], + on: { + DOWNLOADED: "syncing", + NOBLOCK: "syncing", + }, + }, + downloadFinished: { + onEntry: ["downloadFinished"], + on: { + PROCESSFINISHED: "processFinished", + }, + }, + downloadPaused: { + onEntry: ["downloadPaused"], + on: { + PROCESSFINISHED: "processFinished", + }, + }, + processFinished: { + onEntry: ["checkLastBlockSynced"], + on: { + SYNCED: "end", + NOTSYNCED: "downloadBlocks", + }, + }, + end: { + onEntry: ["syncingComplete"], + }, + }, +}; diff --git a/packages/core-blockchain/src/machines/blockchain.ts b/packages/core-blockchain/src/machines/blockchain.ts new file mode 100644 index 0000000000..779bfb5b5d --- /dev/null +++ b/packages/core-blockchain/src/machines/blockchain.ts @@ -0,0 +1,89 @@ +import { Machine } from "xstate"; +import { fork } from "./actions/fork"; +import { rebuildFromNetwork } from "./actions/rebuild-from-network"; +import { syncWithNetwork } from "./actions/sync-with-network"; + +export const blockchainMachine: any = Machine({ + key: "blockchain", + initial: "uninitialised", + states: { + uninitialised: { + on: { + START: "init", + STOP: "stopped", + }, + }, + init: { + onEntry: ["init"], + on: { + REBUILD: "rebuild", + NETWORKSTART: "idle", + STARTED: "syncWithNetwork", + ROLLBACK: "rollback", + FAILURE: "exit", + STOP: "stopped", + }, + }, + rebuild: { + on: { + REBUILDCOMPLETE: "syncWithNetwork", + FORK: "fork", + TEST: "syncWithNetwork", + STOP: "stopped", + }, + ...rebuildFromNetwork, + }, + syncWithNetwork: { + on: { + TEST: "idle", + SYNCFINISHED: "idle", + FORK: "fork", + STOP: "stopped", + }, + ...syncWithNetwork, + }, + idle: { + onEntry: ["checkLater", "blockchainReady"], + on: { + WAKEUP: "syncWithNetwork", + NEWBLOCK: "newBlock", + STOP: "stopped", + }, + }, + newBlock: { + on: { + PROCESSFINISHED: "idle", + FORK: "fork", + STOP: "stopped", + }, + }, + fork: { + onEntry: ["startForkRecovery"], + on: { + SUCCESS: "syncWithNetwork", + FAILURE: "exit", + STOP: "stopped", + }, + ...fork, + }, + rollback: { + onEntry: ["rollbackDatabase"], + on: { + SUCCESS: "init", + FAILURE: "exit", + STOP: "stopped", + }, + }, + /** + * This state should be used for stopping the blockchain on purpose, not as + * a result of critical errors. In those cases, using the `exit` state would + * be a better option + */ + stopped: { + onEntry: ["stopped"], + }, + exit: { + onEntry: ["exitApp"], + }, + }, +}); diff --git a/packages/core-blockchain/src/plugin.ts b/packages/core-blockchain/src/plugin.ts new file mode 100644 index 0000000000..57771cefc7 --- /dev/null +++ b/packages/core-blockchain/src/plugin.ts @@ -0,0 +1,32 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import { asValue } from "awilix"; +import { Blockchain } from "./blockchain"; +import { config } from "./config"; +import { defaults } from "./defaults"; +import { stateStorage } from "./state-storage"; + +/** + * The struct used by the plugin container. + * @type {Object} + */ +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "blockchain", + async register(container: Container.IContainer, options) { + const blockchain = new Blockchain(options); + + config.init(options); + + container.register("state", asValue(stateStorage)); + + if (!process.env.CORE_SKIP_BLOCKCHAIN) { + await blockchain.start(); + } + + return blockchain; + }, + async deregister(container: Container.IContainer, options) { + await container.resolvePlugin("blockchain").stop(); + }, +}; diff --git a/packages/core-blockchain/src/processor/block-processor.ts b/packages/core-blockchain/src/processor/block-processor.ts new file mode 100644 index 0000000000..29d51782db --- /dev/null +++ b/packages/core-blockchain/src/processor/block-processor.ts @@ -0,0 +1,102 @@ +// tslint:disable:max-classes-per-file + +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { isException, models } from "@arkecosystem/crypto"; +import { Blockchain } from "../blockchain"; +import { isBlockChained } from "../utils/is-block-chained"; +import { validateGenerator } from "../utils/validate-generator"; + +import { + AcceptBlockHandler, + AlreadyForgedHandler, + BlockHandler, + ExceptionHandler, + InvalidGeneratorHandler, + UnchainedHandler, + VerificationFailedHandler, +} from "./handlers"; + +export enum BlockProcessorResult { + Accepted, + DiscardedButCanBeBroadcasted, + Rejected, +} + +export class BlockProcessor { + private logger: Logger.ILogger; + + public constructor(private blockchain: Blockchain) { + this.logger = app.resolvePlugin("logger"); + } + + public async process(block: models.Block): Promise { + const handler = await this.getHandler(block); + return handler.execute(); + } + + public async getHandler(block: models.Block): Promise { + if (isException(block.data)) { + return new ExceptionHandler(this.blockchain, block); + } + + if (!this.verifyBlock(block)) { + return new VerificationFailedHandler(this.blockchain, block); + } + + const isValidGenerator = await validateGenerator(block); + const isChained = isBlockChained(this.blockchain.getLastBlock(), block); + if (!isChained) { + return new UnchainedHandler(this.blockchain, block, isValidGenerator); + } + + if (!isValidGenerator) { + return new InvalidGeneratorHandler(this.blockchain, block); + } + + const containsForgedTransactions = await this.checkBlockContainsForgedTransactions(block); + if (containsForgedTransactions) { + return new AlreadyForgedHandler(this.blockchain, block); + } + + return new AcceptBlockHandler(this.blockchain, block); + } + + /** + * Checks if the given block is verified or an exception. + */ + private verifyBlock(block: models.Block): boolean { + const verified = block.verification.verified; + if (!verified) { + this.logger.warn( + `Block ${block.data.height.toLocaleString()} (${ + block.data.id + }) disregarded because verification failed :scroll:`, + ); + this.logger.warn(JSON.stringify(block.verification, null, 4)); + return false; + } + + return true; + } + + /** + * Checks if the given block contains an already forged transaction. + */ + private async checkBlockContainsForgedTransactions(block: models.Block): Promise { + if (block.transactions.length > 0) { + const forgedIds = await this.blockchain.database.getForgedTransactionsIds( + block.transactions.map(tx => tx.id), + ); + if (forgedIds.length > 0) { + this.logger.warn( + `Block ${block.data.height.toLocaleString()} disregarded, because it contains already forged transactions :scroll:`, + ); + this.logger.debug(`${JSON.stringify(forgedIds, null, 4)}`); + return true; + } + } + + return false; + } +} diff --git a/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts b/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts new file mode 100644 index 0000000000..91f9472a82 --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts @@ -0,0 +1,52 @@ +import { BlockProcessorResult } from "../block-processor"; +import { BlockHandler } from "./block-handler"; + +export class AcceptBlockHandler extends BlockHandler { + public async execute(): Promise { + const { database, state, transactionPool } = this.blockchain; + + try { + await database.applyBlock(this.block); + await database.saveBlock(this.block); + + // Check if we recovered from a fork + if (state.forkedBlock && state.forkedBlock.data.height === this.block.data.height) { + this.logger.info("Successfully recovered from fork :star2:"); + state.forkedBlock = null; + } + + if (transactionPool) { + try { + transactionPool.acceptChainedBlock(this.block); + } catch (error) { + this.logger.warn("Issue applying block to transaction pool"); + this.logger.debug(error.stack); + } + } + + // Reset wake-up timer after chaining a block, since there's no need to + // wake up at all if blocks arrive periodically. Only wake up when there are + // no new blocks. + if (state.started) { + this.blockchain.resetWakeUp(); + } + + state.setLastBlock(this.block); + + // Ensure the lastDownloadedBlock is never behind the last accepted block. + if (state.lastDownloadedBlock && state.lastDownloadedBlock.data.height < this.block.data.height) { + state.lastDownloadedBlock = this.block; + } + + return BlockProcessorResult.Accepted; + } catch (error) { + this.logger.error(`Refused new block ${JSON.stringify(this.block.data)}`); + this.logger.debug(error.stack); + + this.blockchain.transactionPool.purgeBlock(this.block); + this.blockchain.forkBlock(this.block); + + return super.execute(); + } + } +} diff --git a/packages/core-blockchain/src/processor/handlers/already-forged-handler.ts b/packages/core-blockchain/src/processor/handlers/already-forged-handler.ts new file mode 100644 index 0000000000..b1185cb74e --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/already-forged-handler.ts @@ -0,0 +1,9 @@ +import { BlockProcessorResult } from "../block-processor"; +import { BlockHandler } from "./block-handler"; + +export class AlreadyForgedHandler extends BlockHandler { + public async execute(): Promise { + super.execute(); + return BlockProcessorResult.DiscardedButCanBeBroadcasted; + } +} diff --git a/packages/core-blockchain/src/processor/handlers/block-handler.ts b/packages/core-blockchain/src/processor/handlers/block-handler.ts new file mode 100644 index 0000000000..5a5ffd0494 --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/block-handler.ts @@ -0,0 +1,18 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; +import { Blockchain } from "../../blockchain"; +import { BlockProcessorResult } from "../block-processor"; + +export abstract class BlockHandler { + protected logger: Logger.ILogger; + + public constructor(protected blockchain: Blockchain, protected block: models.Block) { + this.logger = app.resolvePlugin("logger"); + } + + public async execute(): Promise { + this.blockchain.resetLastDownloadedBlock(); + return BlockProcessorResult.Rejected; + } +} diff --git a/packages/core-blockchain/src/processor/handlers/exception-handler.ts b/packages/core-blockchain/src/processor/handlers/exception-handler.ts new file mode 100644 index 0000000000..52f05e8bec --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/exception-handler.ts @@ -0,0 +1,20 @@ +import { BlockProcessorResult } from "../block-processor"; +import { AcceptBlockHandler } from "./accept-block-handler"; +import { BlockHandler } from "./block-handler"; + +export class ExceptionHandler extends BlockHandler { + public async execute(): Promise { + // Ensure the block has not been forged yet, as an exceptional + // block bypasses all other checks. + const forgedBlock = await this.blockchain.database.getBlock(this.block.data.id); + if (forgedBlock) { + return super.execute(); + } + + this.logger.warn( + `Block ${this.block.data.height.toLocaleString()} (${this.block.data.id}) forcibly accepted. :exclamation:`, + ); + + return new AcceptBlockHandler(this.blockchain, this.block).execute(); + } +} diff --git a/packages/core-blockchain/src/processor/handlers/index.ts b/packages/core-blockchain/src/processor/handlers/index.ts new file mode 100644 index 0000000000..924075ccec --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/index.ts @@ -0,0 +1,7 @@ +export * from "./accept-block-handler"; +export * from "./already-forged-handler"; +export * from "./block-handler"; +export * from "./exception-handler"; +export * from "./invalid-generator-handler"; +export * from "./unchained-handler"; +export * from "./verification-failed-handler"; diff --git a/packages/core-blockchain/src/processor/handlers/invalid-generator-handler.ts b/packages/core-blockchain/src/processor/handlers/invalid-generator-handler.ts new file mode 100644 index 0000000000..2e2906480c --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/invalid-generator-handler.ts @@ -0,0 +1,3 @@ +import { BlockHandler } from "./block-handler"; + +export class InvalidGeneratorHandler extends BlockHandler {} diff --git a/packages/core-blockchain/src/processor/handlers/unchained-handler.ts b/packages/core-blockchain/src/processor/handlers/unchained-handler.ts new file mode 100644 index 0000000000..f706288a43 --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/unchained-handler.ts @@ -0,0 +1,98 @@ +import { app } from "@arkecosystem/core-container"; +import { models } from "@arkecosystem/crypto"; +import { Blockchain } from "../../blockchain"; +import { BlockProcessorResult } from "../block-processor"; +import { BlockHandler } from "./block-handler"; + +enum UnchainedBlockStatus { + NotReadyToAcceptNewHeight, + AlreadyInBlockchain, + EqualToLastBlock, + GeneratorMismatch, + DoubleForging, + InvalidTimestamp, +} + +export class UnchainedHandler extends BlockHandler { + public constructor( + protected blockchain: Blockchain, + protected block: models.Block, + private isValidGenerator: boolean, + ) { + super(blockchain, block); + } + + public async execute(): Promise { + super.execute(); + + this.blockchain.processQueue.clear(); + + const status = this.checkUnchainedBlock(); + switch (status) { + case UnchainedBlockStatus.DoubleForging: { + const database = app.resolvePlugin("database"); + const delegates = await database.getActiveDelegates(this.block.data.height); + if (delegates.some(delegate => delegate.publicKey === this.block.data.generatorPublicKey)) { + this.blockchain.forkBlock(this.block); + } + + return BlockProcessorResult.Rejected; + } + + case UnchainedBlockStatus.GeneratorMismatch: + case UnchainedBlockStatus.InvalidTimestamp: { + return BlockProcessorResult.Rejected; + } + + default: { + return BlockProcessorResult.DiscardedButCanBeBroadcasted; + } + } + } + + private checkUnchainedBlock(): UnchainedBlockStatus { + const lastBlock = this.blockchain.getLastBlock(); + if (this.block.data.height > lastBlock.data.height + 1) { + this.logger.debug( + `Blockchain not ready to accept new block at height ${this.block.data.height.toLocaleString()}. Last block: ${lastBlock.data.height.toLocaleString()} :warning:`, + ); + + // Also remove all remaining queued blocks. Since blocks are downloaded in batches, + // it is very likely that all blocks will be disregarded at this point anyway. + // NOTE: This isn't really elegant, but still better than spamming the log with + // useless `not ready to accept` messages. + if (this.blockchain.processQueue.length() > 0) { + this.logger.debug(`Discarded ${this.blockchain.processQueue.length()} downloaded blocks.`); + } + + return UnchainedBlockStatus.NotReadyToAcceptNewHeight; + } else if (this.block.data.height < lastBlock.data.height) { + this.logger.debug( + `Block ${this.block.data.height.toLocaleString()} disregarded because already in blockchain :warning:`, + ); + + return UnchainedBlockStatus.AlreadyInBlockchain; + } else if (this.block.data.height === lastBlock.data.height && this.block.data.id === lastBlock.data.id) { + this.logger.debug(`Block ${this.block.data.height.toLocaleString()} just received :chains:`); + return UnchainedBlockStatus.EqualToLastBlock; + } else if (this.block.data.timestamp < lastBlock.data.timestamp) { + this.logger.debug( + `Block ${this.block.data.height.toLocaleString()} disregarded, because the timestamp is lower than the previous timestamp.`, + ); + return UnchainedBlockStatus.InvalidTimestamp; + } else { + if (this.isValidGenerator) { + this.logger.warn(`Detect double forging by ${this.block.data.generatorPublicKey} :chains:`); + return UnchainedBlockStatus.DoubleForging; + } + + this.logger.info( + `Forked block disregarded because it is not allowed to be forged. Caused by delegate: ${ + this.block.data.generatorPublicKey + } :bangbang:`, + ); + + return UnchainedBlockStatus.GeneratorMismatch; + } + } +} diff --git a/packages/core-blockchain/src/processor/handlers/verification-failed-handler.ts b/packages/core-blockchain/src/processor/handlers/verification-failed-handler.ts new file mode 100644 index 0000000000..874e907408 --- /dev/null +++ b/packages/core-blockchain/src/processor/handlers/verification-failed-handler.ts @@ -0,0 +1,9 @@ +import { BlockProcessorResult } from "../block-processor"; +import { BlockHandler } from "./block-handler"; + +export class VerificationFailedHandler extends BlockHandler { + public async execute(): Promise { + this.blockchain.transactionPool.purgeSendersWithInvalidTransactions(this.block); + return super.execute(); + } +} diff --git a/packages/core-blockchain/src/processor/index.ts b/packages/core-blockchain/src/processor/index.ts new file mode 100644 index 0000000000..95c35dacb5 --- /dev/null +++ b/packages/core-blockchain/src/processor/index.ts @@ -0,0 +1 @@ +export * from "../processor/block-processor"; diff --git a/packages/core-blockchain/src/queue/index.ts b/packages/core-blockchain/src/queue/index.ts new file mode 100644 index 0000000000..031522c6c0 --- /dev/null +++ b/packages/core-blockchain/src/queue/index.ts @@ -0,0 +1,53 @@ +import { ProcessQueue } from "./process"; +import { RebuildQueue } from "./rebuild"; + +export { ProcessQueue }; +export { RebuildQueue }; + +export class Queue { + public process: ProcessQueue; + public rebuild: RebuildQueue; + + /** + * Create an instance of the queue. + * @param {Blockchain} blockchain + * @param {Object} events + * @return {void} + */ + constructor(blockchain, events) { + this.process = new ProcessQueue(blockchain, events.process); + this.rebuild = new RebuildQueue(blockchain, events.rebuild); + } + + /** + * Pause all queues. + * @return {void} + */ + public pause() { + this.rebuild.pause(); + this.process.pause(); + } + + /** + * Flush all queues. + * @return {void} + */ + public clear() { + this.rebuild.clear(); + this.process.clear(); + } + + /** + * Resume all queues. + * @return {void} + */ + public resume() { + this.rebuild.resume(); + this.process.resume(); + } + + public destroy() { + this.rebuild.destroy(); + this.process.destroy(); + } +} diff --git a/packages/core-blockchain/src/queue/interface.ts b/packages/core-blockchain/src/queue/interface.ts new file mode 100644 index 0000000000..45b863c9a4 --- /dev/null +++ b/packages/core-blockchain/src/queue/interface.ts @@ -0,0 +1,71 @@ +import async from "async"; +import { Blockchain } from "../blockchain"; + +export abstract class QueueInterface { + protected queue: any; + + /** + * Create an instance of the process queue. + */ + constructor(readonly blockchain: Blockchain, readonly event: string) {} + + /** + * Drain the queue. + */ + public drain() { + this.queue.drain = () => this.blockchain.dispatch(this.event); + } + + /** + * Pause the queue. + * @return {void} + */ + public pause() { + return this.queue.pause(); + } + + /** + * Flush the queue. + * @return {void} + */ + public clear() { + return this.queue.remove(() => true); + } + + /** + * Resume the queue. + * @return {void} + */ + public resume() { + return this.queue.resume(); + } + + /** + * Remove the item from the queue. + * @return {void} + */ + public remove(item) { + return this.queue.remove(item); + } + + /** + * Push the item to the queue. + * @param {Function} callback + * @return {void} + */ + public push(callback) { + return this.queue.push(callback); + } + + /** + * Get the length of the queue. + * @return {void} + */ + public length() { + return this.queue.length(); + } + + public destroy() { + return this.queue.kill(); + } +} diff --git a/packages/core-blockchain/src/queue/process.ts b/packages/core-blockchain/src/queue/process.ts new file mode 100644 index 0000000000..bd4afc25ba --- /dev/null +++ b/packages/core-blockchain/src/queue/process.ts @@ -0,0 +1,29 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; +import async from "async"; +import { Blockchain } from "../blockchain"; +import { QueueInterface } from "./interface"; + +const logger = app.resolvePlugin("logger"); + +export class ProcessQueue extends QueueInterface { + /** + * Create an instance of the process queue. + */ + constructor(readonly blockchain: Blockchain, readonly event: string) { + super(blockchain, event); + + this.queue = async.queue((block: models.IBlockData, cb) => { + try { + return blockchain.processBlock(new models.Block(block), cb); + } catch (error) { + logger.error(`Failed to process block in ProcessQueue: ${block.height.toLocaleString()}`); + logger.error(error.stack); + return cb(); + } + }, 1); + + this.drain(); + } +} diff --git a/packages/core-blockchain/src/queue/rebuild.ts b/packages/core-blockchain/src/queue/rebuild.ts new file mode 100644 index 0000000000..786d3dc4bf --- /dev/null +++ b/packages/core-blockchain/src/queue/rebuild.ts @@ -0,0 +1,31 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; +import async from "async"; +import { Blockchain } from "../blockchain"; +import { QueueInterface } from "./interface"; + +const logger = app.resolvePlugin("logger"); + +export class RebuildQueue extends QueueInterface { + /** + * Create an instance of the process queue. + */ + constructor(readonly blockchain: Blockchain, readonly event: string) { + super(blockchain, event); + + this.queue = async.queue((block: models.IBlockData, cb) => { + if (this.queue.paused) { + return cb(); + } + try { + return blockchain.rebuildBlock(new models.Block(block), cb); + } catch (error) { + logger.error(`Failed to rebuild block in RebuildQueue: ${block.height.toLocaleString()}`); + return cb(); + } + }, 1); + + this.drain(); + } +} diff --git a/packages/core-blockchain/src/state-machine.ts b/packages/core-blockchain/src/state-machine.ts new file mode 100644 index 0000000000..b5f5754475 --- /dev/null +++ b/packages/core-blockchain/src/state-machine.ts @@ -0,0 +1,415 @@ +/* tslint:disable:jsdoc-format max-line-length */ + +import { app } from "@arkecosystem/core-container"; +import { EventEmitter, Logger } from "@arkecosystem/core-interfaces"; + +import { roundCalculator } from "@arkecosystem/core-utils"; +import { isException, models, slots } from "@arkecosystem/crypto"; + +import pluralize from "pluralize"; +import { config as localConfig } from "./config"; +import { blockchainMachine } from "./machines/blockchain"; +import { stateStorage } from "./state-storage"; +import { isBlockChained, tickSyncTracker } from "./utils"; + +import { Blockchain } from "./blockchain"; + +const { Block } = models; +const config = app.getConfig(); +const emitter = app.resolvePlugin("event-emitter"); +const logger = app.resolvePlugin("logger"); + +/** + * @type {IStateStorage} + */ +blockchainMachine.state = stateStorage; + +/** + * The blockchain actions. + * @param {Blockchain} blockchain + * @return {Object} + */ +blockchainMachine.actionMap = (blockchain: Blockchain) => ({ + blockchainReady: () => { + if (!stateStorage.started) { + stateStorage.started = true; + emitter.emit("state:started", true); + } + }, + + checkLater() { + if (!blockchain.isStopped && !stateStorage.wakeUpTimeout) { + blockchain.setWakeUp(); + } + }, + + checkLastBlockSynced() { + return blockchain.dispatch(blockchain.isSynced() ? "SYNCED" : "NOTSYNCED"); + }, + + checkRebuildBlockSynced() { + return blockchain.dispatch(blockchain.isRebuildSynced() ? "SYNCED" : "NOTSYNCED"); + }, + + async checkLastDownloadedBlockSynced() { + let event = "NOTSYNCED"; + logger.debug( + `Queued blocks (rebuild: ${blockchain.rebuildQueue.length()} process: ${blockchain.processQueue.length()})`, + ); + + if (blockchain.rebuildQueue.length() > 10000 || blockchain.processQueue.length() > 10000) { + event = "PAUSED"; + } + + // tried to download but no luck after 5 tries (looks like network missing blocks) + if (stateStorage.noBlockCounter > 5 && blockchain.processQueue.length() === 0) { + // TODO: make this dynamic in 2.1 + logger.info( + "Tried to sync 5 times to different nodes, looks like the network is missing blocks :umbrella:", + ); + + stateStorage.noBlockCounter = 0; + event = "NETWORKHALTED"; + + if (stateStorage.p2pUpdateCounter + 1 > 3) { + logger.info("Network keeps missing blocks. :umbrella:"); + + const result = await blockchain.p2p.updatePeersOnMissingBlocks(); + if (result === "rollback") { + event = "FORK"; + } + + stateStorage.p2pUpdateCounter = 0; + } else { + stateStorage.p2pUpdateCounter++; + } + } + + if (blockchain.isSynced(stateStorage.lastDownloadedBlock)) { + stateStorage.noBlockCounter = 0; + stateStorage.p2pUpdateCounter = 0; + + event = "SYNCED"; + } + + if (stateStorage.networkStart) { + event = "SYNCED"; + } + + if (process.env.CORE_ENV === "test") { + event = "TEST"; + } + + blockchain.dispatch(event); + }, + + downloadFinished() { + logger.info("Block download finished :rocket:"); + + if (stateStorage.networkStart) { + // next time we will use normal behaviour + stateStorage.networkStart = false; + + blockchain.dispatch("SYNCFINISHED"); + } else if (blockchain.rebuildQueue.length() === 0) { + blockchain.dispatch("PROCESSFINISHED"); + } + }, + + async rebuildFinished() { + try { + logger.info("Blockchain rebuild finished :chains:"); + + stateStorage.rebuild = false; + + await blockchain.database.commitQueuedQueries(); + await blockchain.rollbackCurrentRound(); + await blockchain.database.buildWallets(stateStorage.getLastBlock().data.height); + await blockchain.database.saveWallets(true); + await blockchain.transactionPool.buildWallets(); + + return blockchain.dispatch("PROCESSFINISHED"); + } catch (error) { + logger.error(error.stack); + return blockchain.dispatch("FAILURE"); + } + }, + + downloadPaused: () => logger.info("Blockchain download paused :clock1030:"), + + syncingComplete() { + logger.info("Blockchain 100% in sync :100:"); + blockchain.dispatch("SYNCFINISHED"); + }, + + rebuildingComplete() { + logger.info("Blockchain rebuild complete :unicorn_face:"); + blockchain.dispatch("REBUILDCOMPLETE"); + }, + + stopped() { + logger.info("The blockchain has been stopped :guitar:"); + }, + + exitApp() { + app.forceExit("Failed to startup blockchain. Exiting Ark Core! :rotating_light:"); + }, + + async init() { + try { + let block = await blockchain.database.getLastBlock(); + + if (!block) { + logger.warn("No block found in database :hushed:"); + + block = new Block(config.get("genesisBlock")); + + if (block.data.payloadHash !== config.get("network.nethash")) { + logger.error( + "FATAL: The genesis block payload hash is different from configured the nethash :rotating_light:", + ); + + return blockchain.dispatch("FAILURE"); + } + + await blockchain.database.saveBlock(block); + } + + if (!blockchain.database.restoredDatabaseIntegrity) { + logger.info("Verifying database integrity :hourglass_flowing_sand:"); + + const blockchainAudit = await blockchain.database.verifyBlockchain(); + if (!blockchainAudit.valid) { + logger.error("FATAL: The database is corrupted :fire:"); + logger.error(JSON.stringify(blockchainAudit.errors, null, 4)); + + return blockchain.dispatch("ROLLBACK"); + } + + logger.info("Verified database integrity :smile_cat:"); + } else { + logger.info("Skipping database integrity check after successful database recovery :smile_cat:"); + } + + // only genesis block? special case of first round needs to be dealt with + if (block.data.height === 1) { + await blockchain.database.deleteRound(1); + } + + /** ******************************* + * state machine data init * + ******************************* */ + const constants = config.getMilestone(block.data.height); + stateStorage.setLastBlock(block); + stateStorage.lastDownloadedBlock = block; + + if (stateStorage.networkStart) { + await blockchain.database.buildWallets(block.data.height); + await blockchain.database.saveWallets(true); + await blockchain.database.applyRound(block.data.height); + await blockchain.transactionPool.buildWallets(); + + return blockchain.dispatch("STARTED"); + } + + stateStorage.rebuild = + slots.getTime() - block.data.timestamp > (constants.activeDelegates + 1) * constants.blocktime; + // no fast rebuild if in last week + stateStorage.fastRebuild = + slots.getTime() - block.data.timestamp > 3600 * 24 * 7 && !!localConfig.get("fastRebuild"); + + if (process.env.NODE_ENV === "test") { + logger.verbose("TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY. :bangbang:"); + + stateStorage.setLastBlock(new Block(config.get("genesisBlock"))); + await blockchain.database.buildWallets(block.data.height); + + return blockchain.dispatch("STARTED"); + } + + logger.info(`Fast rebuild: ${stateStorage.fastRebuild}`); + logger.info(`Last block in database: ${block.data.height.toLocaleString()}`); + + if (stateStorage.fastRebuild) { + return blockchain.dispatch("REBUILD"); + } + + // removing blocks up to the last round to compute active delegate list later if needed + const activeDelegates = await blockchain.database.getActiveDelegates(block.data.height); + + if (!activeDelegates) { + await blockchain.rollbackCurrentRound(); + } + + /** ******************************* + * database init * + ******************************* */ + // SPV rebuild + const verifiedWalletsIntegrity = await blockchain.database.buildWallets(block.data.height); + if (!verifiedWalletsIntegrity && block.data.height > 1) { + logger.warn( + "Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown. :hammer:", + ); + await blockchain.database.saveWallets(true); + } + + // NOTE: if the node is shutdown between round, the round has already been applied + if (roundCalculator.isNewRound(block.data.height + 1)) { + const { round } = roundCalculator.calculateRound(block.data.height + 1); + + logger.info( + `New round ${round.toLocaleString()} detected. Cleaning calculated data before restarting!`, + ); + + await blockchain.database.deleteRound(round); + } + + await blockchain.database.applyRound(block.data.height); + await blockchain.transactionPool.buildWallets(); + + return blockchain.dispatch("STARTED"); + } catch (error) { + logger.error(error.stack); + + return blockchain.dispatch("FAILURE"); + } + }, + + async rebuildBlocks() { + const lastBlock = stateStorage.lastDownloadedBlock || stateStorage.getLastBlock(); + const blocks = await blockchain.p2p.downloadBlocks(lastBlock.data.height); + + tickSyncTracker(blocks.length, lastBlock.data.height); + + if (!blocks || blocks.length === 0) { + logger.info("No new blocks found on this peer"); + + blockchain.dispatch("NOBLOCK"); + } else { + logger.info( + `Downloaded ${blocks.length} new ${pluralize( + "block", + blocks.length, + )} accounting for a total of ${pluralize( + "transaction", + blocks.reduce((sum, b) => sum + b.numberOfTransactions, 0), + true, + )}`, + ); + + if (blocks.length && blocks[0].previousBlock === lastBlock.data.id) { + stateStorage.lastDownloadedBlock = { data: blocks.slice(-1)[0] }; + blockchain.rebuildQueue.push(blocks); + blockchain.dispatch("DOWNLOADED"); + } else { + logger.warn(`Downloaded block not accepted: ${JSON.stringify(blocks[0])}`); + logger.warn(`Last block: ${JSON.stringify(lastBlock.data)}`); + + // disregard the whole block list + blockchain.dispatch("NOBLOCK"); + } + } + }, + + async downloadBlocks() { + const lastDownloadedBlock = stateStorage.lastDownloadedBlock || stateStorage.getLastBlock(); + const blocks = await blockchain.p2p.downloadBlocks(lastDownloadedBlock.data.height); + + if (blockchain.isStopped) { + return; + } + + const empty = !blocks || blocks.length === 0; + const chained = !empty && (isBlockChained(lastDownloadedBlock, { data: blocks[0] }) || isException(blocks[0])); + + if (chained) { + logger.info( + `Downloaded ${blocks.length} new ${pluralize( + "block", + blocks.length, + )} accounting for a total of ${pluralize( + "transaction", + blocks.reduce((sum, b) => sum + b.numberOfTransactions, 0), + true, + )}`, + ); + + stateStorage.noBlockCounter = 0; + stateStorage.p2pUpdateCounter = 0; + + blockchain.enqueueBlocks(blocks); + blockchain.dispatch("DOWNLOADED"); + } else { + if (empty) { + logger.info("No new block found on this peer"); + } else { + logger.warn(`Downloaded block not accepted: ${JSON.stringify(blocks[0])}`); + logger.warn(`Last downloaded block: ${JSON.stringify(lastDownloadedBlock.data)}`); + blockchain.processQueue.clear(); + } + + stateStorage.noBlockCounter++; + stateStorage.lastDownloadedBlock = stateStorage.getLastBlock(); + + blockchain.dispatch("NOBLOCK"); + } + }, + + async analyseFork() { + logger.info("Analysing fork :mag:"); + }, + + async startForkRecovery() { + logger.info("Starting fork recovery :fork_and_knife:"); + blockchain.clearAndStopQueue(); + + await blockchain.database.commitQueuedQueries(); + + const random = 4 + Math.floor(Math.random() * 99); // random int inside [4, 102] range + + await blockchain.removeBlocks(random); + + logger.info(`Removed ${pluralize("block", random, true)} :wastebasket:`); + + await blockchain.transactionPool.buildWallets(); + await blockchain.p2p.refreshPeersAfterFork(); + + blockchain.dispatch("SUCCESS"); + }, + + async rollbackDatabase() { + logger.info("Trying to restore database integrity :fire_engine:"); + + const { maxBlockRewind, steps } = localConfig.get("databaseRollback"); + let blockchainAudit; + + for (let i = maxBlockRewind; i >= 0; i -= steps) { + await blockchain.removeTopBlocks(steps); + + blockchainAudit = await blockchain.database.verifyBlockchain(); + if (blockchainAudit.valid) { + break; + } + } + + if (!blockchainAudit.valid) { + // TODO: multiple attempts? rewind further? restore snapshot? + logger.error("FATAL: Failed to restore database integrity :skull: :skull: :skull:"); + logger.error(JSON.stringify(blockchainAudit.errors, null, 4)); + blockchain.dispatch("FAILURE"); + return; + } + + blockchain.database.restoredDatabaseIntegrity = true; + + const lastBlock = await blockchain.database.getLastBlock(); + logger.info( + `Database integrity verified again after rollback to height ${lastBlock.data.height.toLocaleString()} :green_heart:`, + ); + + blockchain.dispatch("SUCCESS"); + }, +}); + +const stateMachine = blockchainMachine; +export { stateMachine }; diff --git a/packages/core-blockchain/src/state-storage.ts b/packages/core-blockchain/src/state-storage.ts new file mode 100644 index 0000000000..0274dc7006 --- /dev/null +++ b/packages/core-blockchain/src/state-storage.ts @@ -0,0 +1,240 @@ +// tslint:disable:variable-name + +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Logger } from "@arkecosystem/core-interfaces"; +import { configManager, models } from "@arkecosystem/crypto"; +import assert from "assert"; +import immutable from "immutable"; +import { config } from "./config"; +import { blockchainMachine } from "./machines/blockchain"; + +const logger = app.resolvePlugin("logger"); + +// Stores the last n blocks in ascending height. The amount of last blocks +// can be configured with the option `state.maxLastBlocks`. +let _lastBlocks: immutable.OrderedMap = immutable.OrderedMap(); + +// Stores the last n incoming transaction ids. The amount of transaction ids +// can be configred with the option `state.maxLastTransactionIds`. +let _cachedTransactionIds: immutable.OrderedSet = immutable.OrderedSet(); + +// Map Block instances to block data. +const _mapToBlockData = (blocks: immutable.Seq): immutable.Seq => + blocks.map(block => ({ ...block.data, transactions: block.transactions })); + +/** + * Represents an in-memory storage for state machine data. + */ +export class StateStorage implements Blockchain.IStateStorage { + public blockchain: any; + public lastDownloadedBlock: models.IBlock | null; + public blockPing: any; + public started: boolean; + public forkedBlock: models.Block | null; + public rebuild: boolean; + public fastRebuild: boolean; + public wakeUpTimeout: any; + public noBlockCounter: number; + public p2pUpdateCounter: number; + public networkStart: boolean; + + constructor() { + this.reset(); + } + + /** + * Resets the state. + */ + public reset(): void { + this.blockchain = blockchainMachine.initialState; + this.lastDownloadedBlock = null; + this.blockPing = null; + this.started = false; + this.forkedBlock = null; + this.rebuild = true; + this.fastRebuild = false; + this.wakeUpTimeout = null; + this.noBlockCounter = 0; + this.p2pUpdateCounter = 0; + this.networkStart = false; + + this.clear(); + } + + /** + * Clear last blocks. + */ + public clear(): void { + _lastBlocks = _lastBlocks.clear(); + _cachedTransactionIds = _cachedTransactionIds.clear(); + } + + /** + * Clear check later timeout. + */ + public clearWakeUpTimeout(): void { + if (this.wakeUpTimeout) { + clearTimeout(this.wakeUpTimeout); + this.wakeUpTimeout = null; + } + } + + /** + * Get the last block. + */ + public getLastBlock(): models.Block | null { + return _lastBlocks.last() || null; + } + + /** + * Sets the last block. + */ + public setLastBlock(block: models.Block): void { + // Only keep blocks which are below the new block height (i.e. rollback) + if (_lastBlocks.last() && _lastBlocks.last().data.height !== block.data.height - 1) { + assert(block.data.height - 1 <= _lastBlocks.last().data.height); + _lastBlocks = _lastBlocks.filter(b => b.data.height < block.data.height); + } + + _lastBlocks = _lastBlocks.set(block.data.height, block); + configManager.setHeight(block.data.height); + + // Delete oldest block if size exceeds the maximum + if (_lastBlocks.size > config.get("state.maxLastBlocks")) { + _lastBlocks = _lastBlocks.delete(_lastBlocks.first().data.height); + } + } + + /** + * Get the last blocks. + */ + public getLastBlocks(): models.Block[] { + return _lastBlocks + .valueSeq() + .reverse() + .toArray(); + } + + /** + * Get the last blocks data. + */ + public getLastBlocksData(): immutable.Seq { + return _mapToBlockData(_lastBlocks.valueSeq().reverse()); + } + + /** + * Get the last block ids. + */ + public getLastBlockIds(): string[] { + return _lastBlocks + .valueSeq() + .reverse() + .map(b => b.data.id) + .toArray(); + } + + /** + * Get last blocks in the given height range in ascending order. + * @param {Number} start + * @param {Number} end + */ + public getLastBlocksByHeight(start, end?): models.IBlockData[] { + end = end || start; + + const blocks = _lastBlocks.valueSeq().filter(block => block.data.height >= start && block.data.height <= end); + + return _mapToBlockData(blocks).toArray() as models.IBlockData[]; + } + + /** + * Get common blocks for the given IDs. + */ + public getCommonBlocks(ids): models.IBlockData[] { + const idsHash = {}; + ids.forEach(id => (idsHash[id] = true)); + return this.getLastBlocksData() + .filter(block => idsHash[block.id]) + .toArray() as models.IBlockData[]; + } + + /** + * Cache the ids of the given transactions. + */ + public cacheTransactions( + transactions: models.ITransactionData[], + ): { added: models.ITransactionData[]; notAdded: models.ITransactionData[] } { + const notAdded = []; + const added = transactions.filter(tx => { + if (_cachedTransactionIds.has(tx.id)) { + notAdded.push(tx); + return false; + } + return true; + }); + + _cachedTransactionIds = _cachedTransactionIds.withMutations(cache => { + added.forEach(tx => cache.add(tx.id)); + }); + + // Cap the Set of last transaction ids to maxLastTransactionIds + const limit = config.get("state.maxLastTransactionIds"); + if (_cachedTransactionIds.size > limit) { + _cachedTransactionIds = _cachedTransactionIds.takeLast(limit); + } + + return { added, notAdded }; + } + + /** + * Remove the given transaction ids from the cache. + */ + public removeCachedTransactionIds(transactionIds: string[]): void { + _cachedTransactionIds = _cachedTransactionIds.subtract(transactionIds); + } + + /** + * Get cached transaction ids. + */ + public getCachedTransactionIds(): string[] { + return _cachedTransactionIds.toArray(); + } + + /** + * Ping a block. + */ + public pingBlock(incomingBlock: models.IBlockData): boolean { + if (!this.blockPing) { + return false; + } + + if (this.blockPing.block.height === incomingBlock.height && this.blockPing.block.id === incomingBlock.id) { + this.blockPing.count++; + this.blockPing.last = new Date().getTime(); + + return true; + } + + return false; + } + + /** + * Push ping block. + */ + public pushPingBlock(block: models.IBlockData) { + // logging for stats about network health + if (this.blockPing) { + logger.info( + `Block ${this.blockPing.block.height.toLocaleString()} pinged blockchain ${this.blockPing.count} times`, + ); + } + + this.blockPing = { + count: 1, + first: new Date().getTime(), + last: new Date().getTime(), + block, + }; + } +} + +export const stateStorage = Object.seal(new StateStorage()); diff --git a/packages/core-blockchain/src/utils/index.ts b/packages/core-blockchain/src/utils/index.ts new file mode 100644 index 0000000000..5ef9a13ed4 --- /dev/null +++ b/packages/core-blockchain/src/utils/index.ts @@ -0,0 +1,3 @@ +export * from "./is-block-chained"; +export * from "./tick-sync-tracker"; +export * from "./validate-generator"; diff --git a/packages/core-blockchain/src/utils/is-block-chained.ts b/packages/core-blockchain/src/utils/is-block-chained.ts new file mode 100644 index 0000000000..5f3396a3e2 --- /dev/null +++ b/packages/core-blockchain/src/utils/is-block-chained.ts @@ -0,0 +1,12 @@ +import { models, slots } from "@arkecosystem/crypto"; + +export const isBlockChained = (previousBlock: models.IBlock, nextBlock: models.IBlock): boolean => { + const followsPrevious = nextBlock.data.previousBlock === previousBlock.data.id; + const isPlusOne = nextBlock.data.height === previousBlock.data.height + 1; + + const previousSlot = slots.getSlotNumber(previousBlock.data.timestamp); + const nextSlot = slots.getSlotNumber(nextBlock.data.timestamp); + const isAfterPreviousSlot = previousSlot < nextSlot; + + return followsPrevious && isPlusOne && isAfterPreviousSlot; +}; diff --git a/packages/core-blockchain/src/utils/tick-sync-tracker.ts b/packages/core-blockchain/src/utils/tick-sync-tracker.ts new file mode 100644 index 0000000000..ae104568ca --- /dev/null +++ b/packages/core-blockchain/src/utils/tick-sync-tracker.ts @@ -0,0 +1,52 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger, P2P } from "@arkecosystem/core-interfaces"; +import prettyMs from "pretty-ms"; + +const logger = app.resolvePlugin("logger"); +let tracker = null; + +export function tickSyncTracker(blockCount, count) { + if (!tracker) { + tracker = { + start: new Date().getTime(), + networkHeight: app.resolvePlugin("p2p").getNetworkHeight(), + blocksInitial: +count, + blocksDownloaded: +count, + blocksSession: 0, + blocksPerMillisecond: 0, + remainingInMilliseconds: 0, + percent: 0, + }; + } + + // The total amount of downloaded blocks equals the current height + tracker.blocksDownloaded += +blockCount; + + // The total amount of downloaded blocks downloaded since start of the current session + tracker.blocksSession = tracker.blocksDownloaded - tracker.blocksInitial; + + // The number of blocks the node can download per millisecond + const diffSinceStart = new Date().getTime() - tracker.start; + tracker.blocksPerMillisecond = tracker.blocksSession / diffSinceStart; + + // The time left to download the missing blocks in milliseconds + tracker.remainingInMilliseconds = (tracker.networkHeight - tracker.blocksDownloaded) / tracker.blocksPerMillisecond; + tracker.remainingInMilliseconds = Math.abs(Math.trunc(tracker.remainingInMilliseconds)); + + // The percentage of total blocks that has been downloaded + tracker.percent = (tracker.blocksDownloaded * 100) / tracker.networkHeight; + + if (tracker.percent < 100 && Number.isFinite(tracker.remainingInMilliseconds)) { + const blocksDownloaded = tracker.blocksDownloaded.toLocaleString(); + const networkHeight = tracker.networkHeight.toLocaleString(); + const timeLeft = prettyMs(tracker.remainingInMilliseconds, { + secDecimalDigits: 0, + }); + + logger.info(`Synchronising In Progress (${blocksDownloaded} of ${networkHeight} blocks - Est. ${timeLeft})`); + } + + if (tracker.percent === 100) { + tracker = null; + } +} diff --git a/packages/core-blockchain/src/utils/validate-generator.ts b/packages/core-blockchain/src/utils/validate-generator.ts new file mode 100644 index 0000000000..f517b58948 --- /dev/null +++ b/packages/core-blockchain/src/utils/validate-generator.ts @@ -0,0 +1,40 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { models, slots } from "@arkecosystem/crypto"; + +export const validateGenerator = async (block: models.Block): Promise => { + const database = app.resolvePlugin("database"); + const logger = app.resolvePlugin("logger"); + + const delegates = await database.getActiveDelegates(block.data.height); + const slot = slots.getSlotNumber(block.data.timestamp); + const forgingDelegate = delegates[slot % delegates.length]; + + const generatorUsername = database.walletManager.findByPublicKey(block.data.generatorPublicKey).username; + + if (!forgingDelegate) { + logger.debug( + `Could not decide if delegate ${generatorUsername} (${ + block.data.generatorPublicKey + }) is allowed to forge block ${block.data.height.toLocaleString()} :grey_question:`, + ); + } else if (forgingDelegate.publicKey !== block.data.generatorPublicKey) { + const forgingUsername = database.walletManager.findByPublicKey(forgingDelegate.publicKey).username; + + logger.warn( + `Delegate ${generatorUsername} (${ + block.data.generatorPublicKey + }) not allowed to forge, should be ${forgingUsername} (${forgingDelegate.publicKey}) :-1:`, + ); + + return false; + } + + logger.debug( + `Delegate ${generatorUsername} (${ + block.data.generatorPublicKey + }) allowed to forge block ${block.data.height.toLocaleString()} :+1:`, + ); + + return true; +}; diff --git a/packages/core-blockchain/tsconfig.json b/packages/core-blockchain/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-blockchain/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-config/CHANGELOG.md b/packages/core-config/CHANGELOG.md deleted file mode 100644 index 1158e8a0d3..0000000000 --- a/packages/core-config/CHANGELOG.md +++ /dev/null @@ -1,24 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Fixed - -- Stricter regular expression to avoid picking wrong config files - -### Changed - -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-config/LICENSE b/packages/core-config/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-config/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-config/README.md b/packages/core-config/README.md deleted file mode 100644 index 49ea1474a3..0000000000 --- a/packages/core-config/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Ark Core - Configuration - -

- -

- -## Documentation - -You can find installation instructions and detailed instructions on how to use this package at the [dedicated documentation site](https://docs.ark.io/guidebook/core/plugins/core-config.html). - -## Security - -If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. - -## Credits - -- [François-Xavier Thoorens](https://github.com/fix) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) - -## License - -[MIT](LICENSE) © [ArkEcosystem](https://ark.io) diff --git a/packages/core-config/__tests__/__stubs__/delegates.json b/packages/core-config/__tests__/__stubs__/delegates.json deleted file mode 100644 index c615b110c3..0000000000 --- a/packages/core-config/__tests__/__stubs__/delegates.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "secrets": ["this is a test"] -} diff --git a/packages/core-config/__tests__/__stubs__/genesisBlock.json b/packages/core-config/__tests__/__stubs__/genesisBlock.json deleted file mode 100644 index 1f6b5c1bf0..0000000000 --- a/packages/core-config/__tests__/__stubs__/genesisBlock.json +++ /dev/null @@ -1,896 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", - "timestamp": 0, - "numberOfTransactions": 52, - "payloadLength": 11401, - "previousBlock": null, - "generatorPublicKey": "024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231", - "transactions": [ - { - "type": 0, - "amount": 12500000000000000, - "fee": 0, - "recipientId": "DGihocTkwDygiFvmg6aG8jThYTic47GzU9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "03cb7bca143376721d0e9e3f3ccb0dc2e7e8470c06e630c3cef73f03e309b558ad", - "signature": "3044022016ecdf3039e69514c7d75861b22fc076496b61c07a1fcf793dc4f5c76fa0532b0220579c4c0c9d13720f9db5d9df29ed8ceab0adc266c6c160d612d4894dc5867eb1", - "id": "e40ce11cab82736da1cc91191716f3c1f446ca7b6a9f4f93b7120ef105ba06e8", - "senderId": "DUFeXjJmYt1mWY3auywA1EQSqfCv5kYYfP" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e5b39a83e6c7c952c5908089d4524bb8dda93acc2b2b953247e43dc4fe9aa3d1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03e5b39a83e6c7c952c5908089d4524bb8dda93acc2b2b953247e43dc4fe9aa3d1" - } - }, - "signature": "3045022100e3e38811778023e6f17fefd447f179d45ab92c398c7cfb1e34e2f6e1b167c95a022070c36439ecec0fc3c43850070f29515910435d389e059579878d61b5ff2ea337", - "id": "eb0146ac79afc228f0474a5ae1c4771970ae7880450b998c401029f522cd8a21", - "senderId": "DNL81CT6WNG1PHjobBmLvKwLV3UUscBymB" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "031137050d5fed0b5229b150257da2ac9c135efdf4bcb382b0ad0c197d7be458f4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "031137050d5fed0b5229b150257da2ac9c135efdf4bcb382b0ad0c197d7be458f4" - } - }, - "signature": "30440220124baaa04491287d0abbf5a167c9b0f5ac95c22b196f42ff3d275cc9a213c2fd02206e6ebada85f67063e642dbcde6b956f8c99c05f4b9c55f1551d3eebba6375043", - "id": "c9c554056b3428951633a7059dd64dfcbd776fef7f4a156ea362b37ee6ce74c7", - "senderId": "DG9LYv5rqX67wuGvGVa9is5k1r86LKCVTA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "037def83d085778d7767a182a179f345207953441089081f5bc13f86d3891308aa", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "037def83d085778d7767a182a179f345207953441089081f5bc13f86d3891308aa" - } - }, - "signature": "3045022100900cea3c2df393414899c9d74db57d89c9f311c70d08b974d0fd4a98bfae2fc902204a2aa51a1ec71da27c26afc033de6bd2d15978813c120c95e1a4dafca75ce876", - "id": "c82ccaa16be0e3c7ff4a53e2807968b71a0d88115223c3af2eb320f32449ac32", - "senderId": "DMSwarrHg5N9ZAZ6nsqPuUjyAU6gdRAM9d" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "033f28ad2e9b897d46f1e67c7c52070e9ca46b04c0679ebb21fb236719e38aade3", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "033f28ad2e9b897d46f1e67c7c52070e9ca46b04c0679ebb21fb236719e38aade3" - } - }, - "signature": "30440220285188d8900cd3cffccf5e1de305b18856451dd04d2ed21165dffe9a7ce4afc1022009457be6bfe536971697105d47ad1f829738a5cacdb27a23c5d1e8a8dddf3ebd", - "id": "ee6a19fff622ab4e6e96d159396de56d6034b4b18a9cf5c99efcf4e61b28e15a", - "senderId": "DFcYHfCwhGWcBNy6cp48wy5SfXbQmfBYgT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "023e577a7b3362e0aba70e6911d230e86d729b4cb640f0e0b25637b812a3e38b53", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "023e577a7b3362e0aba70e6911d230e86d729b4cb640f0e0b25637b812a3e38b53" - } - }, - "signature": "3045022100afa56542dd473c424b36d4d9f24da68180cfd90527681ab84098f415b2544a8702201e8ebdd619a2dd200e37a57c39a4529afe76d35f6089c00f6dffba6bf7b8a836", - "id": "0dcd6e380bd7eaef8724f64f4b86104ce7497308dacf775afbe6ec0d401007fe", - "senderId": "D5e2FzTPqdEHridjzpFZCCVyepAu6Vpmk4" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02af5e6341efc14f4ba39a9ff65e151cc7304fc742ce7b2678d9aa446c555ee9c1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "02af5e6341efc14f4ba39a9ff65e151cc7304fc742ce7b2678d9aa446c555ee9c1" - } - }, - "signature": "3045022100c8980155c8f8964d76baf3e8d690075708f1a84757c1de52e311772466382da2022012599acfc7839fa1ef6bbd445ab34555fb718491db3089f40d4842b1bc2d3178", - "id": "8af6abb117c69c130e388970d595b741374b1bbca709d9e91459e9e3c721397b", - "senderId": "DDLbnve6XK48cGsQiFhesUJQRQdKkZTfPh" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02845161cfca4d6ddde8e0d53538b6f881fb3ad9383cd77cebc55375dd6fd17663", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "02845161cfca4d6ddde8e0d53538b6f881fb3ad9383cd77cebc55375dd6fd17663" - } - }, - "signature": "30450221009bce7c5c10a4b6306cebe5724adfd3de049a425c44dd314a10154774764c11090220070fb775e71dda6a68f7fc9e0c762fbf96021908911f0de0ca8e9b0c613cb896", - "id": "bd346035d4516b85fb3a2cce6260fdcc6f1c434999e586978e065de3bf98e02a", - "senderId": "DDAHPjVTTV3uur653TB27fcLGh7XXWnvxW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03f264a6d2ebb62279313a6fd7fec4e2244785839b625a0b0c261e689ce5401d87", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "03f264a6d2ebb62279313a6fd7fec4e2244785839b625a0b0c261e689ce5401d87" - } - }, - "signature": "30450221009f74425c2ec50dbee462e735dee3e7917c8433fd5250ff09af4506c38d2df05902206a14a19b9a5defe3c8c59c77d52c182ea34d81d2e0b05dc5925133f2829a1960", - "id": "b48068fb7c848ffd57e82a4d381f53bb69916f3943e0e8935971a028ba245564", - "senderId": "DFHdEBuVCz5zfj8yeo3BmKEdsEKpMaYRRw" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03efd265a086c2a099cda4f4fd202adbac07567e1229ce5e6fe39963b714c1e2d5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "03efd265a086c2a099cda4f4fd202adbac07567e1229ce5e6fe39963b714c1e2d5" - } - }, - "signature": "3044022004df492965ed328134aa6443d38ac4dd951a640e00330da9aa4e80c1577af41a0220588f030f5f9584959647898bb977a1ffe6bba639b1c64a728880f2cd3fd7aa3c", - "id": "73b3b4375e39aabe51ec205559cd728a18c987dabaa0599c611b3076c38c7a49", - "senderId": "DL7Y6smfHHs3Ms3hAYmSYYd5PZukmtDY1i" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "027d616d20f03c375067676c79ff9787e8e42991fbd9e878501d704d23d246d9b0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "027d616d20f03c375067676c79ff9787e8e42991fbd9e878501d704d23d246d9b0" - } - }, - "signature": "3044022051c2f8af62163ca621eeb3087a35bfaca0d679f7be8b19a25972f5a4b24ad8c90220422f3e0e480bf1bf2211e871a102edc15a957c0f97a553d9d707418e6538df26", - "id": "80f1d01158452da31d44f0c24f464a0ade37da51d2f61356ad75a019a91a1ff5", - "senderId": "DBVoRSXBHBPPvssBXrswv22r4dUSpN1fbA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038918951152a37b74dfe61115f83e4b5e3521145065650c4a6d3e94add57d9a9b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "038918951152a37b74dfe61115f83e4b5e3521145065650c4a6d3e94add57d9a9b" - } - }, - "signature": "3045022100facf6ed992c28d41595419666b006800fcb33c6bad4b522e013b4d688e51dc8502207695e968059f7a35486389c430d6a3037e69d3e5f1d4f0a294d8818e4750cf0d", - "id": "86d76b0aad8f496d8c20926bfdeb50ad10db242ea6152b68266680c48e1e1aca", - "senderId": "DHsSK81gRWjgNx1A9gtHgkRsEwshsog7AM" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03231d8f2f39925fa79efc8f8561e6a8d29b95164a753cbb604a46e8a2e96606fc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "03231d8f2f39925fa79efc8f8561e6a8d29b95164a753cbb604a46e8a2e96606fc" - } - }, - "signature": "304402204c627ec3d24fb7b4f86709c0566cee9909ebddb26039e87a2fa673f1f7227362022003be5aa3303b8f4cdab768f80b4699440a61814950cab0fd983526771c4c52ec", - "id": "464614909ac7531a016a0489d78defe262dc0934324f41199975ad42a86f37ac", - "senderId": "DDr7UTGQuPTjxLDWZ8RMjWJMKNXAMj3Bor" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021e6d971e5885a3147ddf1e45bf5c8d0887ad9fc659e24bdf95c2c9607e7e3fe8", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "021e6d971e5885a3147ddf1e45bf5c8d0887ad9fc659e24bdf95c2c9607e7e3fe8" - } - }, - "signature": "3045022100898e59efe518745d3eb3f2b16f7b6192e3289bb4289d43013224549f2015aa4902204e7be92cbba37a05551151e46224da4e5d0ad86ee2106d3a9c0b9afee5f1c4cf", - "id": "9559866ff439959529f69b0947ad2e72d739511ee1f6533c0bca2ebd6dd4ae4a", - "senderId": "DRXNNQ9gQXh6VNUVKaAn9xHAViyiHKtBHZ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d5b3efbe98631443c5cdf4de8a610dd2655b86427bf70aa209451b54256f6758", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "03d5b3efbe98631443c5cdf4de8a610dd2655b86427bf70aa209451b54256f6758" - } - }, - "signature": "3044022037fa085e37a582b2e0b3734d44b813bb18be939f73100c5b6f977d4f53ae708f022064ae54f6a1b17b193ab6b6d633f7b7a7b8171a158cdba7480afe380f383930dc", - "id": "7bab92d5397a4ad291c5d01b8d681e480d19b437a7ab5cbd4c6807c96ef2716f", - "senderId": "DT12wf9erZyNJbBQrpbPDmfH3J8txiDgTE" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0297f2e8e609b2a6799214481e7573a043a197f8adf7b8bb306576fc3da83d2aaa", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "0297f2e8e609b2a6799214481e7573a043a197f8adf7b8bb306576fc3da83d2aaa" - } - }, - "signature": "304402202eee94bc3b53c64f8dee7790fe3eed8639da8faf0aa1f785e921cf139df0fb7e02200224efb0c07ae3972287c12a32143c1356adb93e00ac9e04a1358c8245a24cab", - "id": "1e59740fa596b615231660974d0b656122b799a8b13102ade8c1b779aa5de7b5", - "senderId": "DKGYWPSqa4m4z6h3433rNFbWPDdvHj5wwd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0361b914fd5823bf39ae467e95d99e9f6ddb7d85cc6df3055ce00274b8e4a976cc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "0361b914fd5823bf39ae467e95d99e9f6ddb7d85cc6df3055ce00274b8e4a976cc" - } - }, - "signature": "3044022002ad92b9b9d81dabf96ac7d90034debc55eeeae879b3fe6ffc026bde86bb7ad902205c57d31c5e5e0099b504ba4c49e220a00ff325dceb64c46aefbb7a0ad8570099", - "id": "bf305776da902802923c19b9d2c7f1a809b0847992131cfa578d5e5518c924bf", - "senderId": "DJshaeFyHcFTjiGJnVPaDmFXhnJ9bp96i5" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03fa6bc09bd2ff348b304e0cfbc2d2ec50aa3b9aee0de6a66c13fcd8ee5ac891cd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "03fa6bc09bd2ff348b304e0cfbc2d2ec50aa3b9aee0de6a66c13fcd8ee5ac891cd" - } - }, - "signature": "3045022100be50b19c17a9ff221aae20394a45d92ea47e8c1072b6d5a302937d2fc48cba8002205e9bcb3471a734c07ceff0083ad9ba1570507a29e5014e889ba42a85e797cb5e", - "id": "44e48364b5b8cff3c68ae03de7dfde8d7ba6bcb99bf82b32fdc8bc3d0d9adeca", - "senderId": "DSuNttSb1UvCWg8iormfwPwi67EA84P5Mu" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03241957edca9ed28308e35cbf36762d22de706ebbd7c6a3a2d235d905d660c5c7", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "03241957edca9ed28308e35cbf36762d22de706ebbd7c6a3a2d235d905d660c5c7" - } - }, - "signature": "3045022100c11f8b863133535192e6c3fff20253a2695a2df74cdf1445d4ca0966803f708c0220200d4c2723d84f6334ba5d1cc1a0d45854867f4523fbcc9d09b3d53dd1972950", - "id": "5cba288f9ffc1361ba8f7f19f28347ffd917f37df8cf46ba1e0816725f288528", - "senderId": "DCZt1ozEVvPdYVvkHmUKK6k7gnyNNQDpMq" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "035ae2364c838bc21edf4c04a99c85799f26fb02cc0740c5a1c67d4dc1748ff913", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "035ae2364c838bc21edf4c04a99c85799f26fb02cc0740c5a1c67d4dc1748ff913" - } - }, - "signature": "304402203066f06a1c165795d8a069499a8c0998913ec93e689219f14145754aa3e26e4e02206e9f88da16f1f8a8ebaf481eff798452487738714fe9b5694fec6a5ef8c152a5", - "id": "ada1696532f7faad1dda594bc6db7bfc029a1759402c924348b74222873a3a27", - "senderId": "D7JyqWMPKhhRNQcKTAvrPGBjEjjBcGgPca" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "023aff4a16c3876e885aea70e5bce9734ce5acc95a2c41c9783f5acd617f7c7533", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "023aff4a16c3876e885aea70e5bce9734ce5acc95a2c41c9783f5acd617f7c7533" - } - }, - "signature": "3045022100f5150c23596b9479c8b277401ab9e7da9b2275436f3927dabd70395e52c3ea7c02204e318d498b0176b5f05bb96418c49da3375a8d9b47b3b1e72a6f4db30b3f8c34", - "id": "e186a679f2e47300ec2f24c670192bcede1cb12f359cb8e827374b22f41fbe12", - "senderId": "D6itxYJr4n7ZZk2bd9cZbJE1xaDmpfkNFL" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0217d7ce9c3754f7fc7e5b4c64a1ff397dc75931cd6c92e32d8b42068ad50fe4eb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "0217d7ce9c3754f7fc7e5b4c64a1ff397dc75931cd6c92e32d8b42068ad50fe4eb" - } - }, - "signature": "3045022100b84f69a7ff67ed147fc0a750c3b7b2ecabd582b6d0cb698c0bb4a531daa6ca46022039d2722e486e1674d0db422078d63fcdb90b21bed0dcc1265adff72d0c2bf8b9", - "id": "86d9d146b62dbafe212aba5ec9764223b67f72c3c1aa93e54a270e3a528a8b20", - "senderId": "DDy4aKhF3cMadGhjFZnjaA1tx2rwnSEWcc" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "024019207f50dcb3e8aeb9ac1b00993d2bf131346e7e6d296429ea813a8373818e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "024019207f50dcb3e8aeb9ac1b00993d2bf131346e7e6d296429ea813a8373818e" - } - }, - "signature": "3045022100aa83596b740639ee8947aa6d0f0ee123e4a5b87c39a4c6dd8a50304d4a7c97d102205fd45f85f5bdb076585a77888ef880bea52ade689731dff694d777de34913efc", - "id": "6301b791844e02116df528b1ea46d788e91521189c3828ce224e45a1b72cda59", - "senderId": "D6BwyDJkNFkaDLedcJTE4rPUw5bRtb4K8f" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0275db912c21dca0f0213a76f4544137d7c741b47f281cfd4f8b7cb8187e7ce3cc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "0275db912c21dca0f0213a76f4544137d7c741b47f281cfd4f8b7cb8187e7ce3cc" - } - }, - "signature": "3045022100c7eda0d9cd7ef522615643d1b985c73add2d3612344bdcc0117779fa4f4f54d302203e33fb5d185f5174e9cb7634a3d307b74d3bb56cc2354024ce69c74905a85203", - "id": "eee776fcb8024469eacab3e4b23c3d14185326431369aa84f17921abab8ad0ad", - "senderId": "DHQSmrRdfYAp9Y6CuebKnkoQNzuN7Pk2oQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0331c615ca4bc89d4eeb8d7a9cfbb5c0d4ce49d2f480afbce499b0c7f8c6a24f2f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "0331c615ca4bc89d4eeb8d7a9cfbb5c0d4ce49d2f480afbce499b0c7f8c6a24f2f" - } - }, - "signature": "304402203e69be3a73c5917d89d58f3c0ae18febbbf364d3f9dfbec6b526a5294f9c435902201750bcf6368c181aabc53c73fd271a2967a6f215e1d0506eded5dd1800fea1c8", - "id": "ec3d17c6d38c0b9848c7cb57b968efd1f3872b1d1b8bcfb74bae2b0aaa15877c", - "senderId": "D6EVFQx5Z7M2X9DWXHtfX51CtVekuKPMQF" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0338ca9b719f8047580eed23b64a40aecad3803a12c0dde83e3ec2c2a9bfaa8147", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "0338ca9b719f8047580eed23b64a40aecad3803a12c0dde83e3ec2c2a9bfaa8147" - } - }, - "signature": "3045022100e0bf90949739012b641793da162b3daa88b34c8753ee31b26850729e9df579810220439a3f2f1b8e719767ee68df46f4bc1f18c8c3b2da4118edff22396616d319fb", - "id": "14cd65c5f28f4cefc7c0157518a24f90c2260eb7166105b6b3358d91164ddf39", - "senderId": "DLCQ1jPsYbBCV7JfUJTasKbKoyGbK4a4HG" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03127001718bee76f14133272f0f4a928ffa8c2b38cafd94d7100253dac732c644", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "03127001718bee76f14133272f0f4a928ffa8c2b38cafd94d7100253dac732c644" - } - }, - "signature": "3044022003d2e76aca2848aedfe25415c11b9368dc72f687b66bef4527b40e2997b86b8c022076f7f82cbeb282d26535a2c1f0af0f02b48025d42c1bd56ac687fba1a3adb706", - "id": "0daff3992b54b1384f52f751c933c727cbaaf4fac435eba88a1817a425753614", - "senderId": "D9rv3h61heDYHQ3b3Xk3V5epHSTTC6Vn1d" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0241734825ba45b6de29d6f26242c25ae1ef125b82615ee89a9fdd5b0f3c6b5132", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "0241734825ba45b6de29d6f26242c25ae1ef125b82615ee89a9fdd5b0f3c6b5132" - } - }, - "signature": "3045022100bb2903424bcd0a72da531470779144d60286191bea1b200c5617ae4f92229ba6022046a876e3e6cb85469a16f34d2f937e2eef787011c6a313ee50258f15116148ac", - "id": "bd17dbd23f8dbba2736688702ac185a87c88c43b24ee6d7764a5b4138b2f38b7", - "senderId": "DAcQPbKa8zBWwDHbxj37N13C61iseMDWM9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03b9409203d7091e3f4d49168529b749e942ed18f21beddd236d57d692f09a8f86", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "03b9409203d7091e3f4d49168529b749e942ed18f21beddd236d57d692f09a8f86" - } - }, - "signature": "3044022016d7ecfa776930a6f83464548e7a686735fde752903539a38eb9da0ce2488bbd02203c5e23a4072c8de35a90b296145cce3156a31cc0d754b8a37d363fb088bc7387", - "id": "16e02d3ef24dca4b03a1e489e20335224f18d888ed04f7e3512572f8e0cf92ae", - "senderId": "D5mmTaDAMSyPNKiDKrqwTFGWzWrZA3xaF8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c7b92a2d0027309e21855cf9c42a432b21ad13925e9dfc206f9c01e18fefa08a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02c7b92a2d0027309e21855cf9c42a432b21ad13925e9dfc206f9c01e18fefa08a" - } - }, - "signature": "30450221009de8828a7ad87cb5d52900e09d5beb680f9edc7640a3707d08a379511a7ba0f102202aa1d9294f9631f1325f252adb87c0d866e7398ce410037a42dc861d94308e15", - "id": "fece556bee4de2c7f1bb3099a05a84a33d0c963979fe1a222a899c13b7abb1fc", - "senderId": "DJ3NywAwQh4srbooLH1jTs9ma1hJE79v3z" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0221297804a26a93bb441a9d20a2916abf27fa7b29967678ef1a7a58062f73f40d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "0221297804a26a93bb441a9d20a2916abf27fa7b29967678ef1a7a58062f73f40d" - } - }, - "signature": "3045022100b969611ef532557fa3da8a0325b2c88f3ebec954d64f158431d86b8e07929ea50220520affdcd0728cb7c5f63a58a1200d44133e90b1f7a6a9e28744ad6b0dcc2a75", - "id": "ee086317ea2fdc522f5eb502a0db9f3d4955b2318559e40a1f22a3f5f8d6344b", - "senderId": "D5P7eti7FUY4Tk5KXoxdf2tDAVQrRVCESA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "027f504f6f20648e3bf171952629c7b868a2f799aa4b60f8eb3fe96afff16bbef0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "027f504f6f20648e3bf171952629c7b868a2f799aa4b60f8eb3fe96afff16bbef0" - } - }, - "signature": "3044022006be7cbaa74089cabe47d02621f756762587d210a3f211ee941b5fcd0650908f02207d4040408bd25a2de03e5724362735ee8ad36c099b0c16efd4716e1dd7ec62ae", - "id": "764dd21aa4d0e2e0fa17bb2ff5e7ca304995d9e3593542badecc8ed24d5ea3ea", - "senderId": "D9q26yBTrEYuxHg7bbfZphv6129KvLu4v2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "031954315b84db8f49ab7ee21357270450bb68d06b34472e5e93ddfa5710edc0c9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "031954315b84db8f49ab7ee21357270450bb68d06b34472e5e93ddfa5710edc0c9" - } - }, - "signature": "3045022100859f93df994d86995fdf834bfe86b41eebaa04e5ab7d09f0b37acb50d313cd9802203c8993b793602c96d305fa795a9f2459f4706b340993584f3c56579392c0995c", - "id": "efd9e7c638afe62bec9be61783193ea52eea7b335053bd5af6c758d5b0e5847c", - "senderId": "D9iPFb5kAVnuDdomehRP9LncJj5ng2vrsr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0267b310eac2bb0d6594de382a1ab74ac75b91e9d64a590b6249247b10fd9be829", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "0267b310eac2bb0d6594de382a1ab74ac75b91e9d64a590b6249247b10fd9be829" - } - }, - "signature": "3045022100a678978ab899e3903e760ee98640e3f658792a096a8d771c575944af6536cfdb0220428c312f1e0eb4be73ce4b256a754447570176200cfb6c09b3eb55f66526dd80", - "id": "70edcce5df67a250b6ba3567879bae6379ce4c688597fcedfbfd0313da6998e8", - "senderId": "D6xZmtyBzZKCEkK29JNPAD581TJ8XXrXYn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "028f32320c66a89779756b04946d2aa256dff6cd547349d46e1938710063e387c4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "028f32320c66a89779756b04946d2aa256dff6cd547349d46e1938710063e387c4" - } - }, - "signature": "304402206bc95876897527b39eacf4c961f9c036a9c8a0e53a17ce925c592d079fa643030220096e115d7fbd54aca4af7f621d64178dfcf2c13361106a3e3b5025dca97b44ee", - "id": "7f23f44157f3a677e81514fa431227410a27442e5fd1f2491b177c0f580f296d", - "senderId": "D9dW4eXJjABDQXSQB9GtvY5UBuRWWWejWb" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0218b889a24988527ab3948d80f97cfc37b923082e1f0398bc162190fd66ec4dee", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "0218b889a24988527ab3948d80f97cfc37b923082e1f0398bc162190fd66ec4dee" - } - }, - "signature": "3045022100c40a3f4cf15f9274e2b25ca8608cb965316aa0f00fa77817b79620ad8ccbdd5902206203a1043b03ba58aa9b7399694f8215cf45d30eb0caa748cc06f1a85a8faea9", - "id": "a65244ed17a9280aa694abdf6804b1a0b78dfc052b4845abcd3c89380159b29e", - "senderId": "DFHK7SdmPdjxNZ9uweqLZAv6v5GQ1NnBNe" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "035392ee88c60617764b4fe89ae2cc96560dfa5f992b03be31ce5680db9b863f73", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "035392ee88c60617764b4fe89ae2cc96560dfa5f992b03be31ce5680db9b863f73" - } - }, - "signature": "3044022036200c3191f8f01b77676644b9b94728b5afb2ab2de8c5c7c5582e795465661c02207848f1f2f0ab378d8906fd45aa048f354d5dbac4cb87c15973ffa86fe84ff0cd", - "id": "219e0942afe5f65c548ec2118a1c49febb7ec03fca4334ac16649062db9d146b", - "senderId": "DSh7AAC9KahXU2JZ539HAqEa5sHafxsxDQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e75127d1deccf65844a1761bd26611b6c65c5b51a52eba27e3ee20a539fd63f1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "03e75127d1deccf65844a1761bd26611b6c65c5b51a52eba27e3ee20a539fd63f1" - } - }, - "signature": "304402201a2990b2baae72f5cc8f2d1890f328e4082af0cf2a787d8f05208c3424ce089d0220790dbc7606dd6c03568fd0a771e9e8e89557257238ae90cfcb3bb8f3b475987b", - "id": "ee9ad2a66e9b2009a9fc671f80d0493803fc422161140169c7bc1fd401cd9ad6", - "senderId": "D85WuxGZrFs1QUYTvnRpmc6dd8rmBbpnaX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0329fc1580906307ac9f2f55cec66e47983f8287d542408fb19f473a305d3638d8", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "0329fc1580906307ac9f2f55cec66e47983f8287d542408fb19f473a305d3638d8" - } - }, - "signature": "30450221008f66e89ec4c7af4b77e5b7ff36c542cc02672c8df70806b5a0fab7a7e8c7067502200d99ba19ceb1b471c39c4e95107ad6f8b978a623a790080b16f863347fe06b4f", - "id": "dd3077ed04a76343d340074270ce9826354802bd99e08cb864c1c5ad09f367df", - "senderId": "D85kwsBJKZ4pw5uQpc81eRj95f6a536AP6" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "035ec848e9388877dac88f121d19c8f5e870ac90d8ccb0116be9f734e4bd1a9405", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "035ec848e9388877dac88f121d19c8f5e870ac90d8ccb0116be9f734e4bd1a9405" - } - }, - "signature": "304402202b220d6c028bc23213edddaf303f18eef059551891aadbf7a4b4d7d3287457bb0220245678354bb8960b42ba2f2ceb12f926e82ff0d027b44988d799c8c0d8d7d9f2", - "id": "3afc6ea52b8edc7df0230ceac71baf45460f3bd761c5e75fe796bc7415063220", - "senderId": "DGBJdDadBwJD2xY8VsdAykdd6vPakMMUt6" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0356f5885306e45402aeb354a74d13c104699b3b53da46a5e922e4a6d6132a67e8", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "0356f5885306e45402aeb354a74d13c104699b3b53da46a5e922e4a6d6132a67e8" - } - }, - "signature": "3045022100f18bf2e013f2d9dcac013a76037d787f79baaa65f4f31ffe2b4ed8de249bdc8902202abcf77e809599d3e3a96225363c8e760ed4b4e20f97645547b381dba830c3da", - "id": "aea1fc173a2f4a9233b0fe59a5f6804167bee5658cb3e4e19dfe2be20f5772cd", - "senderId": "DG4VbapL3H39NJLB3DqQEefU47EMVqtxVw" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ff8ab980434516ca28c982d0ecc8fc3107116d6c8b3e09c7ee5033f32adbd2ff", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "03ff8ab980434516ca28c982d0ecc8fc3107116d6c8b3e09c7ee5033f32adbd2ff" - } - }, - "signature": "3045022100e938d9901afeaa5a56d18abd9292ace93be03c84c09a6c4cb58fca96dfb54bc502201e921d27f9886d189f803b14d93655a42c4e095d49ee61051a4e70c7a173f3f1", - "id": "f18426d3ef81d4b7bf0337d70afcecddbd6db2206a2f139f1ca5823c381c7817", - "senderId": "DC3oNWedP48ypGxAeKbFC7gMjWxcNc2JhL" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0393f1590771a8ad1cf2baa086858f3029c4444cb82243917a7011f1f66cf8fd05", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "0393f1590771a8ad1cf2baa086858f3029c4444cb82243917a7011f1f66cf8fd05" - } - }, - "signature": "304502210095745c36a8af07e21546bd064f1ed1bd90e6c2a8db9c0c8e4853d0a8255443db0220259d2ce3677abb42f08b9d22aa13bbe383fd882ed38911b738ebaefc04589694", - "id": "6c51bea35b5e3270dcf7b7dfae8d984e19f476ea7e0435f157c4e0d22b7e7ea1", - "senderId": "DJm2sfcUKhyxakowY9TjyAytkdq7JrFgVj" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0234e24ff1dbc447c804eb385cd05bbd1dc59ef03b44a3346b13e7cccf00b61075", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "0234e24ff1dbc447c804eb385cd05bbd1dc59ef03b44a3346b13e7cccf00b61075" - } - }, - "signature": "3045022100ee1e8df480f2be042386d383d776b3fd6bd2d3f5a9035071153f23dbfdceeaae02203a0834aae4834da3ca7858779d474b9255ead754867d5b4a18873e9ecaa5045e", - "id": "66fb3e36233f4577ba585ccd7daf83e62d8df262d3d832b806479ac67c1ef35d", - "senderId": "D5oS8xfNebiPsjpwPWoZS6sA9qcYjTGT5h" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a5789a4486f20f1fdca78a52b528b3bf9952e7c057de71a22adcfb444ba4c5d3", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "03a5789a4486f20f1fdca78a52b528b3bf9952e7c057de71a22adcfb444ba4c5d3" - } - }, - "signature": "3045022100cb037530bcff9a4d19899431648747022c28aa3239563379d96692bd525eb38902205f3cabb8dd470d9eb3d425e333ad1bc9f0643d489c600a811748fb5f4a203f7f", - "id": "5df9c5e350136571af4b86697bc9d4cfca3ff8b669e254b36f00be1dbde063f7", - "senderId": "D5SzHHdPdGqYUkH7BGNkmGHEUqfZrWb17r" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0347f692345fa7bf90e944eb55246da5f9f595d3f5a20ad50aeb6f9b973aaae17e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "0347f692345fa7bf90e944eb55246da5f9f595d3f5a20ad50aeb6f9b973aaae17e" - } - }, - "signature": "3045022100c377efe5ffab58017473699cd7c839dcf48fa5b20b5ddf9bdc4801e22a579b2b02204d35c1a1416069544e3ec01d2ce21bb409f9f2fa4adedc8c03d6417c034a3fec", - "id": "da4cfad78e37d56421dd6676e5618a507340ef1e496831d1968c509e35ef9202", - "senderId": "DCLdibuZB6UsJP8KmdzcDLWzizrDtJQuxt" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "028a32b441377a69aa76e867026f3109b2f0aef8651fe91e2a4ab01eff102a6b98", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "028a32b441377a69aa76e867026f3109b2f0aef8651fe91e2a4ab01eff102a6b98" - } - }, - "signature": "3045022100e098672958be15989bb125d9018adb4a54e95ab664e64a673997e617e28b39df02206e8459997074d5976b77f90eb9d7180e9d4a0e0efdf433958ffeb2f04d9de382", - "id": "85bafcd07e7ba47ec95cb5b5a6759d4f9f87e036bb7660c7717504e845ef975e", - "senderId": "DSkivgRyimdAVqmm2ZAKwKmKN39WEbbPnL" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0223ac52179903e79865b9a98cf0b52ddc1ab46180c157e8f6bd1e63e7f14fcf31", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "0223ac52179903e79865b9a98cf0b52ddc1ab46180c157e8f6bd1e63e7f14fcf31" - } - }, - "signature": "304402200a005716f67d6cd3963a3c752c95f1bca01aa127c91ab1a632eb3022d11e3e67022024c4746078e440da441bcb366ee8999ffd2419e9a6f9cbf971d696d5b7f8733b", - "id": "0df1ed07d3f95ddf0385bad83a17b3a8fde6bd6532cd3479e48668064672b34f", - "senderId": "DDgKyKqdA6SuamB1eW77WvFu6RQFMZoU36" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "039de0390f28c7731d86ae7006a31888f12856cde3cc3c2619d4d4a42b6dfd6c51", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "039de0390f28c7731d86ae7006a31888f12856cde3cc3c2619d4d4a42b6dfd6c51" - } - }, - "signature": "3045022100efa5d51ca79992be4a87af049b3e9ec1b796576e4d937ea9e3760ab0bdcd301e022027e22a6c3395df155bd399643c241e4cc317eaead1f273fd7a709339dfa9dc99", - "id": "436bebc107fad38e944fd14785e09f0600df4d75d31cf3eac53f850462d0be74", - "senderId": "DKCaoaXApw1xE7K1BJcVkr1KGzjKmFWyTk" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02b87b0e70a7ae10613390f405620e24c495ba2b0cfcdbc67688e9b483dea564ee", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "02b87b0e70a7ae10613390f405620e24c495ba2b0cfcdbc67688e9b483dea564ee" - } - }, - "signature": "304502210096cdd35f803a37730ac73a97a23061dceac96319c67bfb1ddcfbac737febe96102202fa0b279f697da3afc043ffd3ecc838789be07ff119b5527a5c13468cecf66e9", - "id": "bb65f9dbe6272fd07a555fc86762d6a487f538b972f2926ff7698cdc906a32df", - "senderId": "DJQXFKEguZVabsAs46JbXXnQJ5jFhUtN9m" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c9f8f4001216603c152b4b4429c2ead322ac34672999e808d567a7d1140e46be", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "03c9f8f4001216603c152b4b4429c2ead322ac34672999e808d567a7d1140e46be" - } - }, - "signature": "3045022100ee961089d02d7bb68fe2257f6a972eeaf6e2c1a1ad2f491c417e161fedbb556b02204c834644e5b5cde9a0b3f92fa23bade7670efab0a067597f6c151ee633932706", - "id": "cef44df9684f05dab67c0568a2c5295bb50cbb3c88f5cfbe672365bda274620f", - "senderId": "DKpt7cm2tZk4RPLyQ5ugwEH7gkriRaA7ov" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02cf70f73328d490cfb03ee822d3fc0cf9259d67c0564e843491e739501809d657", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "02cf70f73328d490cfb03ee822d3fc0cf9259d67c0564e843491e739501809d657" - } - }, - "signature": "30440220645b912b60f829c0bce58bfe9890ef9253418b6898416aaead663bdf158a99f2022061abbbabd454ec7f7e3f4b502216eec28110e945a4b9b913b1fc0b9758e7e6e4", - "id": "09408dbcf3e3e0835bf92a05330c023a7d6471f3825301a34efa094e0fd4fc30", - "senderId": "DQfjSqDuKr5YZaLAF8rWpFMqMYwEbPtGKg" - } - ], - "height": 1, - "id": "13149578060728881902", - "blockSignature": "3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8" -} diff --git a/packages/core-config/__tests__/__stubs__/network.json b/packages/core-config/__tests__/__stubs__/network.json deleted file mode 100644 index d57a1c1694..0000000000 --- a/packages/core-config/__tests__/__stubs__/network.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "devnet", - "messagePrefix": "ARK message:\n", - "bip32": { - "public": 46090600, - "private": 46089520 - }, - "pubKeyHash": 30, - "nethash": "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", - "wif": 170, - "client": { - "token": "DARK", - "symbol": "DѦ", - "explorer": "https://dexplorer.ark.io" - }, - "constants": [ - { - "height": 1, - "reward": 0, - "activeDelegates": 51, - "blocktime": 8, - "block": { - "version": 0, - "maxTransactions": 150, - "maxPayload": 2097152 - }, - "epoch": "2017-03-21T13:00:00.000Z", - "fees": { - "dynamic": false, - "dynamicFees": { - "minFeePool": 1000, - "minFeeBroadcast": 1000, - "addonBytes": { - "transfer": 100, - "secondSignature": 250, - "delegateRegistration": 500, - "vote": 100, - "multiSignature": 500, - "ipfs": 250, - "timelockTransfer": 500, - "multiPayment": 500, - "delegateResignation": 500 - } - }, - "staticFees": { - "transfer": 10000000, - "secondSignature": 500000000, - "delegateRegistration": 2500000000, - "vote": 100000000, - "multiSignature": 500000000, - "ipfs": 0, - "timelockTransfer": 0, - "multiPayment": 0, - "delegateResignation": 0 - } - } - }, - { - "height": 75600, - "reward": 200000000 - } - ], - "exceptions": {} -} diff --git a/packages/core-config/__tests__/__stubs__/peers.json b/packages/core-config/__tests__/__stubs__/peers.json deleted file mode 100644 index f1f6038735..0000000000 --- a/packages/core-config/__tests__/__stubs__/peers.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "blackList": [], - "list": [ - { - "ip": "127.0.0.1", - "port": 4102 - }, - { - "ip": "127.0.0.1", - "port": 4202 - } - ] -} diff --git a/packages/core-config/__tests__/loader.test.js b/packages/core-config/__tests__/loader.test.js deleted file mode 100644 index 8db2ae1f63..0000000000 --- a/packages/core-config/__tests__/loader.test.js +++ /dev/null @@ -1,45 +0,0 @@ -const path = require('path') -const configLoader = require('../lib/loader') - -const stubConfigPath = path.resolve(__dirname, './__stubs__') - -const stubConfig = { - delegates: require('./__stubs__/delegates'), - genesisBlock: require('./__stubs__/genesisBlock'), - network: require('./__stubs__/network'), -} - -beforeEach(() => { - process.env.ARK_PATH_CONFIG = stubConfigPath - process.env.ARK_NETWORK = JSON.stringify(stubConfig.network) -}) - -afterEach(() => { - delete process.env.ARK_PATH_CONFIG -}) - -describe('Config Loader', () => { - it('should fail without a config', async () => { - try { - await configLoader.setUp() - } catch (error) { - expect(error.message).toEqual('undefined (object) is required') - } - }) - - it('should succeed with a config from a string', async () => { - const result = await configLoader.setUp() - - expect(result.delegates).toEqual(stubConfig.delegates) - expect(result.genesisBlock).toEqual(stubConfig.genesisBlock) - expect(result.network).toEqual(stubConfig.network) - }) - - it('should succeed with a config from an object', async () => { - const result = await configLoader.setUp() - - expect(result.delegates).toEqual(stubConfig.delegates) - expect(result.genesisBlock).toEqual(stubConfig.genesisBlock) - expect(result.network).toEqual(stubConfig.network) - }) -}) diff --git a/packages/core-config/jest.config.js b/packages/core-config/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-config/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-config/lib/index.js b/packages/core-config/lib/index.js deleted file mode 100644 index 86e71cb89a..0000000000 --- a/packages/core-config/lib/index.js +++ /dev/null @@ -1,18 +0,0 @@ -const { client } = require('@arkecosystem/crypto') -const loader = require('./loader') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - alias: 'config', - async register(container, options) { - const config = await loader.setUp(options) - - client.setConfig(config.network) - - return config - }, -} diff --git a/packages/core-config/lib/loader.js b/packages/core-config/lib/loader.js deleted file mode 100644 index b98060672d..0000000000 --- a/packages/core-config/lib/loader.js +++ /dev/null @@ -1,167 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const axios = require('axios') -const dirTree = require('directory-tree') -const fs = require('fs-extra') -const ow = require('ow') -const path = require('path') -const { configManager } = require('@arkecosystem/crypto') - -class ConfigLoader { - /** - * Make the config instance. - * @param {Object} options - * @return {ConfigLoader} - */ - async setUp(options) { - this.options = options - this.network = JSON.parse(process.env.ARK_NETWORK) - - await this.__createFromDirectory() - - this._validateConfig() - - this.configureCrypto() - - return this - } - - /** - * Get constants for the specified height. - * @param {Number} height - * @return {void} - */ - getConstants(height) { - return configManager.getConstants(height) - } - - /** - * Configure the crypto package. - * @return {void} - */ - configureCrypto() { - configManager.setConfig(this.network) - } - - /** - * Copy the config files to the given destination. - * @param {String} dest - * @return {Promise} - */ - async copyFiles(dest) { - if (!dest) { - dest = `${process.env.ARK_PATH_DATA}/config` - } - - await fs.ensureDir(dest) - - return fs.copy(process.env.ARK_PATH_CONFIG, dest) - } - - /** - * Load and bind the config. - * @return {void} - */ - async __createFromDirectory() { - const files = this.__getFiles() - - this.__createBindings(files) - - await this.__buildPeers(files.peers) - } - - /** - * Bind the config values to the instance. - * @param {Array} files - * @return {void} - */ - __createBindings(files) { - for (const [key, value] of Object.entries(files)) { - this[key] = require(value) - } - } - - /** - * Get all config files. - * @return {Object} - */ - __getFiles() { - const basePath = path.resolve(process.env.ARK_PATH_CONFIG) - - if (!fs.existsSync(basePath)) { - throw new Error( - "An invalid configuration was provided or is inaccessible due to it's security settings.", - ) - process.exit(1) // eslint-disable-line no-unreachable - } - - const formatName = file => path.basename(file.name, path.extname(file.name)) - - const configTree = {} - - dirTree(basePath, { extensions: /\.(js|json)$/ }).children.forEach( - entry => { - if (entry.type === 'file') { - configTree[formatName(entry)] = entry.path - } - }, - ) - - return configTree - } - - /** - * Build the peer list either from a local file, remote file or object. - * @param {String} configFile - * @return {void} - */ - async __buildPeers(configFile) { - if (!this.peers.sources) { - return - } - - const output = require(configFile) - - for (const source of this.peers.sources) { - // Local File... - if (source.startsWith('/')) { - output.list = require(source) - - fs.writeFileSync(configFile, JSON.stringify(output, null, 2)) - - break - } - - // URL... - try { - const response = await axios.get(source) - - output.list = response.data - - fs.writeFileSync(configFile, JSON.stringify(output, null, 2)) - - break - } catch (error) { - console.error(error.message) - } - } - } - - /** - * Validate crucial parts of the configuration. - * @return {void} - */ - _validateConfig() { - try { - ow(this.network.pubKeyHash, ow.number) - ow(this.network.nethash, ow.string.length(64)) - ow(this.network.wif, ow.number) - } catch (error) { - console.error('Invalid configuration. Shutting down :rotating_light:') - throw Error(error.message) - process.exit(1) // eslint-disable-line no-unreachable - } - } -} - -module.exports = new ConfigLoader() diff --git a/packages/core-config/package.json b/packages/core-config/package.json deleted file mode 100644 index 1a62e558d9..0000000000 --- a/packages/core-config/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@arkecosystem/core-config", - "version": "0.2.0", - "description": "Configuration Loader for Ark Core", - "contributors": [ - "François-Xavier Thoorens ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/crypto": "~0.2", - "axios": "^0.18.0", - "directory-tree": "^2.1.1", - "fs-extra": "^7.0.1", - "ow": "^0.8.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } -} diff --git a/packages/core-container/.gitignore b/packages/core-container/.gitignore index 5d1942c190..1269488f7f 100644 --- a/packages/core-container/.gitignore +++ b/packages/core-container/.gitignore @@ -1,2 +1 @@ -config data diff --git a/packages/core-container/CHANGELOG.md b/packages/core-container/CHANGELOG.md deleted file mode 100644 index 5a2008de88..0000000000 --- a/packages/core-container/CHANGELOG.md +++ /dev/null @@ -1,33 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Support plugin extensions -- More graceful handling of shutdown -- Silent shutdown to hide output -- Configuration through a remote peer -- Expose the git commit hash on development networks - -### Fixed - -- Cast numerical strings to numbers - -### Changed - -- No longer load the `.env` file in test environments -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-container/LICENSE b/packages/core-container/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-container/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-container/README.md b/packages/core-container/README.md index dc6a8b990b..91d6c0666e 100644 --- a/packages/core-container/README.md +++ b/packages/core-container/README.md @@ -14,8 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-container/__tests__/__stubs__/config/delegates.json b/packages/core-container/__tests__/__stubs__/config/delegates.json new file mode 100644 index 0000000000..cb2b4899cd --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/delegates.json @@ -0,0 +1,3 @@ +{ + "secrets": ["this is a test"] +} diff --git a/packages/core-container/__tests__/__stubs__/config/exceptions.json b/packages/core-container/__tests__/__stubs__/config/exceptions.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/exceptions.json @@ -0,0 +1 @@ +{} diff --git a/packages/core-container/__tests__/__stubs__/config/genesisBlock.json b/packages/core-container/__tests__/__stubs__/config/genesisBlock.json new file mode 100644 index 0000000000..83801fa8e0 --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/genesisBlock.json @@ -0,0 +1,896 @@ +{ + "version": 0, + "totalAmount": 12500000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", + "timestamp": 0, + "numberOfTransactions": 52, + "payloadLength": 11401, + "previousBlock": null, + "generatorPublicKey": "024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231", + "transactions": [ + { + "type": 0, + "amount": 12500000000000000, + "fee": 0, + "recipientId": "DGihocTkwDygiFvmg6aG8jThYTic47GzU9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "03cb7bca143376721d0e9e3f3ccb0dc2e7e8470c06e630c3cef73f03e309b558ad", + "signature": "3044022016ecdf3039e69514c7d75861b22fc076496b61c07a1fcf793dc4f5c76fa0532b0220579c4c0c9d13720f9db5d9df29ed8ceab0adc266c6c160d612d4894dc5867eb1", + "id": "e40ce11cab82736da1cc91191716f3c1f446ca7b6a9f4f93b7120ef105ba06e8", + "senderId": "DUFeXjJmYt1mWY3auywA1EQSqfCv5kYYfP" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03e5b39a83e6c7c952c5908089d4524bb8dda93acc2b2b953247e43dc4fe9aa3d1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "03e5b39a83e6c7c952c5908089d4524bb8dda93acc2b2b953247e43dc4fe9aa3d1" + } + }, + "signature": "3045022100e3e38811778023e6f17fefd447f179d45ab92c398c7cfb1e34e2f6e1b167c95a022070c36439ecec0fc3c43850070f29515910435d389e059579878d61b5ff2ea337", + "id": "eb0146ac79afc228f0474a5ae1c4771970ae7880450b998c401029f522cd8a21", + "senderId": "DNL81CT6WNG1PHjobBmLvKwLV3UUscBymB" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "031137050d5fed0b5229b150257da2ac9c135efdf4bcb382b0ad0c197d7be458f4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "031137050d5fed0b5229b150257da2ac9c135efdf4bcb382b0ad0c197d7be458f4" + } + }, + "signature": "30440220124baaa04491287d0abbf5a167c9b0f5ac95c22b196f42ff3d275cc9a213c2fd02206e6ebada85f67063e642dbcde6b956f8c99c05f4b9c55f1551d3eebba6375043", + "id": "c9c554056b3428951633a7059dd64dfcbd776fef7f4a156ea362b37ee6ce74c7", + "senderId": "DG9LYv5rqX67wuGvGVa9is5k1r86LKCVTA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "037def83d085778d7767a182a179f345207953441089081f5bc13f86d3891308aa", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "037def83d085778d7767a182a179f345207953441089081f5bc13f86d3891308aa" + } + }, + "signature": "3045022100900cea3c2df393414899c9d74db57d89c9f311c70d08b974d0fd4a98bfae2fc902204a2aa51a1ec71da27c26afc033de6bd2d15978813c120c95e1a4dafca75ce876", + "id": "c82ccaa16be0e3c7ff4a53e2807968b71a0d88115223c3af2eb320f32449ac32", + "senderId": "DMSwarrHg5N9ZAZ6nsqPuUjyAU6gdRAM9d" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "033f28ad2e9b897d46f1e67c7c52070e9ca46b04c0679ebb21fb236719e38aade3", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "033f28ad2e9b897d46f1e67c7c52070e9ca46b04c0679ebb21fb236719e38aade3" + } + }, + "signature": "30440220285188d8900cd3cffccf5e1de305b18856451dd04d2ed21165dffe9a7ce4afc1022009457be6bfe536971697105d47ad1f829738a5cacdb27a23c5d1e8a8dddf3ebd", + "id": "ee6a19fff622ab4e6e96d159396de56d6034b4b18a9cf5c99efcf4e61b28e15a", + "senderId": "DFcYHfCwhGWcBNy6cp48wy5SfXbQmfBYgT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "023e577a7b3362e0aba70e6911d230e86d729b4cb640f0e0b25637b812a3e38b53", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "023e577a7b3362e0aba70e6911d230e86d729b4cb640f0e0b25637b812a3e38b53" + } + }, + "signature": "3045022100afa56542dd473c424b36d4d9f24da68180cfd90527681ab84098f415b2544a8702201e8ebdd619a2dd200e37a57c39a4529afe76d35f6089c00f6dffba6bf7b8a836", + "id": "0dcd6e380bd7eaef8724f64f4b86104ce7497308dacf775afbe6ec0d401007fe", + "senderId": "D5e2FzTPqdEHridjzpFZCCVyepAu6Vpmk4" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02af5e6341efc14f4ba39a9ff65e151cc7304fc742ce7b2678d9aa446c555ee9c1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "02af5e6341efc14f4ba39a9ff65e151cc7304fc742ce7b2678d9aa446c555ee9c1" + } + }, + "signature": "3045022100c8980155c8f8964d76baf3e8d690075708f1a84757c1de52e311772466382da2022012599acfc7839fa1ef6bbd445ab34555fb718491db3089f40d4842b1bc2d3178", + "id": "8af6abb117c69c130e388970d595b741374b1bbca709d9e91459e9e3c721397b", + "senderId": "DDLbnve6XK48cGsQiFhesUJQRQdKkZTfPh" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02845161cfca4d6ddde8e0d53538b6f881fb3ad9383cd77cebc55375dd6fd17663", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "02845161cfca4d6ddde8e0d53538b6f881fb3ad9383cd77cebc55375dd6fd17663" + } + }, + "signature": "30450221009bce7c5c10a4b6306cebe5724adfd3de049a425c44dd314a10154774764c11090220070fb775e71dda6a68f7fc9e0c762fbf96021908911f0de0ca8e9b0c613cb896", + "id": "bd346035d4516b85fb3a2cce6260fdcc6f1c434999e586978e065de3bf98e02a", + "senderId": "DDAHPjVTTV3uur653TB27fcLGh7XXWnvxW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03f264a6d2ebb62279313a6fd7fec4e2244785839b625a0b0c261e689ce5401d87", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "03f264a6d2ebb62279313a6fd7fec4e2244785839b625a0b0c261e689ce5401d87" + } + }, + "signature": "30450221009f74425c2ec50dbee462e735dee3e7917c8433fd5250ff09af4506c38d2df05902206a14a19b9a5defe3c8c59c77d52c182ea34d81d2e0b05dc5925133f2829a1960", + "id": "b48068fb7c848ffd57e82a4d381f53bb69916f3943e0e8935971a028ba245564", + "senderId": "DFHdEBuVCz5zfj8yeo3BmKEdsEKpMaYRRw" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03efd265a086c2a099cda4f4fd202adbac07567e1229ce5e6fe39963b714c1e2d5", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "03efd265a086c2a099cda4f4fd202adbac07567e1229ce5e6fe39963b714c1e2d5" + } + }, + "signature": "3044022004df492965ed328134aa6443d38ac4dd951a640e00330da9aa4e80c1577af41a0220588f030f5f9584959647898bb977a1ffe6bba639b1c64a728880f2cd3fd7aa3c", + "id": "73b3b4375e39aabe51ec205559cd728a18c987dabaa0599c611b3076c38c7a49", + "senderId": "DL7Y6smfHHs3Ms3hAYmSYYd5PZukmtDY1i" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "027d616d20f03c375067676c79ff9787e8e42991fbd9e878501d704d23d246d9b0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "027d616d20f03c375067676c79ff9787e8e42991fbd9e878501d704d23d246d9b0" + } + }, + "signature": "3044022051c2f8af62163ca621eeb3087a35bfaca0d679f7be8b19a25972f5a4b24ad8c90220422f3e0e480bf1bf2211e871a102edc15a957c0f97a553d9d707418e6538df26", + "id": "80f1d01158452da31d44f0c24f464a0ade37da51d2f61356ad75a019a91a1ff5", + "senderId": "DBVoRSXBHBPPvssBXrswv22r4dUSpN1fbA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "038918951152a37b74dfe61115f83e4b5e3521145065650c4a6d3e94add57d9a9b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "038918951152a37b74dfe61115f83e4b5e3521145065650c4a6d3e94add57d9a9b" + } + }, + "signature": "3045022100facf6ed992c28d41595419666b006800fcb33c6bad4b522e013b4d688e51dc8502207695e968059f7a35486389c430d6a3037e69d3e5f1d4f0a294d8818e4750cf0d", + "id": "86d76b0aad8f496d8c20926bfdeb50ad10db242ea6152b68266680c48e1e1aca", + "senderId": "DHsSK81gRWjgNx1A9gtHgkRsEwshsog7AM" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03231d8f2f39925fa79efc8f8561e6a8d29b95164a753cbb604a46e8a2e96606fc", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "03231d8f2f39925fa79efc8f8561e6a8d29b95164a753cbb604a46e8a2e96606fc" + } + }, + "signature": "304402204c627ec3d24fb7b4f86709c0566cee9909ebddb26039e87a2fa673f1f7227362022003be5aa3303b8f4cdab768f80b4699440a61814950cab0fd983526771c4c52ec", + "id": "464614909ac7531a016a0489d78defe262dc0934324f41199975ad42a86f37ac", + "senderId": "DDr7UTGQuPTjxLDWZ8RMjWJMKNXAMj3Bor" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021e6d971e5885a3147ddf1e45bf5c8d0887ad9fc659e24bdf95c2c9607e7e3fe8", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "021e6d971e5885a3147ddf1e45bf5c8d0887ad9fc659e24bdf95c2c9607e7e3fe8" + } + }, + "signature": "3045022100898e59efe518745d3eb3f2b16f7b6192e3289bb4289d43013224549f2015aa4902204e7be92cbba37a05551151e46224da4e5d0ad86ee2106d3a9c0b9afee5f1c4cf", + "id": "9559866ff439959529f69b0947ad2e72d739511ee1f6533c0bca2ebd6dd4ae4a", + "senderId": "DRXNNQ9gQXh6VNUVKaAn9xHAViyiHKtBHZ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d5b3efbe98631443c5cdf4de8a610dd2655b86427bf70aa209451b54256f6758", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "03d5b3efbe98631443c5cdf4de8a610dd2655b86427bf70aa209451b54256f6758" + } + }, + "signature": "3044022037fa085e37a582b2e0b3734d44b813bb18be939f73100c5b6f977d4f53ae708f022064ae54f6a1b17b193ab6b6d633f7b7a7b8171a158cdba7480afe380f383930dc", + "id": "7bab92d5397a4ad291c5d01b8d681e480d19b437a7ab5cbd4c6807c96ef2716f", + "senderId": "DT12wf9erZyNJbBQrpbPDmfH3J8txiDgTE" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0297f2e8e609b2a6799214481e7573a043a197f8adf7b8bb306576fc3da83d2aaa", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "0297f2e8e609b2a6799214481e7573a043a197f8adf7b8bb306576fc3da83d2aaa" + } + }, + "signature": "304402202eee94bc3b53c64f8dee7790fe3eed8639da8faf0aa1f785e921cf139df0fb7e02200224efb0c07ae3972287c12a32143c1356adb93e00ac9e04a1358c8245a24cab", + "id": "1e59740fa596b615231660974d0b656122b799a8b13102ade8c1b779aa5de7b5", + "senderId": "DKGYWPSqa4m4z6h3433rNFbWPDdvHj5wwd" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0361b914fd5823bf39ae467e95d99e9f6ddb7d85cc6df3055ce00274b8e4a976cc", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "0361b914fd5823bf39ae467e95d99e9f6ddb7d85cc6df3055ce00274b8e4a976cc" + } + }, + "signature": "3044022002ad92b9b9d81dabf96ac7d90034debc55eeeae879b3fe6ffc026bde86bb7ad902205c57d31c5e5e0099b504ba4c49e220a00ff325dceb64c46aefbb7a0ad8570099", + "id": "bf305776da902802923c19b9d2c7f1a809b0847992131cfa578d5e5518c924bf", + "senderId": "DJshaeFyHcFTjiGJnVPaDmFXhnJ9bp96i5" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03fa6bc09bd2ff348b304e0cfbc2d2ec50aa3b9aee0de6a66c13fcd8ee5ac891cd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "03fa6bc09bd2ff348b304e0cfbc2d2ec50aa3b9aee0de6a66c13fcd8ee5ac891cd" + } + }, + "signature": "3045022100be50b19c17a9ff221aae20394a45d92ea47e8c1072b6d5a302937d2fc48cba8002205e9bcb3471a734c07ceff0083ad9ba1570507a29e5014e889ba42a85e797cb5e", + "id": "44e48364b5b8cff3c68ae03de7dfde8d7ba6bcb99bf82b32fdc8bc3d0d9adeca", + "senderId": "DSuNttSb1UvCWg8iormfwPwi67EA84P5Mu" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03241957edca9ed28308e35cbf36762d22de706ebbd7c6a3a2d235d905d660c5c7", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "03241957edca9ed28308e35cbf36762d22de706ebbd7c6a3a2d235d905d660c5c7" + } + }, + "signature": "3045022100c11f8b863133535192e6c3fff20253a2695a2df74cdf1445d4ca0966803f708c0220200d4c2723d84f6334ba5d1cc1a0d45854867f4523fbcc9d09b3d53dd1972950", + "id": "5cba288f9ffc1361ba8f7f19f28347ffd917f37df8cf46ba1e0816725f288528", + "senderId": "DCZt1ozEVvPdYVvkHmUKK6k7gnyNNQDpMq" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "035ae2364c838bc21edf4c04a99c85799f26fb02cc0740c5a1c67d4dc1748ff913", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "035ae2364c838bc21edf4c04a99c85799f26fb02cc0740c5a1c67d4dc1748ff913" + } + }, + "signature": "304402203066f06a1c165795d8a069499a8c0998913ec93e689219f14145754aa3e26e4e02206e9f88da16f1f8a8ebaf481eff798452487738714fe9b5694fec6a5ef8c152a5", + "id": "ada1696532f7faad1dda594bc6db7bfc029a1759402c924348b74222873a3a27", + "senderId": "D7JyqWMPKhhRNQcKTAvrPGBjEjjBcGgPca" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "023aff4a16c3876e885aea70e5bce9734ce5acc95a2c41c9783f5acd617f7c7533", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "023aff4a16c3876e885aea70e5bce9734ce5acc95a2c41c9783f5acd617f7c7533" + } + }, + "signature": "3045022100f5150c23596b9479c8b277401ab9e7da9b2275436f3927dabd70395e52c3ea7c02204e318d498b0176b5f05bb96418c49da3375a8d9b47b3b1e72a6f4db30b3f8c34", + "id": "e186a679f2e47300ec2f24c670192bcede1cb12f359cb8e827374b22f41fbe12", + "senderId": "D6itxYJr4n7ZZk2bd9cZbJE1xaDmpfkNFL" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0217d7ce9c3754f7fc7e5b4c64a1ff397dc75931cd6c92e32d8b42068ad50fe4eb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "0217d7ce9c3754f7fc7e5b4c64a1ff397dc75931cd6c92e32d8b42068ad50fe4eb" + } + }, + "signature": "3045022100b84f69a7ff67ed147fc0a750c3b7b2ecabd582b6d0cb698c0bb4a531daa6ca46022039d2722e486e1674d0db422078d63fcdb90b21bed0dcc1265adff72d0c2bf8b9", + "id": "86d9d146b62dbafe212aba5ec9764223b67f72c3c1aa93e54a270e3a528a8b20", + "senderId": "DDy4aKhF3cMadGhjFZnjaA1tx2rwnSEWcc" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "024019207f50dcb3e8aeb9ac1b00993d2bf131346e7e6d296429ea813a8373818e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "024019207f50dcb3e8aeb9ac1b00993d2bf131346e7e6d296429ea813a8373818e" + } + }, + "signature": "3045022100aa83596b740639ee8947aa6d0f0ee123e4a5b87c39a4c6dd8a50304d4a7c97d102205fd45f85f5bdb076585a77888ef880bea52ade689731dff694d777de34913efc", + "id": "6301b791844e02116df528b1ea46d788e91521189c3828ce224e45a1b72cda59", + "senderId": "D6BwyDJkNFkaDLedcJTE4rPUw5bRtb4K8f" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0275db912c21dca0f0213a76f4544137d7c741b47f281cfd4f8b7cb8187e7ce3cc", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "0275db912c21dca0f0213a76f4544137d7c741b47f281cfd4f8b7cb8187e7ce3cc" + } + }, + "signature": "3045022100c7eda0d9cd7ef522615643d1b985c73add2d3612344bdcc0117779fa4f4f54d302203e33fb5d185f5174e9cb7634a3d307b74d3bb56cc2354024ce69c74905a85203", + "id": "eee776fcb8024469eacab3e4b23c3d14185326431369aa84f17921abab8ad0ad", + "senderId": "DHQSmrRdfYAp9Y6CuebKnkoQNzuN7Pk2oQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0331c615ca4bc89d4eeb8d7a9cfbb5c0d4ce49d2f480afbce499b0c7f8c6a24f2f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "0331c615ca4bc89d4eeb8d7a9cfbb5c0d4ce49d2f480afbce499b0c7f8c6a24f2f" + } + }, + "signature": "304402203e69be3a73c5917d89d58f3c0ae18febbbf364d3f9dfbec6b526a5294f9c435902201750bcf6368c181aabc53c73fd271a2967a6f215e1d0506eded5dd1800fea1c8", + "id": "ec3d17c6d38c0b9848c7cb57b968efd1f3872b1d1b8bcfb74bae2b0aaa15877c", + "senderId": "D6EVFQx5Z7M2X9DWXHtfX51CtVekuKPMQF" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0338ca9b719f8047580eed23b64a40aecad3803a12c0dde83e3ec2c2a9bfaa8147", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "0338ca9b719f8047580eed23b64a40aecad3803a12c0dde83e3ec2c2a9bfaa8147" + } + }, + "signature": "3045022100e0bf90949739012b641793da162b3daa88b34c8753ee31b26850729e9df579810220439a3f2f1b8e719767ee68df46f4bc1f18c8c3b2da4118edff22396616d319fb", + "id": "14cd65c5f28f4cefc7c0157518a24f90c2260eb7166105b6b3358d91164ddf39", + "senderId": "DLCQ1jPsYbBCV7JfUJTasKbKoyGbK4a4HG" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03127001718bee76f14133272f0f4a928ffa8c2b38cafd94d7100253dac732c644", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "03127001718bee76f14133272f0f4a928ffa8c2b38cafd94d7100253dac732c644" + } + }, + "signature": "3044022003d2e76aca2848aedfe25415c11b9368dc72f687b66bef4527b40e2997b86b8c022076f7f82cbeb282d26535a2c1f0af0f02b48025d42c1bd56ac687fba1a3adb706", + "id": "0daff3992b54b1384f52f751c933c727cbaaf4fac435eba88a1817a425753614", + "senderId": "D9rv3h61heDYHQ3b3Xk3V5epHSTTC6Vn1d" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0241734825ba45b6de29d6f26242c25ae1ef125b82615ee89a9fdd5b0f3c6b5132", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "0241734825ba45b6de29d6f26242c25ae1ef125b82615ee89a9fdd5b0f3c6b5132" + } + }, + "signature": "3045022100bb2903424bcd0a72da531470779144d60286191bea1b200c5617ae4f92229ba6022046a876e3e6cb85469a16f34d2f937e2eef787011c6a313ee50258f15116148ac", + "id": "bd17dbd23f8dbba2736688702ac185a87c88c43b24ee6d7764a5b4138b2f38b7", + "senderId": "DAcQPbKa8zBWwDHbxj37N13C61iseMDWM9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03b9409203d7091e3f4d49168529b749e942ed18f21beddd236d57d692f09a8f86", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "03b9409203d7091e3f4d49168529b749e942ed18f21beddd236d57d692f09a8f86" + } + }, + "signature": "3044022016d7ecfa776930a6f83464548e7a686735fde752903539a38eb9da0ce2488bbd02203c5e23a4072c8de35a90b296145cce3156a31cc0d754b8a37d363fb088bc7387", + "id": "16e02d3ef24dca4b03a1e489e20335224f18d888ed04f7e3512572f8e0cf92ae", + "senderId": "D5mmTaDAMSyPNKiDKrqwTFGWzWrZA3xaF8" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c7b92a2d0027309e21855cf9c42a432b21ad13925e9dfc206f9c01e18fefa08a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "02c7b92a2d0027309e21855cf9c42a432b21ad13925e9dfc206f9c01e18fefa08a" + } + }, + "signature": "30450221009de8828a7ad87cb5d52900e09d5beb680f9edc7640a3707d08a379511a7ba0f102202aa1d9294f9631f1325f252adb87c0d866e7398ce410037a42dc861d94308e15", + "id": "fece556bee4de2c7f1bb3099a05a84a33d0c963979fe1a222a899c13b7abb1fc", + "senderId": "DJ3NywAwQh4srbooLH1jTs9ma1hJE79v3z" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0221297804a26a93bb441a9d20a2916abf27fa7b29967678ef1a7a58062f73f40d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "0221297804a26a93bb441a9d20a2916abf27fa7b29967678ef1a7a58062f73f40d" + } + }, + "signature": "3045022100b969611ef532557fa3da8a0325b2c88f3ebec954d64f158431d86b8e07929ea50220520affdcd0728cb7c5f63a58a1200d44133e90b1f7a6a9e28744ad6b0dcc2a75", + "id": "ee086317ea2fdc522f5eb502a0db9f3d4955b2318559e40a1f22a3f5f8d6344b", + "senderId": "D5P7eti7FUY4Tk5KXoxdf2tDAVQrRVCESA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "027f504f6f20648e3bf171952629c7b868a2f799aa4b60f8eb3fe96afff16bbef0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "027f504f6f20648e3bf171952629c7b868a2f799aa4b60f8eb3fe96afff16bbef0" + } + }, + "signature": "3044022006be7cbaa74089cabe47d02621f756762587d210a3f211ee941b5fcd0650908f02207d4040408bd25a2de03e5724362735ee8ad36c099b0c16efd4716e1dd7ec62ae", + "id": "764dd21aa4d0e2e0fa17bb2ff5e7ca304995d9e3593542badecc8ed24d5ea3ea", + "senderId": "D9q26yBTrEYuxHg7bbfZphv6129KvLu4v2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "031954315b84db8f49ab7ee21357270450bb68d06b34472e5e93ddfa5710edc0c9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "031954315b84db8f49ab7ee21357270450bb68d06b34472e5e93ddfa5710edc0c9" + } + }, + "signature": "3045022100859f93df994d86995fdf834bfe86b41eebaa04e5ab7d09f0b37acb50d313cd9802203c8993b793602c96d305fa795a9f2459f4706b340993584f3c56579392c0995c", + "id": "efd9e7c638afe62bec9be61783193ea52eea7b335053bd5af6c758d5b0e5847c", + "senderId": "D9iPFb5kAVnuDdomehRP9LncJj5ng2vrsr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0267b310eac2bb0d6594de382a1ab74ac75b91e9d64a590b6249247b10fd9be829", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "0267b310eac2bb0d6594de382a1ab74ac75b91e9d64a590b6249247b10fd9be829" + } + }, + "signature": "3045022100a678978ab899e3903e760ee98640e3f658792a096a8d771c575944af6536cfdb0220428c312f1e0eb4be73ce4b256a754447570176200cfb6c09b3eb55f66526dd80", + "id": "70edcce5df67a250b6ba3567879bae6379ce4c688597fcedfbfd0313da6998e8", + "senderId": "D6xZmtyBzZKCEkK29JNPAD581TJ8XXrXYn" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "028f32320c66a89779756b04946d2aa256dff6cd547349d46e1938710063e387c4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "028f32320c66a89779756b04946d2aa256dff6cd547349d46e1938710063e387c4" + } + }, + "signature": "304402206bc95876897527b39eacf4c961f9c036a9c8a0e53a17ce925c592d079fa643030220096e115d7fbd54aca4af7f621d64178dfcf2c13361106a3e3b5025dca97b44ee", + "id": "7f23f44157f3a677e81514fa431227410a27442e5fd1f2491b177c0f580f296d", + "senderId": "D9dW4eXJjABDQXSQB9GtvY5UBuRWWWejWb" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0218b889a24988527ab3948d80f97cfc37b923082e1f0398bc162190fd66ec4dee", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "0218b889a24988527ab3948d80f97cfc37b923082e1f0398bc162190fd66ec4dee" + } + }, + "signature": "3045022100c40a3f4cf15f9274e2b25ca8608cb965316aa0f00fa77817b79620ad8ccbdd5902206203a1043b03ba58aa9b7399694f8215cf45d30eb0caa748cc06f1a85a8faea9", + "id": "a65244ed17a9280aa694abdf6804b1a0b78dfc052b4845abcd3c89380159b29e", + "senderId": "DFHK7SdmPdjxNZ9uweqLZAv6v5GQ1NnBNe" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "035392ee88c60617764b4fe89ae2cc96560dfa5f992b03be31ce5680db9b863f73", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "035392ee88c60617764b4fe89ae2cc96560dfa5f992b03be31ce5680db9b863f73" + } + }, + "signature": "3044022036200c3191f8f01b77676644b9b94728b5afb2ab2de8c5c7c5582e795465661c02207848f1f2f0ab378d8906fd45aa048f354d5dbac4cb87c15973ffa86fe84ff0cd", + "id": "219e0942afe5f65c548ec2118a1c49febb7ec03fca4334ac16649062db9d146b", + "senderId": "DSh7AAC9KahXU2JZ539HAqEa5sHafxsxDQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03e75127d1deccf65844a1761bd26611b6c65c5b51a52eba27e3ee20a539fd63f1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "03e75127d1deccf65844a1761bd26611b6c65c5b51a52eba27e3ee20a539fd63f1" + } + }, + "signature": "304402201a2990b2baae72f5cc8f2d1890f328e4082af0cf2a787d8f05208c3424ce089d0220790dbc7606dd6c03568fd0a771e9e8e89557257238ae90cfcb3bb8f3b475987b", + "id": "ee9ad2a66e9b2009a9fc671f80d0493803fc422161140169c7bc1fd401cd9ad6", + "senderId": "D85WuxGZrFs1QUYTvnRpmc6dd8rmBbpnaX" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0329fc1580906307ac9f2f55cec66e47983f8287d542408fb19f473a305d3638d8", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "0329fc1580906307ac9f2f55cec66e47983f8287d542408fb19f473a305d3638d8" + } + }, + "signature": "30450221008f66e89ec4c7af4b77e5b7ff36c542cc02672c8df70806b5a0fab7a7e8c7067502200d99ba19ceb1b471c39c4e95107ad6f8b978a623a790080b16f863347fe06b4f", + "id": "dd3077ed04a76343d340074270ce9826354802bd99e08cb864c1c5ad09f367df", + "senderId": "D85kwsBJKZ4pw5uQpc81eRj95f6a536AP6" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "035ec848e9388877dac88f121d19c8f5e870ac90d8ccb0116be9f734e4bd1a9405", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "035ec848e9388877dac88f121d19c8f5e870ac90d8ccb0116be9f734e4bd1a9405" + } + }, + "signature": "304402202b220d6c028bc23213edddaf303f18eef059551891aadbf7a4b4d7d3287457bb0220245678354bb8960b42ba2f2ceb12f926e82ff0d027b44988d799c8c0d8d7d9f2", + "id": "3afc6ea52b8edc7df0230ceac71baf45460f3bd761c5e75fe796bc7415063220", + "senderId": "DGBJdDadBwJD2xY8VsdAykdd6vPakMMUt6" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0356f5885306e45402aeb354a74d13c104699b3b53da46a5e922e4a6d6132a67e8", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "0356f5885306e45402aeb354a74d13c104699b3b53da46a5e922e4a6d6132a67e8" + } + }, + "signature": "3045022100f18bf2e013f2d9dcac013a76037d787f79baaa65f4f31ffe2b4ed8de249bdc8902202abcf77e809599d3e3a96225363c8e760ed4b4e20f97645547b381dba830c3da", + "id": "aea1fc173a2f4a9233b0fe59a5f6804167bee5658cb3e4e19dfe2be20f5772cd", + "senderId": "DG4VbapL3H39NJLB3DqQEefU47EMVqtxVw" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03ff8ab980434516ca28c982d0ecc8fc3107116d6c8b3e09c7ee5033f32adbd2ff", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "03ff8ab980434516ca28c982d0ecc8fc3107116d6c8b3e09c7ee5033f32adbd2ff" + } + }, + "signature": "3045022100e938d9901afeaa5a56d18abd9292ace93be03c84c09a6c4cb58fca96dfb54bc502201e921d27f9886d189f803b14d93655a42c4e095d49ee61051a4e70c7a173f3f1", + "id": "f18426d3ef81d4b7bf0337d70afcecddbd6db2206a2f139f1ca5823c381c7817", + "senderId": "DC3oNWedP48ypGxAeKbFC7gMjWxcNc2JhL" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0393f1590771a8ad1cf2baa086858f3029c4444cb82243917a7011f1f66cf8fd05", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "0393f1590771a8ad1cf2baa086858f3029c4444cb82243917a7011f1f66cf8fd05" + } + }, + "signature": "304502210095745c36a8af07e21546bd064f1ed1bd90e6c2a8db9c0c8e4853d0a8255443db0220259d2ce3677abb42f08b9d22aa13bbe383fd882ed38911b738ebaefc04589694", + "id": "6c51bea35b5e3270dcf7b7dfae8d984e19f476ea7e0435f157c4e0d22b7e7ea1", + "senderId": "DJm2sfcUKhyxakowY9TjyAytkdq7JrFgVj" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0234e24ff1dbc447c804eb385cd05bbd1dc59ef03b44a3346b13e7cccf00b61075", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "0234e24ff1dbc447c804eb385cd05bbd1dc59ef03b44a3346b13e7cccf00b61075" + } + }, + "signature": "3045022100ee1e8df480f2be042386d383d776b3fd6bd2d3f5a9035071153f23dbfdceeaae02203a0834aae4834da3ca7858779d474b9255ead754867d5b4a18873e9ecaa5045e", + "id": "66fb3e36233f4577ba585ccd7daf83e62d8df262d3d832b806479ac67c1ef35d", + "senderId": "D5oS8xfNebiPsjpwPWoZS6sA9qcYjTGT5h" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03a5789a4486f20f1fdca78a52b528b3bf9952e7c057de71a22adcfb444ba4c5d3", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "03a5789a4486f20f1fdca78a52b528b3bf9952e7c057de71a22adcfb444ba4c5d3" + } + }, + "signature": "3045022100cb037530bcff9a4d19899431648747022c28aa3239563379d96692bd525eb38902205f3cabb8dd470d9eb3d425e333ad1bc9f0643d489c600a811748fb5f4a203f7f", + "id": "5df9c5e350136571af4b86697bc9d4cfca3ff8b669e254b36f00be1dbde063f7", + "senderId": "D5SzHHdPdGqYUkH7BGNkmGHEUqfZrWb17r" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0347f692345fa7bf90e944eb55246da5f9f595d3f5a20ad50aeb6f9b973aaae17e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "0347f692345fa7bf90e944eb55246da5f9f595d3f5a20ad50aeb6f9b973aaae17e" + } + }, + "signature": "3045022100c377efe5ffab58017473699cd7c839dcf48fa5b20b5ddf9bdc4801e22a579b2b02204d35c1a1416069544e3ec01d2ce21bb409f9f2fa4adedc8c03d6417c034a3fec", + "id": "da4cfad78e37d56421dd6676e5618a507340ef1e496831d1968c509e35ef9202", + "senderId": "DCLdibuZB6UsJP8KmdzcDLWzizrDtJQuxt" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "028a32b441377a69aa76e867026f3109b2f0aef8651fe91e2a4ab01eff102a6b98", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "028a32b441377a69aa76e867026f3109b2f0aef8651fe91e2a4ab01eff102a6b98" + } + }, + "signature": "3045022100e098672958be15989bb125d9018adb4a54e95ab664e64a673997e617e28b39df02206e8459997074d5976b77f90eb9d7180e9d4a0e0efdf433958ffeb2f04d9de382", + "id": "85bafcd07e7ba47ec95cb5b5a6759d4f9f87e036bb7660c7717504e845ef975e", + "senderId": "DSkivgRyimdAVqmm2ZAKwKmKN39WEbbPnL" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0223ac52179903e79865b9a98cf0b52ddc1ab46180c157e8f6bd1e63e7f14fcf31", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "0223ac52179903e79865b9a98cf0b52ddc1ab46180c157e8f6bd1e63e7f14fcf31" + } + }, + "signature": "304402200a005716f67d6cd3963a3c752c95f1bca01aa127c91ab1a632eb3022d11e3e67022024c4746078e440da441bcb366ee8999ffd2419e9a6f9cbf971d696d5b7f8733b", + "id": "0df1ed07d3f95ddf0385bad83a17b3a8fde6bd6532cd3479e48668064672b34f", + "senderId": "DDgKyKqdA6SuamB1eW77WvFu6RQFMZoU36" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "039de0390f28c7731d86ae7006a31888f12856cde3cc3c2619d4d4a42b6dfd6c51", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "039de0390f28c7731d86ae7006a31888f12856cde3cc3c2619d4d4a42b6dfd6c51" + } + }, + "signature": "3045022100efa5d51ca79992be4a87af049b3e9ec1b796576e4d937ea9e3760ab0bdcd301e022027e22a6c3395df155bd399643c241e4cc317eaead1f273fd7a709339dfa9dc99", + "id": "436bebc107fad38e944fd14785e09f0600df4d75d31cf3eac53f850462d0be74", + "senderId": "DKCaoaXApw1xE7K1BJcVkr1KGzjKmFWyTk" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02b87b0e70a7ae10613390f405620e24c495ba2b0cfcdbc67688e9b483dea564ee", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "02b87b0e70a7ae10613390f405620e24c495ba2b0cfcdbc67688e9b483dea564ee" + } + }, + "signature": "304502210096cdd35f803a37730ac73a97a23061dceac96319c67bfb1ddcfbac737febe96102202fa0b279f697da3afc043ffd3ecc838789be07ff119b5527a5c13468cecf66e9", + "id": "bb65f9dbe6272fd07a555fc86762d6a487f538b972f2926ff7698cdc906a32df", + "senderId": "DJQXFKEguZVabsAs46JbXXnQJ5jFhUtN9m" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c9f8f4001216603c152b4b4429c2ead322ac34672999e808d567a7d1140e46be", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "03c9f8f4001216603c152b4b4429c2ead322ac34672999e808d567a7d1140e46be" + } + }, + "signature": "3045022100ee961089d02d7bb68fe2257f6a972eeaf6e2c1a1ad2f491c417e161fedbb556b02204c834644e5b5cde9a0b3f92fa23bade7670efab0a067597f6c151ee633932706", + "id": "cef44df9684f05dab67c0568a2c5295bb50cbb3c88f5cfbe672365bda274620f", + "senderId": "DKpt7cm2tZk4RPLyQ5ugwEH7gkriRaA7ov" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02cf70f73328d490cfb03ee822d3fc0cf9259d67c0564e843491e739501809d657", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "02cf70f73328d490cfb03ee822d3fc0cf9259d67c0564e843491e739501809d657" + } + }, + "signature": "30440220645b912b60f829c0bce58bfe9890ef9253418b6898416aaead663bdf158a99f2022061abbbabd454ec7f7e3f4b502216eec28110e945a4b9b913b1fc0b9758e7e6e4", + "id": "09408dbcf3e3e0835bf92a05330c023a7d6471f3825301a34efa094e0fd4fc30", + "senderId": "DQfjSqDuKr5YZaLAF8rWpFMqMYwEbPtGKg" + } + ], + "height": 1, + "id": "13149578060728881902", + "blockSignature": "3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8" +} diff --git a/packages/core-container/__tests__/__stubs__/config/milestones.json b/packages/core-container/__tests__/__stubs__/config/milestones.json new file mode 100644 index 0000000000..51c32313cf --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/milestones.json @@ -0,0 +1,31 @@ +[ + { + "height": 1, + "reward": 0, + "activeDelegates": 51, + "blocktime": 8, + "block": { + "version": 0, + "maxTransactions": 150, + "maxPayload": 2097152 + }, + "epoch": "2017-03-21T13:00:00.000Z", + "fees": { + "staticFees": { + "transfer": 10000000, + "secondSignature": 500000000, + "delegateRegistration": 2500000000, + "vote": 100000000, + "multiSignature": 500000000, + "ipfs": 0, + "timelockTransfer": 0, + "multiPayment": 0, + "delegateResignation": 0 + } + } + }, + { + "height": 75600, + "reward": 200000000 + } +] diff --git a/packages/core-container/__tests__/__stubs__/config/network.json b/packages/core-container/__tests__/__stubs__/config/network.json new file mode 100644 index 0000000000..0f373e462e --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/network.json @@ -0,0 +1,17 @@ +{ + "name": "testnet", + "messagePrefix": "TEST message:\n", + "bip32": { + "public": 70617039, + "private": 70615956 + }, + "pubKeyHash": 23, + "nethash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + "wif": 186, + "aip20": 0, + "client": { + "token": "TARK", + "symbol": "TѦ", + "explorer": "http://texplorer.ark.io" + } +} diff --git a/packages/core-container/__tests__/__stubs__/config/peers.json b/packages/core-container/__tests__/__stubs__/config/peers.json new file mode 100644 index 0000000000..e212ecdd33 --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/peers.json @@ -0,0 +1,12 @@ +{ + "list": [ + { + "ip": "127.0.0.1", + "port": 4102 + }, + { + "ip": "127.0.0.1", + "port": 4202 + } + ] +} diff --git a/packages/core-container/__tests__/__stubs__/config/plugins.js b/packages/core-container/__tests__/__stubs__/config/plugins.js new file mode 100644 index 0000000000..2647260229 --- /dev/null +++ b/packages/core-container/__tests__/__stubs__/config/plugins.js @@ -0,0 +1,12 @@ +module.exports = { + "./plugin-a": { + enabled: true, + }, + "./plugin-b": { + enabled: true, + property: "value", + }, + "./plugin-c": { + enabled: true, + }, +}; diff --git a/packages/core-container/__tests__/__stubs__/plugin-a.js b/packages/core-container/__tests__/__stubs__/plugin-a.js index c7595c7c40..cd92b85ba0 100644 --- a/packages/core-container/__tests__/__stubs__/plugin-a.js +++ b/packages/core-container/__tests__/__stubs__/plugin-a.js @@ -1,14 +1,14 @@ exports.plugin = { - pkg: { - name: 'stub/plugin-a', - version: '1.0.0', - }, - alias: 'stub-plugin-a', - register(container, options) { - return { - container, - options, - } - }, - deregister() {}, -} + pkg: { + name: "stub/plugin-a", + version: "1.0.0", + }, + alias: "stub-plugin-a", + register(container, options) { + return { + container, + options, + }; + }, + deregister() {}, +}; diff --git a/packages/core-container/__tests__/__stubs__/plugin-b.js b/packages/core-container/__tests__/__stubs__/plugin-b.js index 6023222e04..dc1dedccd5 100644 --- a/packages/core-container/__tests__/__stubs__/plugin-b.js +++ b/packages/core-container/__tests__/__stubs__/plugin-b.js @@ -1,14 +1,14 @@ exports.plugin = { - pkg: { - name: 'stub/plugin-b', - version: '1.0.0', - }, - alias: 'stub-plugin-b', - register(container, options) { - return { - container, - options, - } - }, - deregister() {}, -} + pkg: { + name: "stub/plugin-b", + version: "1.0.0", + }, + alias: "stub-plugin-b", + register(container, options) { + return { + container, + options, + }; + }, + deregister() {}, +}; diff --git a/packages/core-container/__tests__/__stubs__/plugin-c.js b/packages/core-container/__tests__/__stubs__/plugin-c.js index 7d262d985b..b303b97ce3 100644 --- a/packages/core-container/__tests__/__stubs__/plugin-c.js +++ b/packages/core-container/__tests__/__stubs__/plugin-c.js @@ -1,13 +1,13 @@ exports.plugin = { - pkg: { - name: 'stub/plugin-c', - version: '1.0.0', - }, - alias: 'stub-plugin-c', - register(container, options) { - return { - container, - options, - } - }, -} + pkg: { + name: "stub/plugin-c", + version: "1.0.0", + }, + alias: "stub-plugin-c", + register(container, options) { + return { + container, + options, + }; + }, +}; diff --git a/packages/core-container/__tests__/__stubs__/plugins.js b/packages/core-container/__tests__/__stubs__/plugins.js index ad2a319c0c..2647260229 100644 --- a/packages/core-container/__tests__/__stubs__/plugins.js +++ b/packages/core-container/__tests__/__stubs__/plugins.js @@ -1,12 +1,12 @@ module.exports = { - './plugin-a': { - enabled: true, - }, - './plugin-b': { - enabled: true, - property: 'value', - }, - './plugin-c': { - enabled: true, - }, -} + "./plugin-a": { + enabled: true, + }, + "./plugin-b": { + enabled: true, + property: "value", + }, + "./plugin-c": { + enabled: true, + }, +}; diff --git a/packages/core-container/__tests__/config/loaders/file-loader.test.ts b/packages/core-container/__tests__/config/loaders/file-loader.test.ts new file mode 100644 index 0000000000..50c41588de --- /dev/null +++ b/packages/core-container/__tests__/config/loaders/file-loader.test.ts @@ -0,0 +1,34 @@ +import "jest-extended"; + +import { resolve } from "path"; +import { fileLoader } from "../../../src/config/loaders"; +import { Network } from "../../../src/config/network"; + +const stubConfigPath = resolve(__dirname, "../../__stubs__/config"); + +const stubConfig = { + delegates: require(resolve(__dirname, "../../__stubs__/config/delegates")), + peers: require(resolve(__dirname, "../../__stubs__/config/peers")), + plugins: require(resolve(__dirname, "../../__stubs__/config/plugins")), +}; + +beforeEach(() => { + process.env.CORE_PATH_CONFIG = stubConfigPath; +}); + +afterEach(() => { + delete process.env.CORE_PATH_CONFIG; +}); + +describe("File Loader", () => { + it("should fail without a config", async () => { + await expect(fileLoader.setUp(null)).rejects.toThrowError("Invalid network configuration provided."); + }); + + it("should succeed with a config", async () => { + const { config } = await fileLoader.setUp(Network.setUp({})); + + expect(config.delegates).toEqual(stubConfig.delegates); + expect(config.peers).toEqual(stubConfig.peers); + }); +}); diff --git a/packages/core-container/__tests__/config/loaders/remote-loader.test.ts b/packages/core-container/__tests__/config/loaders/remote-loader.test.ts new file mode 100644 index 0000000000..7033b2eaf0 --- /dev/null +++ b/packages/core-container/__tests__/config/loaders/remote-loader.test.ts @@ -0,0 +1,139 @@ +import "jest-extended"; + +import { existsSync, pathExistsSync, removeSync } from "fs-extra"; +import * as mockProcess from "jest-mock-process"; + +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { RemoteLoader } from "../../../src/config/loaders"; + +const axiosMock = new MockAdapter(axios); +const configDir = "./__test-remote-config__"; + +let testSubject; + +afterAll(() => { + removeSync(configDir); +}); + +beforeEach(() => { + testSubject = new RemoteLoader({ + remote: "127.0.0.1:4002", + config: configDir, + data: "./data", + }); +}); + +afterEach(() => { + axiosMock.reset(); +}); + +describe.skip("Remote Loader", () => { + it("should ensure the config directory exists", () => { + expect(pathExistsSync(testSubject.config)).toBeTrue(); + }); + + describe("__configureNetwork", () => { + it("should not be OK", async () => { + const mockExit = mockProcess.mockProcessExit(); + + axiosMock.onGet("http://127.0.0.1:4002/config/network").reply(() => [404, {}]); + + await testSubject.__configureNetwork(); + + expect(mockExit).toHaveBeenCalledWith(1); + }); + + it("should be OK", async () => { + axiosMock.onGet("http://127.0.0.1:4002/config/network").reply(() => [ + 200, + { + data: require("../../crypto/src/networks/devnet.json"), + }, + ]); + + await testSubject.__configureNetwork(); + + expect(existsSync(`${configDir}/network.json`)).toBeTrue(); + }); + }); + + describe("__configureGenesisBlock", () => { + it("should not be OK", async () => { + axiosMock.onGet("http://127.0.0.1:4002/config/genesis-block").reply(() => [404, {}]); + + await expect(testSubject.__configureGenesisBlock()).rejects.toThrowError(); + }); + + it("should be OK", async () => { + axiosMock.onGet("http://127.0.0.1:4002/config/genesis-block").reply(() => [ + 200, + { + data: require("../../core/src/config/devnet/genesisBlock.json"), + }, + ]); + + await testSubject.__configureGenesisBlock(); + + expect(existsSync(`${configDir}/genesisBlock.json`)).toBeTrue(); + }); + }); + + describe("__configurePeers", () => { + it("should not be OK", async () => { + const mockExit = mockProcess.mockProcessExit(); + + axiosMock.onGet("http://127.0.0.1:4002/config/peers").reply(() => [404, {}]); + + await testSubject.__configurePeers(); + + expect(mockExit).toHaveBeenCalledWith(1); + }); + + it("should be OK", async () => { + axiosMock.onGet("http://127.0.0.1:4002/config/peers").reply(() => [ + 200, + { + data: require("../../core/src/config/devnet/peers.json"), + }, + ]); + + await testSubject.__configurePeers(); + + expect(existsSync(`${configDir}/peers.json`)).toBeTrue(); + }); + }); + + describe("__configureDelegates", () => { + it("should not be OK", async () => { + const mockExit = mockProcess.mockProcessExit(); + + axiosMock.onGet("http://127.0.0.1:4002/config/delegates").reply(() => [404, {}]); + + await testSubject.__configureDelegates(); + + expect(mockExit).toHaveBeenCalledWith(1); + }); + + it("should be OK", async () => { + axiosMock.onGet("http://127.0.0.1:4002/config/delegates").reply(() => [ + 200, + { + data: require("../../core/src/config/devnet/delegates.json"), + }, + ]); + + await testSubject.__configureDelegates(); + + expect(existsSync(`${configDir}/delegates.json`)).toBeTrue(); + }); + }); + + describe("__configurePlugins", () => { + it("should be OK", async () => { + await testSubject.__configurePlugins({ name: "devnet" }); + + expect(existsSync(`${configDir}/plugins.js`)).toBeTrue(); + }); + }); +}); diff --git a/packages/core-container/__tests__/container.test.js b/packages/core-container/__tests__/container.test.js deleted file mode 100644 index f0ac16b9f1..0000000000 --- a/packages/core-container/__tests__/container.test.js +++ /dev/null @@ -1,48 +0,0 @@ -const path = require('path') -const { asValue } = require('awilix') - -let app -beforeEach(async () => { - app = require('../lib') - - await app.setUp( - '2.0.0', - { - data: 'fake-path', - config: path.resolve(__dirname, '../../core/lib/config/testnet'), - token: 'ark', - network: 'testnet', - }, - { - skipPlugins: true, - }, - ) -}) - -describe('Container', () => { - it('should be an object', () => { - expect(app).toBeObject() - }) - - it('should add a new registration', () => { - app.register('fake', asValue('value')) - - expect(app.container.registrations.fake).toBeTruthy() - }) - - it('should resolve a registration', () => { - app.register('fake', asValue('value')) - - expect(app.resolve('fake')).toBe('value') - }) - - it('should determine if a registration exists', () => { - app.register('fake', asValue('value')) - - expect(app.has('fake')).toBeTrue() - }) - - it('should resolve and export paths', () => { - expect(process.env.ARK_PATH_DATA).toEqual(path.resolve('fake-path')) - }) -}) diff --git a/packages/core-container/__tests__/container.test.ts b/packages/core-container/__tests__/container.test.ts new file mode 100644 index 0000000000..bdd57ad12c --- /dev/null +++ b/packages/core-container/__tests__/container.test.ts @@ -0,0 +1,86 @@ +import "jest-extended"; + +import { asValue } from "awilix"; +import { resolve } from "path"; +import { app } from "../src"; + +const dummyPlugin = { + name: "dummy", + version: "0.1.0", + plugin: { key: "value" }, + options: { key: "value" }, +}; + +beforeEach(async () => { + await app.setUp( + "2.0.0", + { + data: "fake-path", + config: resolve(__dirname, "../../core/src/config/testnet"), + token: "ark", + network: "testnet", + }, + { + skipPlugins: true, + }, + ); +}); + +describe("Container", () => { + it("should add a new registration", () => { + app.register("fake", asValue("value")); + + expect(app.has("fake")).toBeTruthy(); + expect(app.has("unregistered")).toBeFalsy(); + }); + + it("should resolve a registration", () => { + app.register("fake", asValue("value")); + + expect(app.resolve("fake")).toBe("value"); + }); + + it("should resolve a plugin", () => { + app.register("fake", asValue(dummyPlugin)); + + expect(app.resolvePlugin("fake")).toEqual(dummyPlugin.plugin); + }); + + it("should resolve the options of a plugin", () => { + app.register("fake", asValue(dummyPlugin)); + + expect(app.resolveOptions("fake")).toEqual(dummyPlugin.options); + }); + + it("should determine if a registration exists", () => { + app.register("fake", asValue("value")); + + expect(app.has("fake")).toBeTrue(); + }); + + it("should determine if a registration exists", () => { + app.register("fake", asValue("value")); + + expect(app.has("fake")).toBeTrue(); + }); + + it("should get the hashid", () => { + expect(app.getHashid()).toBeString(); + }); + + it("should get the version", () => { + expect(app.getVersion()).toBe("2.0.0"); + }); + + it("should set the version", () => { + expect(app.getVersion()).toBe("2.0.0"); + + app.setVersion("3.0.0"); + + expect(app.getVersion()).toBe("3.0.0"); + }); + + it("should resolve and export paths", () => { + expect(process.env.CORE_PATH_DATA).toEqual(resolve("fake-path")); + }); +}); diff --git a/packages/core-container/__tests__/registrars/plugin.test.js b/packages/core-container/__tests__/registrars/plugin.test.js deleted file mode 100644 index 7e09eb1cdb..0000000000 --- a/packages/core-container/__tests__/registrars/plugin.test.js +++ /dev/null @@ -1,121 +0,0 @@ -const path = require('path') -const Container = require('../../lib/container') -const PluginRegistrar = require('../../lib/registrars/plugin') - -const stubPluginPath = path.resolve(__dirname, '../__stubs__') - -let instance -beforeEach(() => { - process.env.ARK_PATH_CONFIG = stubPluginPath - - instance = new PluginRegistrar(new Container()) -}) - -describe('Plugin Registrar', () => { - it('should be an object', () => { - expect(instance).toBeObject() - }) - - it('should load the plugins and their options', () => { - ;['a', 'b', 'c'].forEach(char => { - const pluginName = `./plugin-${char}` - expect(instance.plugins[pluginName]).toBeObject() - }) - - expect(instance.plugins['./plugin-b']).toHaveProperty('property', 'value') - }) - - describe('register', () => { - it('should be a function', () => { - expect(instance.setUp).toBeFunction() - }) - - it('should register plugins with relative paths', async () => { - const pluginName = './plugin-a' - - await instance.register(pluginName, { enabled: false }) - - expect(instance.container.has('stub-plugin-a')).toBeTrue() - }) - - it.skip('should register plugins with @ paths', () => {}) - }) - - describe('setUp', () => { - it('should be a function', () => { - expect(instance.setUp).toBeFunction() - }) - - it('should register each plugin', async () => { - await instance.setUp() - ;['a', 'b', 'c'].forEach(char => { - expect(instance.container.has(`stub-plugin-${char}`)).toBeTrue() - }) - }) - - describe('with a plugin name as the value of the `exit` option', () => { - it('should register the plugins but ignore the rest', async () => { - instance.options.exit = './plugin-a' - - await instance.setUp() - - expect(instance.container.has('stub-plugin-a')).toBeTrue() - ;['b', 'c'].forEach(char => { - expect(instance.container.has(`stub-plugin-${char}`)).toBeFalse() - }) - }) - }) - }) - - describe('tearDown', () => { - const plugins = {} - - beforeEach(async () => { - await instance.setUp() - ;['a', 'b', 'c'].forEach(char => { - expect(instance.container.has(`stub-plugin-${char}`)).toBeTrue() - }) - ;['a', 'b', 'c'].forEach(char => { - plugins[char] = require(`${stubPluginPath}/plugin-${char}`) - }) - }) - - it('should deregister plugins supporting deregister', async () => { - ;['a', 'b'].forEach(char => { - plugins[char].plugin.deregister = jest.fn() - }) - - await instance.tearDown() - ;['a', 'b'].forEach(char => { - expect(plugins[char].plugin.deregister).toHaveBeenCalled() - }) - - expect(plugins.c.deregister).not.toBeDefined() - }) - - it('should deregister all the plugins in inverse order', async () => { - const spy = jest.fn() - ;['a', 'b'].forEach(char => { - plugins[char].plugin.deregister = () => spy(char) - }) - - await instance.tearDown() - - expect(spy).toHaveBeenNthCalledWith(1, 'b') - expect(spy).toHaveBeenNthCalledWith(2, 'a') - }) - }) - - describe('__castOptions', () => { - it('should cast options', async () => { - const options = { - number: '1', - notANumber: '0.0.0.0', - } - - instance.__castOptions(options) - expect(options.number).toEqual(1) - expect(options.notANumber).toEqual('0.0.0.0') - }) - }) -}) diff --git a/packages/core-container/__tests__/registrars/plugin.test.ts b/packages/core-container/__tests__/registrars/plugin.test.ts new file mode 100644 index 0000000000..657e0b5198 --- /dev/null +++ b/packages/core-container/__tests__/registrars/plugin.test.ts @@ -0,0 +1,114 @@ +import "jest-extended"; + +import { resolve } from "path"; +import { Container } from "../../src/container"; +import { PluginRegistrar } from "../../src/registrars/plugin"; + +const stubPluginPath = resolve(__dirname, "../__stubs__"); + +let instance; +beforeEach(() => { + process.env.CORE_PATH_CONFIG = stubPluginPath; + + instance = new PluginRegistrar(new Container()); +}); + +describe("Plugin Registrar", () => { + it("should load the plugins and their options", () => { + ["a", "b", "c"].forEach(char => { + const pluginName = `./plugin-${char}`; + expect(instance.plugins[pluginName]).toBeObject(); + }); + + expect(instance.plugins["./plugin-b"]).toHaveProperty("property", "value"); + }); + + describe("register", () => { + it("should register plugins with relative paths", async () => { + const pluginName = "./plugin-a"; + + await instance.register(pluginName, { enabled: false }); + + expect(instance.container.has("stub-plugin-a")).toBeTrue(); + }); + }); + + describe("setUp", () => { + it("should register each plugin", async () => { + await instance.setUp(); + const plugins = ["a", "b", "c"]; + plugins.forEach(char => { + expect(instance.container.has(`stub-plugin-${char}`)).toBeTrue(); + }); + }); + + describe("with a plugin name as the value of the `exit` option", () => { + it("should register the plugins but ignore the rest", async () => { + instance.options.exit = "./plugin-a"; + + await instance.setUp(); + + expect(instance.container.has("stub-plugin-a")).toBeTrue(); + const plugins = ["b", "c"]; + plugins.forEach(char => { + expect(instance.container.has(`stub-plugin-${char}`)).toBeFalse(); + }); + }); + }); + }); + + describe("tearDown", () => { + const plugins: any = {}; + + beforeEach(async () => { + await instance.setUp(); + const dummyPlugins = ["a", "b", "c"]; + dummyPlugins.forEach(char => { + expect(instance.container.has(`stub-plugin-${char}`)).toBeTrue(); + }); + dummyPlugins.forEach(char => { + plugins[char] = require(`${stubPluginPath}/plugin-${char}`); + }); + }); + + it("should deregister plugins supporting deregister", async () => { + const dummyPlugins = ["a", "b"]; + dummyPlugins.forEach(char => { + plugins[char].plugin.deregister = jest.fn(); + }); + + await instance.tearDown(); + dummyPlugins.forEach(char => { + expect(plugins[char].plugin.deregister).toHaveBeenCalled(); + }); + + expect(plugins.c.deregister).not.toBeDefined(); + }); + + it("should deregister all the plugins in inverse order", async () => { + const spy = jest.fn(); + const dummyPlugins = ["a", "b"]; + dummyPlugins.forEach(char => { + plugins[char].plugin.deregister = () => spy(char); + }); + + await instance.tearDown(); + + expect(spy).toHaveBeenNthCalledWith(1, "b"); + expect(spy).toHaveBeenNthCalledWith(2, "a"); + }); + }); + + describe("__castOptions", () => { + it("should cast options", async () => { + const options = { + number: "1", + notANumber: "0.0.0.0", + }; + + instance.__castOptions(options); + expect(options.number).toEqual(1); + expect(options.notANumber).toEqual("0.0.0.0"); + }); + }); +}); diff --git a/packages/core-container/__tests__/remote-loader.test.js b/packages/core-container/__tests__/remote-loader.test.js deleted file mode 100644 index ca4d002486..0000000000 --- a/packages/core-container/__tests__/remote-loader.test.js +++ /dev/null @@ -1,171 +0,0 @@ -const fs = require('fs-extra') -const mockProcess = require('jest-mock-process') - -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const RemoteLoader = require('../lib/remote-loader') - -const axiosMock = new MockAdapter(axios) -const configDir = './__test-remote-config__' - -let testSubject - -afterAll(() => { - fs.removeSync(configDir) -}) - -beforeEach(() => { - testSubject = new RemoteLoader({ - remote: '127.0.0.1:4002', - config: configDir, - data: './data', - }) -}) - -afterEach(() => { - axiosMock.reset() -}) - -describe('Remote Loader', () => { - it('should be an object', () => { - expect(testSubject).toBeObject() - }) - - it('should ensure the config directory exists', () => { - expect(fs.pathExistsSync(testSubject.config)).toBeTrue() - }) - - describe('__configureNetwork', () => { - it('should be a function', () => { - expect(testSubject.__configureNetwork).toBeFunction() - }) - - it('should not be OK', async () => { - const mockExit = mockProcess.mockProcessExit() - - axiosMock - .onGet('http://127.0.0.1:4002/config/network') - .reply(() => [404, {}]) - - await testSubject.__configureNetwork() - - expect(mockExit).toHaveBeenCalledWith(1) - }) - - it('should be OK', async () => { - axiosMock.onGet('http://127.0.0.1:4002/config/network').reply(() => [ - 200, - { - data: require('../../crypto/lib/networks/ark/devnet.json'), - }, - ]) - - await testSubject.__configureNetwork() - - expect(fs.existsSync(`${configDir}/network.json`)).toBeTrue() - }) - }) - - describe('__configureGenesisBlock', () => { - it('should be a function', () => { - expect(testSubject.__configureGenesisBlock).toBeFunction() - }) - - it('should not be OK', async () => { - axiosMock - .onGet('http://127.0.0.1:4002/config/genesis-block') - .reply(() => [404, {}]) - - await expect(testSubject.__configureGenesisBlock()).rejects.toThrowError() - }) - - it('should be OK', async () => { - axiosMock - .onGet('http://127.0.0.1:4002/config/genesis-block') - .reply(() => [ - 200, - { - data: require('../../core/lib/config/devnet/genesisBlock.json'), - }, - ]) - - await testSubject.__configureGenesisBlock() - - expect(fs.existsSync(`${configDir}/genesisBlock.json`)).toBeTrue() - }) - }) - - describe('__configurePeers', () => { - it('should be a function', () => { - expect(testSubject.__configurePeers).toBeFunction() - }) - - it('should not be OK', async () => { - const mockExit = mockProcess.mockProcessExit() - - axiosMock - .onGet('http://127.0.0.1:4002/config/peers') - .reply(() => [404, {}]) - - await testSubject.__configurePeers() - - expect(mockExit).toHaveBeenCalledWith(1) - }) - - it('should be OK', async () => { - axiosMock.onGet('http://127.0.0.1:4002/config/peers').reply(() => [ - 200, - { - data: require('../../core/lib/config/devnet/peers.json'), - }, - ]) - - await testSubject.__configurePeers() - - expect(fs.existsSync(`${configDir}/peers.json`)).toBeTrue() - }) - }) - - describe('__configureDelegates', () => { - it('should be a function', () => { - expect(testSubject.__configureDelegates).toBeFunction() - }) - - it('should not be OK', async () => { - const mockExit = mockProcess.mockProcessExit() - - axiosMock - .onGet('http://127.0.0.1:4002/config/delegates') - .reply(() => [404, {}]) - - await testSubject.__configureDelegates() - - expect(mockExit).toHaveBeenCalledWith(1) - }) - - it('should be OK', async () => { - axiosMock.onGet('http://127.0.0.1:4002/config/delegates').reply(() => [ - 200, - { - data: require('../../core/lib/config/devnet/delegates.json'), - }, - ]) - - await testSubject.__configureDelegates() - - expect(fs.existsSync(`${configDir}/delegates.json`)).toBeTrue() - }) - }) - - describe('__configurePlugins', () => { - it('should be a function', () => { - expect(testSubject.__configurePlugins).toBeFunction() - }) - - it('should be OK', async () => { - await testSubject.__configurePlugins({ name: 'devnet' }) - - expect(fs.existsSync(`${configDir}/plugins.js`)).toBeTrue() - }) - }) -}) diff --git a/packages/core-container/jest.config.js b/packages/core-container/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-container/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-container/lib/container.js b/packages/core-container/lib/container.js deleted file mode 100644 index f5d25cf845..0000000000 --- a/packages/core-container/lib/container.js +++ /dev/null @@ -1,247 +0,0 @@ -const { createContainer } = require('awilix') -const semver = require('semver') -const delay = require('delay') -const PluginRegistrar = require('./registrars/plugin') -const Environment = require('./environment') -const RemoteLoader = require('./remote-loader') - -module.exports = class Container { - /** - * Create a new container instance. - * @constructor - */ - constructor() { - this.container = createContainer() - this.exitEvents = ['SIGINT', 'exit'] - - /** - * May be used by CLI programs to suppress the shutdown - * messages. - */ - this.silentShutdown = false - - /** - * The git commit hash of the repository. Used during development to - * easily idenfity nodes based on their commit hash and version. - */ - try { - this.hashid = require('child_process') - .execSync('git rev-parse --short=8 HEAD') - .toString() - .trim() - } catch (e) { - this.hashid = 'unknown' - } - } - - /** - * Set up the app. - * @param {String} version - * @param {Object} variables - * @param {Object} options - * @return {void} - */ - async setUp(version, variables, options = {}) { - this.__registerExitHandler() - - this.setVersion(version) - - if (variables.remote) { - const remoteLoader = new RemoteLoader(variables) - await remoteLoader.setUp() - } - - this.env = new Environment(variables) - this.env.setUp() - - if (options.skipPlugins) { - return - } - - // TODO: Move this out eventually - not really the responsibility of the container - this.plugins = new PluginRegistrar(this, options) - await this.plugins.setUp() - } - - /** - * Tear down the app. - * @return {Promise} - */ - async tearDown() { - return this.plugins.tearDown() - } - - /** - * Add a new registration. - * @param {string} key - * @return {Object} - * @throws {Error} - */ - register(name, resolver) { - try { - return this.container.register(name, resolver) - } catch (err) { - throw new Error(err.message) - } - } - - /** - * Resolve a registration. - * @param {string} key - * @return {Object} - * @throws {Error} - */ - resolve(key) { - try { - return this.container.resolve(key) - } catch (err) { - throw new Error(err.message) - } - } - - /** - * Resolve a plugin. - * @param {string} key - * @return {Object} - * @throws {Error} - */ - resolvePlugin(key) { - try { - return this.container.resolve(key).plugin - } catch (err) { - return null - } - } - - /** - * Resolve the options of a plugin. Available before a plugin mounts. - * @param {string} key - * @return {Object} - * @throws {Error} - */ - resolveOptions(key) { - return this.plugins.resolveOptions(key) - } - - /** - * Determine if the given registration exists. - * @param {String} key - * @return {Boolean} - */ - has(key) { - try { - this.container.resolve(key) - - return true - } catch (err) { - return false - } - } - - /** - * Force the container to exit and print the given message and associated error. - * @param {String} message - * @param {Error} error - * @return {void} - */ - forceExit(message, error = null) { - this.exit(1, message, error) - } - - /** - * Exit the container with the given exitCode, message and associated error. - * @param {Number} exitCode - * @param {String} message - * @param {Error} error - * @return {void} - */ - exit(exitCode, message, error = null) { - this.shuttingDown = true - - const logger = this.resolvePlugin('logger') - logger.error(':boom: Container force shutdown :boom:') - logger.error(message) - - if (error) { - logger.error(error.stack) - } - - process.exit(exitCode) - } - - /** - * Get the application git commit hash. - * @throws {String} - */ - getHashid() { - return this.hashid - } - - /** - * Get the application version. - * @throws {String} - */ - getVersion() { - return this.version - } - - /** - * Set the application version. - * @param {String} version - * @return {void} - */ - setVersion(version) { - if (!semver.valid(version)) { - this.forceExit( - `The provided version ("${version}") is invalid. Please check https://semver.org/ and make sure you follow the spec.`, - ) - } - - this.version = version - } - - /** - * Handle any exit signals. - * @return {void} - */ - __registerExitHandler() { - const handleExit = async () => { - if (this.shuttingDown) { - return - } - - this.shuttingDown = true - - const logger = this.resolvePlugin('logger') - logger.suppressConsoleOutput(this.silentShutdown) - logger.info( - 'Ark Core is trying to gracefully shut down to avoid data corruption :pizza:', - ) - - try { - const database = this.resolvePlugin('database') - if (database) { - const emitter = this.resolvePlugin('event-emitter') - - // Notify plugins about shutdown - emitter.emit('shutdown') - - // Wait for event to be emitted and give time to finish - await delay(1000) - - // Save dirty wallets - await database.saveWallets(false) - } - } catch (error) { - console.error(error.stack) - } - - await this.plugins.tearDown() - - process.exit() - } - - // Handle exit events - this.exitEvents.forEach(eventType => process.on(eventType, handleExit)) - } -} diff --git a/packages/core-container/lib/environment.js b/packages/core-container/lib/environment.js deleted file mode 100644 index 67aa61f0c7..0000000000 --- a/packages/core-container/lib/environment.js +++ /dev/null @@ -1,97 +0,0 @@ -const fs = require('fs-extra') -const path = require('path') -const expandHomeDir = require('expand-home-dir') -const { NetworkManager } = require('@arkecosystem/crypto') - -module.exports = class Environment { - /** - * Create a new environment instance. - * @param {Object} variables - * @return {void} - */ - constructor(variables) { - this.variables = variables - } - - /** - * Set up the environment variables. - */ - setUp() { - this.__exportPaths() - this.__exportNetwork() - this.__exportVariables() - } - - /** - * Export all path variables for the core environment. - * @return {void} - */ - __exportPaths() { - const allowedKeys = ['config', 'data'] - - for (const [key, value] of Object.entries(this.variables)) { - if (allowedKeys.includes(key)) { - process.env[`ARK_PATH_${key.toUpperCase()}`] = path.resolve( - expandHomeDir(value), - ) - } - } - } - - /** - * Export all network variables for the core environment. - * @return {void} - */ - __exportNetwork() { - let config - - if (this.variables.token && this.variables.network) { - config = NetworkManager.findByName( - this.variables.network, - this.variables.token, - ) - } else { - try { - const networkPath = path.resolve( - expandHomeDir(`${process.env.ARK_PATH_CONFIG}/network.json`), - ) - - config = require(networkPath) - } catch (error) { - config = false - } - } - - if (!config) { - throw new Error( - "An invalid network configuration was provided or is inaccessible due to it's security settings.", - ) - process.exit(1) // eslint-disable-line no-unreachable - } - - process.env.ARK_NETWORK = JSON.stringify(config) - process.env.ARK_NETWORK_NAME = config.name - } - - /** - * Export all additional variables for the core environment. - * @return {void} - */ - __exportVariables() { - // Don't pollute the test environment, which is more in line with how - // travis runs the tests. - if (process.env.NODE_ENV === 'test') { - return - } - - const envPath = expandHomeDir(`${process.env.ARK_PATH_DATA}/.env`) - - if (fs.existsSync(envPath)) { - const env = require('envfile').parseFileSync(envPath) - - Object.keys(env).forEach(key => { - process.env[key] = env[key] - }) - } - } -} diff --git a/packages/core-container/lib/index.js b/packages/core-container/lib/index.js deleted file mode 100644 index 914fde2228..0000000000 --- a/packages/core-container/lib/index.js +++ /dev/null @@ -1,6 +0,0 @@ -const Container = require('./container') - -/** - * @type {Container} - */ -module.exports = new Container() diff --git a/packages/core-container/lib/registrars/plugin.js b/packages/core-container/lib/registrars/plugin.js deleted file mode 100644 index 48035f49a9..0000000000 --- a/packages/core-container/lib/registrars/plugin.js +++ /dev/null @@ -1,255 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const path = require('path') -const fs = require('fs') -const semver = require('semver') -const isString = require('lodash/isString') -const expandHomeDir = require('expand-home-dir') -const Hoek = require('hoek') -const { asValue } = require('awilix') - -module.exports = class PluginRegistrars { - /** - * Create a new plugin manager instance. - * @param {Container} container - * @param {Object} options - */ - constructor(container, options = {}) { - this.container = container - this.plugins = this.__loadPlugins() - this.resolvedPlugins = [] - this.options = this.__castOptions(options) - this.deregister = [] - } - - /** - * Set up all available plugins. - * @return {void} - */ - resolveOptions(name) { - if (!this.resolvedPlugins.length) { - this.resolvedPlugins = Object.keys(this.plugins).map( - item => require(item).plugin, - ) - } - - const plugin = Object.values(this.resolvedPlugins).find( - item => item.alias === name || item.pkg.name === name, - ) - - return this.__applyToDefaults( - plugin.pkg.name, - plugin.defaults, - this.plugins[plugin.pkg.name], - ) - } - - /** - * Set up all available plugins. - * @return {void} - */ - async setUp() { - for (const [name, options] of Object.entries(this.plugins)) { - await this.register(name, options) - - if ( - (this.options.exit && this.options.exit === name) || - this.container.shuttingDown - ) { - break - } - } - } - - /** - * Deregister all plugins. - * @return {void} - */ - async tearDown() { - const plugins = this.deregister.reverse() - - for (let i = 0; i < plugins.length; i++) { - await plugins[i].plugin.deregister(this.container, plugins[i].options) - } - } - - /** - * Register a plugin. - * @param {String} name - * @param {Object} options - * @return {void} - */ - async register(name, options = {}) { - if (!this.__shouldBeRegistered(name)) { - return - } - - if (this.plugins[name]) { - options = Hoek.applyToDefaults(this.plugins[name], options) - } - - return this.__registerWithContainer(name, options) - } - - /** - * Register a plugin. - * @param {Object} plugin - * @param {Object} options - * @return {void} - */ - async __registerWithContainer(plugin, options = {}) { - const item = this.__resolve(plugin) - - if (!item.plugin.register) { - return - } - - if (item.plugin.extends) { - await this.__registerWithContainer(item.plugin.extends) - } - - const name = item.plugin.name || item.plugin.pkg.name - const version = item.plugin.version || item.plugin.pkg.version - const defaults = item.plugin.defaults || item.plugin.pkg.defaults - const alias = item.plugin.alias || item.plugin.pkg.alias - - if (!semver.valid(version)) { - throw new Error( - `The plugin "${name}" provided an invalid version "${version}". Please check https://semver.org/ and make sure you follow the spec.`, - ) - } - - options = this.__applyToDefaults(name, defaults, options) - - plugin = await item.plugin.register(this.container, options || {}) - this.container.register( - alias || name, - asValue({ - name, - version, - plugin, - options, - }), - ) - - if (item.plugin.deregister) { - this.deregister.push({ plugin: item.plugin, options }) - } - } - - /** - * Apply the given options to the defaults of the given plugin. - * - * @param {String} name - * @param {Object} defaults - * @param {Object} options - * @return {Object} - */ - __applyToDefaults(name, defaults, options) { - if (defaults) { - options = Hoek.applyToDefaults(defaults, options) - } - - if (this.options.options && this.options.options[name]) { - options = Hoek.applyToDefaults(options, this.options.options[name]) - } - - return this.__castOptions(options) - } - - /** - * When the env is used to overwrite options, we get strings even if we - * expect a number. This is in most cases not desired and leads to side- - * effects. Here is assumed all numeric strings except blacklisted ones - * should be treated as numbers. - * @param {Object} options - * @return {Object} options - */ - __castOptions(options) { - const blacklist = [] - const regex = new RegExp(/^\d+$/) - Object.keys(options).forEach(key => { - const value = options[key] - if (isString(value) && !blacklist.includes(key) && regex.test(value)) { - options[key] = +value - } - }) - - return options - } - - /** - * Resolve a plugin instance. - * @param {(String|Object)} plugin - plugin name or path, or object - * @return {Object} - */ - __resolve(plugin) { - let item = {} - - if (isString(plugin)) { - if (plugin.startsWith('.')) { - plugin = path.resolve( - `${path.dirname(this.pluginsConfigPath)}/${plugin}`, - ) - } else if (!plugin.startsWith('@')) { - plugin = path.resolve(plugin) - } - - try { - item = require(plugin) - } catch (error) { - console.error(error) - } - - if (!item.plugin) { - item = { plugin: item } - } - } - - return item - } - - /** - * Determine if the given plugin should be registered. - * @param {String} name - * @return {Boolean} - */ - __shouldBeRegistered(name) { - let register = true - - if (this.options.include) { - register = this.options.include.includes(name) - } - - if (this.options.exclude) { - register = !this.options.exclude.includes(name) - } - - return register - } - - /** - * Load plugins from any of the available files (plugins.js or plugins.json). - * @return {[Object|void]} - */ - __loadPlugins() { - const files = ['plugins.js', 'plugins.json'] - - for (const file of files) { - const configPath = path.resolve( - expandHomeDir(`${process.env.ARK_PATH_CONFIG}/${file}`), - ) - - if (fs.existsSync(configPath)) { - this.pluginsConfigPath = configPath - - return require(configPath) - } - } - - throw new Error( - "An invalid configuration was provided or is inaccessible due to it's security settings.", - ) - process.exit(1) // eslint-disable-line no-unreachable - } -} diff --git a/packages/core-container/lib/remote-loader.js b/packages/core-container/lib/remote-loader.js deleted file mode 100644 index f4e5896b75..0000000000 --- a/packages/core-container/lib/remote-loader.js +++ /dev/null @@ -1,112 +0,0 @@ -const axios = require('axios') -const expandHomeDir = require('expand-home-dir') -const fs = require('fs-extra') -const path = require('path') -const { - models: { Block }, -} = require('@arkecosystem/crypto') -const { spawnSync } = require('child_process') - -module.exports = class RemoteLoader { - constructor(variables) { - this.remote = variables.remote - this.config = expandHomeDir(variables.config) - this.data = expandHomeDir(variables.data) - - fs.ensureDirSync(this.config) - } - - async setUp() { - const network = await this.__configureNetwork() - - await this.__configureGenesisBlock() - - await this.__configurePeers() - - await this.__configureDelegates() - - this.__configurePlugins(network) - - this.__configureDatabase(network) - } - - async __configureNetwork() { - const network = await this.__getConfig('network') - - this.__writeConfig('network', network) - - return network - } - - async __configureGenesisBlock() { - const genesisBlock = await this.__getConfig('genesis-block') - const genesisBlockModel = new Block(genesisBlock) - - if (!genesisBlockModel.verification.verified) { - console.error( - 'Failed to verify the genesis block. Try another remote host.', - ) - process.exit(1) - } - - this.__writeConfig('genesisBlock', genesisBlock) - } - - async __configurePeers() { - const peers = await this.__getConfig('peers') - - this.__writeConfig('peers', peers) - } - - async __configureDelegates() { - const delegates = await this.__getConfig('delegates') - - this.__writeConfig('delegates', delegates) - } - - __configurePlugins(network) { - const plugins = path.resolve( - __dirname, - `../../core/lib/config/${network.name}/plugins.js`, - ) - - fs.copySync(plugins, `${this.config}/plugins.js`) - } - - __configureDatabase(network) { - const command = spawnSync('createdb', [`ark_${network.name}`]) - - if (command.stderr.length > 0) { - console.error(command.stderr.toString()) - process.exit(1) - } - - console.info(command.stdout.toString()) - } - - async __getConfig(type) { - try { - const { data } = await axios.get(`http://${this.remote}/config/${type}`, { - headers: { 'Content-Type': 'application/json' }, - }) - - return data.data - } catch (error) { - if (!this.__exists(type)) { - console.error(error.message) - process.exit(1) - } - } - } - - __writeConfig(file, data) { - fs.writeFileSync( - `${this.config}/${file}.json`, - JSON.stringify(data, null, 4), - ) - } - - __exists(file) { - return fs.existsSync(`${this.config}/${file}.json`) - } -} diff --git a/packages/core-container/package.json b/packages/core-container/package.json index b6c34cfe71..f0e4c9b12c 100644 --- a/packages/core-container/package.json +++ b/packages/core-container/package.json @@ -1,40 +1,68 @@ { - "name": "@arkecosystem/core-container", - "description": "Container for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/crypto": "~0.2", - "awilix": "^4.0.1", - "axios": "^0.18.0", - "delay": "^4.1.0", - "envfile": "^2.3.0", - "expand-home-dir": "^0.0.3", - "fs-extra": "^7.0.1", - "hoek": "^6.1.1", - "lodash.isstring": "^4.0.1", - "semver": "^5.6.0" - }, - "devDependencies": { - "axios-mock-adapter": "^1.15.0", - "jest-mock-process": "^1.1.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-container", + "description": "Container for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/fs-extra": "^5.0.4", + "@types/hoek": "^4.1.3", + "@types/joi": "^14.0.1", + "@types/lodash.get": "^4.4.4", + "@types/lodash.isstring": "^4.0.4", + "@types/lodash.set": "^4.3.4", + "@types/semver": "^5.5.0", + "awilix": "^4.0.1", + "axios": "^0.18.0", + "delay": "^4.1.0", + "env-paths": "^2.0.0", + "envfile": "^2.3.0", + "expand-home-dir": "^0.0.3", + "fs-extra": "^7.0.1", + "hoek": "^6.1.2", + "joi": "^14.3.0", + "lodash.get": "^4.4.2", + "lodash.isstring": "^4.0.1", + "lodash.set": "^4.3.2", + "semver": "^5.6.0" + }, + "devDependencies": { + "@types/env-paths": "^1.0.2", + "axios-mock-adapter": "^1.15.0", + "jest-mock-process": "^1.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-container/src/config/index.ts b/packages/core-container/src/config/index.ts new file mode 100644 index 0000000000..2c370cb9ad --- /dev/null +++ b/packages/core-container/src/config/index.ts @@ -0,0 +1,66 @@ +import { configManager as crypto, HashAlgorithms } from "@arkecosystem/crypto"; +import get from "lodash/get"; +import set from "lodash/set"; +import { fileLoader, RemoteLoader } from "./loaders"; +import { Network } from "./network"; + +class Config { + private config: Record; + + public async setUp(opts) { + if (opts.remote) { + const remoteLoader = new RemoteLoader(opts); + await remoteLoader.setUp(); + } + + const network = Network.setUp(opts); + + const { files } = await fileLoader.setUp(network); + + this.config = files; + + this.configureCrypto(network); + + return this; + } + + public all(): any { + return this.config; + } + + public get(key: string, defaultValue: any = null): any { + return get(this.config, key, defaultValue); + } + + public set(key: string, value: any): void { + set(this.config, key, value); + } + + /** + * Get constants for the specified height. + */ + public getMilestone(height: number): any { + return crypto.getMilestone(height); + } + + /** + * Configure the @arkecosystem/crypto package. + * @return {void} + */ + private configureCrypto(value: any): void { + crypto.setConfig(value); + + this.config.network = crypto.all(); + this.config.exceptions = crypto.get("exceptions"); + this.config.milestones = crypto.get("milestones"); + this.config.genesisBlock = crypto.get("genesisBlock"); + + // Calculate milestone hash + const milestonesBuffer = Buffer.from(JSON.stringify(this.config.milestones)); + this.config.milestoneHash = HashAlgorithms.sha256(milestonesBuffer) + .slice(0, 8) + .toString("hex"); + } +} + +export const configManager = new Config(); diff --git a/packages/core-container/src/config/loaders/file-loader.ts b/packages/core-container/src/config/loaders/file-loader.ts new file mode 100644 index 0000000000..8120c53b44 --- /dev/null +++ b/packages/core-container/src/config/loaders/file-loader.ts @@ -0,0 +1,101 @@ +import { configManager } from "@arkecosystem/crypto"; +import axios from "axios"; +import { existsSync, readdirSync, writeFileSync } from "fs-extra"; +import Joi from "joi"; +import { basename, extname, resolve } from "path"; +import { schemaConfig } from "../schema"; + +class FileLoader { + /** + * Make the config instance. + * @param {Object} opts + * @return {Loader} + */ + public async setUp(opts) { + if (!opts) { + throw new Error("Invalid network configuration provided."); + } + + const files = await this.createFromDirectory(); + + const { value, error } = Joi.validate(files, schemaConfig); + + if (error) { + throw error; + } + + return { config: value, files }; + } + + /** + * Load and bind the config. + * @return {void} + */ + private async createFromDirectory() { + const files: Record = this.getFiles(); + + for (const [key, value] of Object.entries(files)) { + files[key] = require(value); + } + + await this.buildPeers(files.peers); + + return files; + } + + /** + * Get all config files. + * @return {Object} + */ + private getFiles(): Record { + const basePath = resolve(process.env.CORE_PATH_CONFIG); + + if (!existsSync(basePath)) { + throw new Error("An invalid configuration was provided or is inaccessible due to it's security settings."); + } + + const configTree = {}; + for (const file of readdirSync(basePath)) { + if ([".js", ".json"].includes(extname(file))) { + configTree[basename(file, extname(file))] = resolve(basePath, file); + } + } + + return configTree; + } + + /** + * Build the peer list either from a local file, remote file or object. + * @param {String} configFile + * @return {void} + */ + private async buildPeers(configFile: any): Promise { + if (configFile.sources) { + for (const source of configFile.sources) { + // Local File... + if (source.startsWith("/")) { + configFile.list = require(source); + + writeFileSync(configFile, JSON.stringify(configFile, null, 2)); + + break; + } + + // URL... + try { + const response = await axios.get(source); + + configFile.list = response.data; + + writeFileSync(configFile, JSON.stringify(configFile, null, 2)); + + break; + } catch (error) { + // + } + } + } + } +} + +export const fileLoader = new FileLoader(); diff --git a/packages/core-container/src/config/loaders/index.ts b/packages/core-container/src/config/loaders/index.ts new file mode 100644 index 0000000000..a59ad04e10 --- /dev/null +++ b/packages/core-container/src/config/loaders/index.ts @@ -0,0 +1,2 @@ +export * from "./file-loader"; +export * from "./remote-loader"; diff --git a/packages/core-container/src/config/loaders/remote-loader.ts b/packages/core-container/src/config/loaders/remote-loader.ts new file mode 100644 index 0000000000..1481d0df2a --- /dev/null +++ b/packages/core-container/src/config/loaders/remote-loader.ts @@ -0,0 +1,130 @@ +import { models } from "@arkecosystem/crypto"; +import axios from "axios"; +import { spawnSync } from "child_process"; +import expandHomeDir from "expand-home-dir"; +import { copySync, ensureDirSync, existsSync, writeFileSync } from "fs-extra"; +import { resolve } from "path"; + +export class RemoteLoader { + private remote: any; + private config: any; + + constructor(variables) { + this.remote = variables.remote; + this.config = expandHomeDir(variables.config); + + ensureDirSync(this.config); + } + + public async setUp() { + const network = await this.configureNetwork(); + + await this.configureExceptions(); + + await this.configureMilestones(); + + await this.configureGenesisBlock(); + + await this.configurePeers(); + + await this.configureDelegates(); + + this.configurePlugins(network); + + this.configureDatabase(network); + } + + private async configureNetwork() { + const network = await this.getConfig("network"); + + this.writeConfig("network", network); + + return network; + } + + private async configureExceptions() { + const exceptions = await this.getConfig("exceptions"); + + this.writeConfig("exceptions", exceptions); + + return exceptions; + } + + private async configureMilestones() { + const milestones = await this.getConfig("milestones"); + + this.writeConfig("milestones", milestones); + + return milestones; + } + + private async configureGenesisBlock() { + const { Block } = models; + + const genesisBlock = await this.getConfig("genesis-block"); + const genesisBlockModel = new Block(genesisBlock); + + if (!genesisBlockModel.verification.verified) { + // tslint:disable-next-line:no-console + console.error("Failed to verify the genesis block. Try another remote host."); + process.exit(1); + } + + this.writeConfig("genesisBlock", genesisBlock); + } + + private async configurePeers() { + const peers = await this.getConfig("peers"); + + this.writeConfig("peers", peers); + } + + private async configureDelegates() { + const delegates = await this.getConfig("delegates"); + + this.writeConfig("delegates", delegates); + } + + private configurePlugins(network) { + const plugins = resolve(__dirname, `../../core/src/config/${network.name}/plugins.js`); + + copySync(plugins, `${this.config}/plugins.js`); + } + + private configureDatabase(network) { + const command = spawnSync("createdb", [`core_${network.name}`]); + + if (command.stderr.length > 0) { + // tslint:disable-next-line:no-console + console.error(command.stderr.toString()); + process.exit(1); + } + + // tslint:disable-next-line:no-console + console.info(command.stdout.toString()); + } + + private async getConfig(type) { + try { + const { data } = await axios.get(`http://${this.remote}/config/${type}`, { + headers: { "Content-Type": "application/json" }, + }); + + return data.data; + } catch (error) { + if (!this.exists(type)) { + // tslint:disable-next-line:no-console + console.error(error.message); + process.exit(1); + } + } + } + + private writeConfig(file, data) { + writeFileSync(`${this.config}/${file}.json`, JSON.stringify(data, null, 4)); + } + + private exists(file) { + return existsSync(`${this.config}/${file}.json`); + } +} diff --git a/packages/core-container/src/config/network.ts b/packages/core-container/src/config/network.ts new file mode 100644 index 0000000000..3259247662 --- /dev/null +++ b/packages/core-container/src/config/network.ts @@ -0,0 +1,48 @@ +import { NetworkManager } from "@arkecosystem/crypto"; +import expandHomeDir from "expand-home-dir"; +import Joi from "joi"; +import { resolve } from "path"; +import { schemaNetwork } from "./schema"; + +export class Network { + /** + * Expose information about the for the operating network to the environment. + * @return {void} + */ + public static setUp(opts: any) { + let config; + + // Default configuration... + if (opts.network) { + config = NetworkManager.findByName(opts.network); + } else { + try { + const networkPath = resolve(expandHomeDir(process.env.CORE_PATH_CONFIG)); + + config = { + exceptions: require(`${networkPath}/exceptions`), + milestones: require(`${networkPath}/milestones`), + genesisBlock: require(`${networkPath}/genesisBlock`), + network: require(`${networkPath}/network`), + }; + } catch (error) { + config = false; + } + } + + // Validate the configuration... + const { error } = Joi.validate(config, schemaNetwork); + + if (error) { + throw new Error( + `An invalid network configuration was provided or is inaccessible due to it's security settings. ${ + error.message + }.`, + ); + } + + process.env.CORE_NETWORK_NAME = config.network.name; + + return config; + } +} diff --git a/packages/core-container/src/config/schema.ts b/packages/core-container/src/config/schema.ts new file mode 100644 index 0000000000..e9df8f6e30 --- /dev/null +++ b/packages/core-container/src/config/schema.ts @@ -0,0 +1,50 @@ +import Joi from "joi"; + +export const schemaNetwork = Joi.object({ + milestones: Joi.array() + .items(Joi.object()) + .required(), + exceptions: Joi.object({ + blocks: Joi.array().items(Joi.string()), + transactions: Joi.array().items(Joi.string()), + outlookTable: Joi.object(), + transactionIdFixTable: Joi.object(), + }).default({ exceptions: {} }), + genesisBlock: Joi.object().required(), + network: Joi.object({ + name: Joi.string().required(), + messagePrefix: Joi.string().required(), + bip32: Joi.object({ + public: Joi.number() + .positive() + .required(), + private: Joi.number() + .positive() + .required(), + }), + pubKeyHash: Joi.number() + .positive() + .required(), + nethash: Joi.string() + .hex() + .required(), + wif: Joi.number() + .positive() + .required(), + aip20: Joi.number().required(), + client: Joi.object({ + token: Joi.string().required(), + symbol: Joi.string().required(), + explorer: Joi.string().required(), + }), + }).required(), +}); + +export const schemaConfig = Joi.object({ + delegates: Joi.object({ + secrets: Joi.array().items(Joi.string()), + bip38: Joi.string(), + }), + peers: Joi.object().required(), + plugins: Joi.object().required(), +}).unknown(); diff --git a/packages/core-container/src/container.ts b/packages/core-container/src/container.ts new file mode 100644 index 0000000000..0856862dda --- /dev/null +++ b/packages/core-container/src/container.ts @@ -0,0 +1,280 @@ +import { Container as container, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import { createContainer, Resolver } from "awilix"; +import { execSync } from "child_process"; +import delay from "delay"; +import semver from "semver"; +import { configManager } from "./config"; +import { Environment } from "./environment"; +import { PluginRegistrar } from "./registrars/plugin"; + +export class Container implements container.IContainer { + public options: any; + public exitEvents: any; + /** + * May be used by CLI programs to suppress the shutdown messages. + */ + public silentShutdown = false; + public hashid: string; + public plugins: any; + public shuttingDown: boolean; + public version: string; + public isReady: boolean = false; + public variables: any; + public config: any; + private container = createContainer(); + + /** + * Create a new container instance. + * @constructor + */ + constructor() { + /** + * The git commit hash of the repository. Used during development to + * easily idenfity nodes based on their commit hash and version. + */ + try { + this.hashid = execSync("git rev-parse --short=8 HEAD") + .toString() + .trim(); + } catch (e) { + this.hashid = "unknown"; + } + + /** + * Register any exit signal handling. + */ + this.registerExitHandler(["SIGINT", "exit"]); + } + + /** + * Set up the app. + * @param {String} version + * @param {Object} variables + * @param {Object} options + * @return {void} + */ + public async setUp(version: string, variables: any, options: any = {}) { + this.options = options; + this.variables = variables; + + this.setVersion(version); + + // Register the environment variables + new Environment(variables).setUp(); + + // Mainly used for testing environments! + if (options.skipPlugins) { + this.isReady = true; + return; + } + + // Setup the configuration + this.config = await configManager.setUp(variables); + + // TODO: Move this out eventually - not really the responsibility of the container + this.plugins = new PluginRegistrar(this, options); + await this.plugins.setUp(); + + this.isReady = true; + } + + public getConfig() { + return this.config; + } + + /** + * Tear down the app. + * @return {Promise} + */ + public async tearDown() { + if (!this.options.skipPlugins) { + await this.plugins.tearDown(); + } + + this.isReady = false; + } + + /** + * Add a new registration. + */ + public register(name, resolver: Resolver) { + try { + this.container.register(name, resolver); + return this; + } catch (err) { + throw new Error(err.message); + } + } + + /** + * Resolve a registration. + * @param {string} key + * @return {Object} + * @throws {Error} + */ + public resolve(key): T { + + try { + return this.container.resolve(key); + } catch (err) { + throw new Error(err.message); + } + } + + /** + * Resolve a plugin. + * @param {string} key + * @return {Object} + * @throws {Error} + */ + public resolvePlugin(key): T { + try { + return this.container.resolve>(key).plugin; + } catch (err) { + return null; + } + } + + /** + * Resolve the options of a plugin. Available before a plugin mounts. + * @param {string} key + * @return {Object} + * @throws {Error} + */ + public resolveOptions(key) { + try { + return this.container.resolve>(key).options; + } catch (err) { + throw err; + } + } + + /** + * Determine if the given registration exists. + * @param {String} key + * @return {Boolean} + */ + public has(key) { + try { + this.container.resolve(key); + + return true; + } catch (err) { + return false; + } + } + + /** + * Force the container to exit and print the given message and associated error. + * @param {String} message + * @param {Error} error + * @return {void} + */ + public forceExit(message, error = null) { + this.exit(1, message, error); + } + + /** + * Exit the container with the given exitCode, message and associated error. + * @param {Number} exitCode + * @param {String} message + * @param {Error} error + * @return {void} + */ + public exit(exitCode, message, error = null) { + this.shuttingDown = true; + + const logger = this.resolvePlugin("logger"); + logger.error(":boom: Container force shutdown :boom:"); + logger.error(message); + + if (error) { + logger.error(error.stack); + } + + process.exit(exitCode); + } + + /** + * Get the application git commit hash. + * @throws {String} + */ + public getHashid() { + return this.hashid; + } + + /** + * Get the application version. + * @throws {String} + */ + public getVersion() { + return this.version; + } + + /** + * Set the application version. + * @param {String} version + * @return {void} + */ + public setVersion(version) { + if (!semver.valid(version)) { + this.forceExit( + // tslint:disable-next-line:max-line-length + `The provided version ("${version}") is invalid. Please check https://semver.org/ and make sure you follow the spec.`, + ); + } + + this.version = version; + } + + /** + * Handle any exit signals. + * @return {void} + */ + private registerExitHandler(exitEvents: string[]) { + const handleExit = async () => { + if (this.shuttingDown) { + return; + } + + this.shuttingDown = true; + + const logger = this.resolvePlugin("logger"); + if (logger) { + logger.suppressConsoleOutput(this.silentShutdown); + logger.info("Core is trying to gracefully shut down to avoid data corruption :pizza:"); + } + + try { + /* TODO: core-database-postgres has a dep on core-container. Yet we have code in core-container fetching a reference to core-database-postgres. + If we try to import core-database-postgres types, we create a circular dependency: core-container -> core-database-postgres -> core-container. + The only thing we're doing here is trying to save the wallets upon shutdown. The code can and should be moved into core-database-postgres instead + and leverage either the plugins `tearDown` method or the event-emitter's 'shutdown' event + */ + const database = this.resolvePlugin("database"); + if (database) { + const emitter = this.resolvePlugin("event-emitter"); + + // Notify plugins about shutdown + emitter.emit("shutdown"); + + // Wait for event to be emitted and give time to finish + await delay(1000); + + // Save dirty wallets + await database.saveWallets(false); + } + } catch (error) { + // tslint:disable-next-line:no-console + console.error(error.stack); + } + + await this.plugins.tearDown(); + + process.exit(); + }; + + // Handle exit events + exitEvents.forEach(eventType => process.on(eventType as any, handleExit)); + } +} diff --git a/packages/core-container/src/environment.ts b/packages/core-container/src/environment.ts new file mode 100644 index 0000000000..15bd410c8b --- /dev/null +++ b/packages/core-container/src/environment.ts @@ -0,0 +1,73 @@ +import envPaths from "env-paths"; +import expandHomeDir from "expand-home-dir"; +import { ensureDirSync, existsSync } from "fs-extra"; +import { resolve } from "path"; + +export class Environment { + /** + * Create a new environment instance. + * @param {Object} variables + * @return {void} + */ + constructor(readonly variables: any) {} + + /** + * Set up the environment variables. + */ + public setUp() { + this.exportPaths(); + this.exportVariables(); + } + + /** + * Export all path variables for the core environment. + * @return {void} + */ + private exportPaths() { + const allowedKeys = ["data", "config", "cache", "log", "temp"]; + + const createPathVariables = (values, namespace?) => + allowedKeys.forEach(key => { + if (values[key]) { + const name = `CORE_PATH_${key.toUpperCase()}`; + let path = resolve(expandHomeDir(values[key])); + + if (namespace) { + path += `/${this.variables.network}`; + } + + process.env[name] = path; + ensureDirSync(path); + } + }); + + createPathVariables(envPaths(this.variables.token, { suffix: "core" }), this.variables.network); + + if (this.variables.data || this.variables.config) { + createPathVariables(this.variables); + } + } + + /** + * Export all additional variables for the core environment. + * @return {void} + */ + private exportVariables() { + process.env.CORE_TOKEN = this.variables.token; + + // Don't pollute the test environment! + if (process.env.NODE_ENV === "test") { + return; + } + + const envPath = expandHomeDir(`${process.env.CORE_PATH_CONFIG}/.env`); + + if (existsSync(envPath)) { + const env = require("envfile").parseFileSync(envPath); + + Object.keys(env).forEach(key => { + process.env[key] = env[key]; + }); + } + } +} diff --git a/packages/core-container/src/index.ts b/packages/core-container/src/index.ts new file mode 100644 index 0000000000..6e9d336d6a --- /dev/null +++ b/packages/core-container/src/index.ts @@ -0,0 +1,5 @@ +import { Container as container } from "@arkecosystem/core-interfaces"; +import { Container } from "./container"; + +const app: container.IContainer = new Container(); +export { app }; diff --git a/packages/core-container/src/registrars/plugin.ts b/packages/core-container/src/registrars/plugin.ts new file mode 100644 index 0000000000..a7b6b0e09c --- /dev/null +++ b/packages/core-container/src/registrars/plugin.ts @@ -0,0 +1,229 @@ +import { asValue } from "awilix"; +import expandHomeDir from "expand-home-dir"; +import { existsSync } from "fs"; +import Hoek from "hoek"; +import isString from "lodash/isString"; +import { dirname, resolve } from "path"; +import semver from "semver"; + +export class PluginRegistrar { + private container: any; + private plugins: any; + private resolvedPlugins: any; + private options: any; + private deregister: any; + private pluginsConfigPath: any; + + /** + * Create a new plugin manager instance. + * @param {IContainer} container + * @param {Object} options + */ + constructor(container, options: any = {}) { + this.container = container; + this.plugins = this.__loadPlugins(); + this.resolvedPlugins = []; + this.options = this.__castOptions(options); + this.deregister = []; + } + + /** + * Set up all available plugins. + * @return {void} + */ + public async setUp() { + for (const [name, options] of Object.entries(this.plugins)) { + await this.register(name, options); + + if ((this.options.exit && this.options.exit === name) || this.container.shuttingDown) { + break; + } + } + } + + /** + * Deregister all plugins. + * @return {void} + */ + public async tearDown() { + for (const plugin of this.deregister.reverse()) { + await plugin.plugin.deregister(this.container, plugin.options); + } + } + + /** + * Register a plugin. + * @param {String} name + * @param {Object} options + * @return {void} + */ + public async register(name, options = {}) { + if (!this.__shouldBeRegistered(name)) { + return; + } + + if (this.plugins[name]) { + options = Hoek.applyToDefaults(this.plugins[name], options); + } + + return this.__registerWithContainer(name, options); + } + + /** + * Register a plugin. + * @param {Object} plugin + * @param {Object} options + * @return {void} + */ + public async __registerWithContainer(plugin, options = {}) { + const item: any = this.__resolve(plugin); + + if (!item.plugin.register) { + return; + } + + if (item.plugin.extends) { + await this.__registerWithContainer(item.plugin.extends); + } + + const name = item.plugin.name || item.plugin.pkg.name; + const version = item.plugin.version || item.plugin.pkg.version; + const defaults = item.plugin.defaults || item.plugin.pkg.defaults; + const alias = item.plugin.alias || item.plugin.pkg.alias; + + if (!semver.valid(version)) { + throw new Error( + // tslint:disable-next-line:max-line-length + `The plugin "${name}" provided an invalid version "${version}". Please check https://semver.org/ and make sure you follow the spec.`, + ); + } + + options = this.__applyToDefaults(name, defaults, options); + + plugin = await item.plugin.register(this.container, options || {}); + this.container.register( + alias || name, + asValue({ + name, + version, + plugin, + options, + }), + ); + + if (item.plugin.deregister) { + this.deregister.push({ plugin: item.plugin, options }); + } + } + + /** + * Apply the given options to the defaults of the given plugin. + * + * @param {String} name + * @param {Object} defaults + * @param {Object} options + * @return {Object} + */ + public __applyToDefaults(name, defaults, options) { + if (defaults) { + options = Hoek.applyToDefaults(defaults, options); + } + + if (this.options.options && this.options.options[name]) { + options = Hoek.applyToDefaults(options, this.options.options[name]); + } + + return this.__castOptions(options); + } + + /** + * When the env is used to overwrite options, we get strings even if we + * expect a number. This is in most cases not desired and leads to side- + * effects. Here is assumed all numeric strings except blacklisted ones + * should be treated as numbers. + * @param {Object} options + * @return {Object} options + */ + public __castOptions(options) { + const blacklist: any = []; + const regex = new RegExp(/^\d+$/); + + Object.keys(options).forEach(key => { + const value = options[key]; + if (isString(value) && !blacklist.includes(key) && regex.test(value)) { + options[key] = +value; + } + }); + + return options; + } + + /** + * Resolve a plugin instance. + * @param {(String|Object)} plugin - plugin name or path, or object + * @return {Object} + */ + public __resolve(plugin) { + let item: any = {}; + + if (isString(plugin)) { + if (plugin.startsWith(".")) { + plugin = resolve(`${dirname(this.pluginsConfigPath)}/${plugin}`); + } else if (!plugin.startsWith("@")) { + plugin = resolve(plugin); + } + + try { + item = require(plugin); + } catch (error) { + // tslint:disable-next-line:no-console + console.error(error); + } + + if (!item.plugin) { + item = { plugin: item }; + } + } + + return item; + } + + /** + * Determine if the given plugin should be registered. + * @param {String} name + * @return {Boolean} + */ + public __shouldBeRegistered(name) { + let register = true; + + if (this.options.include) { + register = this.options.include.includes(name); + } + + if (this.options.exclude) { + register = !this.options.exclude.includes(name); + } + + return register; + } + + /** + * Load plugins from any of the available files (plugins.js or plugins.json). + * @return {[Object|void]} + */ + public __loadPlugins() { + const files = ["plugins.js", "plugins.json"]; + + for (const file of files) { + const configPath = resolve(expandHomeDir(`${process.env.CORE_PATH_CONFIG}/${file}`)); + + if (existsSync(configPath)) { + this.pluginsConfigPath = configPath; + + return require(configPath); + } + } + + throw new Error("An invalid configuration was provided or is inaccessible due to it's security settings."); + } +} diff --git a/packages/core-container/tsconfig.json b/packages/core-container/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-container/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-database-postgres/CHANGELOG.md b/packages/core-database-postgres/CHANGELOG.md deleted file mode 100644 index 13db840f6b..0000000000 --- a/packages/core-database-postgres/CHANGELOG.md +++ /dev/null @@ -1,59 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.11 - 2018-12-05 - -### Added - -- Store executed migrations in the database - -## 0.2.1 - 2018-12-05 - -### Added - -- `sender_public_key`, `recipient_id` and `timestamp` indices on the `transactions` table -- `generator_public_key` index on the `blocks` table - -## 0.2.0 - 2018-12-03 - -### Added - -- Database rollback - -### Changed - -- Build delegate list in-memory to reduce database load -- Perform vote balance calculations in-memory to reduce database load -- Handle numbers as `BigNumber` instances -- Reduced complexity and duplicated logic -- Improved performance of various SQL queries -- Improved performance of wallet saving -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Removed - -- All `redis` integrations and dependencies - -### Fixed - -- Wrong documentation -- Bad method calls for `sync/async` methods -- Cast rounds to integers -- Only commit data when `saveBlockCommit` is called -- Various bad method calls for expected query results -- Sorting of votes during SPV -- Added a missing index for the `block_id` column in the `transactions` table -- Moved the wallets integrity check after the wallet rebuild process to avoid false positive blockchain rebuilds -- Insert bignumber objects as strings to avoid rounding issues caused by `Number.MAX_SAFE_INTEGER` - -## 0.1.0 - 2018-09-11 - -### Added - -- initial release diff --git a/packages/core-database-postgres/LICENSE b/packages/core-database-postgres/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-database-postgres/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-database-postgres/README.md b/packages/core-database-postgres/README.md index 3ef5117887..9a38b12edf 100644 --- a/packages/core-database-postgres/README.md +++ b/packages/core-database-postgres/README.md @@ -14,8 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-database-postgres/__tests__/__support__/setup.ts b/packages/core-database-postgres/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..3300e02f17 --- /dev/null +++ b/packages/core-database-postgres/__tests__/__support__/setup.ts @@ -0,0 +1,36 @@ +import { app } from "@arkecosystem/core-container"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; + +jest.setTimeout(60000); + +const options = { + connection: { + host: "localhost", + port: 5432, + database: "core_development", + user: "core", + password: "password", + }, +}; + +export const setUp = async () => { + await setUpContainer({ + exit: "@arkecosystem/core-database-postgres", + exclude: ["@arkecosystem/core-database-postgres"], + }); + + // register first core-database because core-database-postgres extends it + // (we might improve registerWithContainer to take care of extends) + const { plugin: pluginDatabase } = require("@arkecosystem/core-database"); + await registerWithContainer(pluginDatabase, options); + + const { plugin } = require("../../src/plugin"); + await registerWithContainer(plugin, options); +}; + +export const tearDown = async () => { + await app.tearDown(); + + const { plugin } = require("../../src/plugin"); + await plugin.deregister(app, options); +}; diff --git a/packages/core-database-postgres/__tests__/connection.test.ts b/packages/core-database-postgres/__tests__/connection.test.ts new file mode 100644 index 0000000000..034f28c3dd --- /dev/null +++ b/packages/core-database-postgres/__tests__/connection.test.ts @@ -0,0 +1,40 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; +import genesisBlock from "../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { setUp, tearDown } from "./__support__/setup"; + +const { Block } = models; + +let databaseService: Database.IDatabaseService; + +beforeAll(async () => { + await setUp(); + + databaseService = app.resolvePlugin("database"); + + await databaseService.saveBlock(new Block(genesisBlock)); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("Connection", () => { + describe("verifyBlockchain", () => { + it("should be valid - no errors - when verifying blockchain", async () => { + expect(await databaseService.verifyBlockchain()).toEqual({ + valid: true, + errors: [], + }); + }); + }); + + describe("getLastBlock", () => { + it("should get the genesis block as last block", async () => { + const lastBlock = await databaseService.getLastBlock(); + + expect(lastBlock).toEqual(new Block(genesisBlock as any)); + }); + }); +}); diff --git a/packages/core-database-postgres/jest.config.js b/packages/core-database-postgres/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-database-postgres/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-database-postgres/lib/connection.js b/packages/core-database-postgres/lib/connection.js deleted file mode 100644 index f79ac58548..0000000000 --- a/packages/core-database-postgres/lib/connection.js +++ /dev/null @@ -1,733 +0,0 @@ -/* eslint no-await-in-loop: "off" */ -/* eslint no-use-before-define: "warn" */ -/* eslint max-len: "off" */ - -const pgPromise = require('pg-promise') -const crypto = require('crypto') -const chunk = require('lodash/chunk') -const pluralize = require('pluralize') -const fs = require('fs') -const path = require('path') - -const { ConnectionInterface } = require('@arkecosystem/core-database') - -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const logger = app.resolvePlugin('logger') -const emitter = app.resolvePlugin('event-emitter') - -const { roundCalculator } = require('@arkecosystem/core-utils') - -const { - Bignum, - models: { Block, Transaction }, -} = require('@arkecosystem/crypto') - -const SPV = require('./spv') - -const migrations = require('./migrations') -const QueryExecutor = require('./sql/query-executor') -const repositories = require('./repositories') -const { camelizeColumns } = require('./utils') - -module.exports = class PostgresConnection extends ConnectionInterface { - /** - * Make the database connection instance. - * @return {PostgresConnection} - */ - async make() { - if (this.db) { - throw new Error('Database connection already initialised') - } - - logger.debug('Connecting to database') - - this.queuedQueries = null - this.cache = new Map() - - try { - await this.connect() - await this.__registerQueryExecutor() - await this.__runMigrations() - await this.__registerModels() - await super._registerRepositories() - await super._registerWalletManager() - - this.blocksInCurrentRound = await this.__getBlocksForRound() - - return this - } catch (error) { - app.forceExit('Unable to connect to the database!', error) - } - } - - /** - * Connect to the database. - * @return {void} - */ - async connect() { - const initialization = { - receive(data, result, e) { - camelizeColumns(pgp, data) - }, - extend(object) { - for (const repository of Object.keys(repositories)) { - object[repository] = new repositories[repository](object, pgp) - } - }, - } - - const pgp = pgPromise({ ...this.config.initialization, ...initialization }) - - this.pgp = pgp - this.db = this.pgp(this.config.connection) - } - - /** - * Disconnects from the database and closes the cache. - * @return {Promise} The successfulness of closing the Sequelize connection - */ - async disconnect() { - try { - await this.commitQueuedQueries() - this.cache.clear() - } catch (error) { - logger.warn('Issue in commiting blocks, database might be corrupted') - logger.warn(error.message) - } - - logger.debug('Disconnecting from database') - - return this.pgp.end() - } - - /** - * Verify the blockchain stored on db is not corrupted making simple assertions: - * - Last block is available - * - Last block height equals the number of stored blocks - * - Number of stored transactions equals the sum of block.numberOfTransactions in the database - * - Sum of all tx fees equals the sum of block.totalFee - * - Sum of all tx amount equals the sum of block.totalAmount - * @return {Object} An object { valid, errors } with the result of the verification and the errors - */ - async verifyBlockchain() { - const errors = [] - - const lastBlock = await this.getLastBlock() - - // Last block is available - if (!lastBlock) { - errors.push('Last block is not available') - } else { - const { count: numberOfBlocks } = await this.db.blocks.count() - - // Last block height equals the number of stored blocks - if (lastBlock.data.height !== +numberOfBlocks) { - errors.push( - `Last block height: ${lastBlock.data.height.toLocaleString()}, number of stored blocks: ${numberOfBlocks}`, - ) - } - } - - const blockStats = await this.db.blocks.statistics() - const transactionStats = await this.db.transactions.statistics() - - // Number of stored transactions equals the sum of block.numberOfTransactions in the database - if (blockStats.numberOfTransactions !== transactionStats.count) { - errors.push( - `Number of transactions: ${ - transactionStats.count - }, number of transactions included in blocks: ${ - blockStats.numberOfTransactions - }`, - ) - } - - // Sum of all tx fees equals the sum of block.totalFee - if (blockStats.totalFee !== transactionStats.totalFee) { - errors.push( - `Total transaction fees: ${ - transactionStats.totalFee - }, total of block.totalFee : ${blockStats.totalFee}`, - ) - } - - // Sum of all tx amount equals the sum of block.totalAmount - if (blockStats.totalAmount !== transactionStats.totalAmount) { - errors.push( - `Total transaction amounts: ${ - transactionStats.totalAmount - }, total of block.totalAmount : ${blockStats.totalAmount}`, - ) - } - - return { - valid: !errors.length, - errors, - } - } - - /** - * Get the top 51 delegates. - * @param {Number} height - * @param {Array} delegates - * @return {Array} - */ - async getActiveDelegates(height, delegates) { - const maxDelegates = config.getConstants(height).activeDelegates - const round = Math.floor((height - 1) / maxDelegates) + 1 - - if ( - this.forgingDelegates && - this.forgingDelegates.length && - this.forgingDelegates[0].round === round - ) { - return this.forgingDelegates - } - - // When called during applyRound we already know the delegates, so we don't have to query the database. - if (!delegates || delegates.length === 0) { - delegates = await this.db.rounds.findById(round) - } - - const seedSource = round.toString() - let currentSeed = crypto - .createHash('sha256') - .update(seedSource, 'utf8') - .digest() - - for (let i = 0, delCount = delegates.length; i < delCount; i++) { - for (let x = 0; x < 4 && i < delCount; i++, x++) { - const newIndex = currentSeed[x] % delCount - const b = delegates[newIndex] - delegates[newIndex] = delegates[i] - delegates[i] = b - } - currentSeed = crypto - .createHash('sha256') - .update(currentSeed) - .digest() - } - - this.forgingDelegates = delegates.map(delegate => { - delegate.round = +delegate.round - return delegate - }) - - return this.forgingDelegates - } - - /** - * Store the given round. - * @param {Array} delegates - * @return {Array} - */ - async saveRound(delegates) { - logger.info(`Saving round ${delegates[0].round.toLocaleString()}`) - - await this.db.rounds.create(delegates) - - emitter.emit('round.created', delegates) - } - - /** - * Delete the given round. - * @param {Number} round - * @return {Promise} - */ - async deleteRound(round) { - return this.db.rounds.delete(round) - } - - /** - * Load a list of wallets into memory. - * @param {Number} height - * @return {Boolean} success - */ - async buildWallets(height) { - this.walletManager.reset() - - const spvPath = `${process.env.ARK_PATH_DATA}/spv.json` - - if (fs.existsSync(spvPath)) { - fs.removeSync(spvPath) - - logger.info( - 'Ark Core ended unexpectedly - resuming from where we left off :runner:', - ) - - return true - } - - try { - const spv = new SPV(this) - const success = await spv.build(height) - - this._spvFinished = true - - await this.__registerListeners() - - return success - } catch (error) { - logger.error(error.stack) - } - } - - /** - * Load all wallets from database. - * @return {Array} - */ - async loadWallets() { - const wallets = await this.db.wallets.all() - - this.walletManager.index(wallets) - - return this.walletManager.all() - } - - /** - * Commit wallets from the memory. - * @param {Boolean} force - * @return {void} - */ - async saveWallets(force) { - const wallets = this.walletManager - .allByPublicKey() - .filter(wallet => wallet.publicKey && (force || wallet.dirty)) - - // Remove dirty flags first to not save all dirty wallets in the exit handler - // when called during a force insert right after SPV. - this.walletManager.clear() - - if (force) { - // all wallets to be updated, performance is better without upsert - await this.db.wallets.truncate() - - try { - const chunks = chunk(wallets, 5000).map(c => this.db.wallets.create(c)) - await this.db.tx(t => t.batch(chunks)) - } catch (error) { - logger.error(error.stack) - } - } else { - // NOTE: The list of delegates is calculated in-memory against the WalletManager, - // so it is safe to perform the costly UPSERT non-blocking during round change only: - // 'await saveWallets(false)' -> 'saveWallets(false)' - try { - const queries = wallets.map(wallet => - this.db.wallets.updateOrCreate(wallet), - ) - await this.db.tx(t => t.batch(queries)) - } catch (error) { - logger.error(error.stack) - } - } - - logger.info( - `${wallets.length} modified ${pluralize( - 'wallet', - wallets.length, - )} committed to database`, - ) - - emitter.emit('wallet.saved', wallets.length) - - // NOTE: commented out as more use cases to be taken care of - // this.walletManager.purgeEmptyNonDelegates() - } - - /** - * Commit the given block. - * NOTE: to be used when node is in sync and committing newly received blocks - * @param {Block} block - * @return {void} - */ - async saveBlock(block) { - try { - const queries = [this.db.blocks.create(block.data)] - - if (block.transactions.length > 0) { - queries.push(this.db.transactions.create(block.transactions)) - } - - await this.db.tx(t => t.batch(queries)) - } catch (err) { - logger.error(err.message) - } - } - - /** - * Delete the given block. - * @param {Block} block - * @return {void} - */ - async deleteBlock(block) { - try { - const queries = [ - this.db.transactions.deleteByBlock(block.data.id), - this.db.blocks.delete(block.data.id), - ] - - await this.db.tx(t => t.batch(queries)) - } catch (error) { - logger.error(error.stack) - - throw error - } - } - - /** - * Stores the block in memory. Generated insert statements are stored in this.queuedQueries, to be later saved to the database by calling commit. - * NOTE: to use when rebuilding to decrease the number of database tx, and commit blocks (save only every 1000s for instance) by calling commit. - * @param {Block} block - * @return {void} - */ - enqueueSaveBlock(block) { - const queries = [this.db.blocks.create(block.data)] - - if (block.transactions.length > 0) { - queries.push(this.db.transactions.create(block.transactions)) - } - - this.enqueueQueries(queries) - } - - /** - * Generated delete statements are stored in this.queuedQueries to be later executed by calling this.commitQueuedQueries. - * See also enqueueSaveBlock. - * @param {Block} block - * @return {void} - */ - enqueueDeleteBlock(block) { - const queries = [ - this.db.transactions.deleteByBlock(block.data.id), - this.db.blocks.delete(block.data.id), - ] - - this.enqueueQueries(queries) - } - - /** - * Generated delete statements are stored in this.queuedQueries to be later executed by calling this.commitQueuedQueries. - * @param {Number} round - * @return {void} - */ - enqueueDeleteRound(height) { - const { round, nextRound, maxDelegates } = roundCalculator.calculateRound( - height, - ) - - if (nextRound === round + 1 && height >= maxDelegates) { - this.enqueueQueries([this.db.rounds.delete(nextRound)]) - } - } - - /** - * Add queries to the queue to be executed when calling commit. - * @param {Array} queries - */ - enqueueQueries(queries) { - if (!this.queuedQueries) { - this.queuedQueries = [] - } - - this.queuedQueries.push(...queries) - } - - /** - * Commit all queued queries. - * NOTE: to be used in combination with enqueueSaveBlock and enqueueDeleteBlock. - * @return {void} - */ - async commitQueuedQueries() { - if (!this.queuedQueries || this.queuedQueries.length === 0) { - return - } - - logger.debug('Committing database transactions.') - - try { - await this.db.tx(t => t.batch(this.queuedQueries)) - } catch (error) { - logger.error(error) - - throw error - } finally { - this.queuedQueries = null - } - } - - /** - * Get a block. - * @param {Number} id - * @return {Block} - */ - async getBlock(id) { - // TODO: caching the last 1000 blocks, in combination with `saveBlock` could help to optimise - const block = await this.db.blocks.findById(id) - - if (!block) { - return null - } - - const transactions = await this.db.transactions.findByBlock(block.id) - - block.transactions = transactions.map(({ serialized }) => - Transaction.deserialize(serialized.toString('hex')), - ) - - return new Block(block) - } - - /** - * Get the last block. - * @return {(Block|null)} - */ - async getLastBlock() { - const block = await this.db.blocks.latest() - - if (!block) { - return null - } - - const transactions = await this.db.transactions.latestByBlock(block.id) - - block.transactions = transactions.map(({ serialized }) => - Transaction.deserialize(serialized.toString('hex')), - ) - - return new Block(block) - } - - /** - * Get a transaction. - * @param {Number} id - * @return {Promise} - */ - async getTransaction(id) { - return this.db.transactions.findById(id) - } - - /** - * Get common blocks for the given IDs. - * @param {Array} ids - * @return {Array} - */ - async getCommonBlocks(ids) { - const state = app.resolve('state') - let commonBlocks = state.getCommonBlocks(ids) - if (commonBlocks.length < ids.length) { - commonBlocks = await this.db.blocks.common(ids) - } - - return commonBlocks - } - - /** - * Get transactions for the given IDs. - * @param {Array} ids - * @return {Array} - */ - async getTransactionsFromIds(ids) { - return this.db.transactions.findManyById(ids) - } - - /** - * Get forged transactions for the given IDs. - * @param {Array} ids - * @return {Array} - */ - async getForgedTransactionsIds(ids) { - if (!ids.length) { - return [] - } - - const transactions = await this.db.transactions.forged(ids) - - return transactions.map(transaction => transaction.id) - } - - /** - * Get blocks for the given offset and limit. - * @param {Number} offset - * @param {Number} limit - * @return {Array} - */ - async getBlocks(offset, limit) { - let blocks = [] - - if (app.has('state')) { - blocks = app - .resolve('state') - .getLastBlocksByHeight(offset, offset + limit) - } - - if (blocks.length !== limit) { - blocks = await this.db.blocks.heightRange(offset, offset + limit) - - await this.loadTransactionsForBlocks(blocks) - } - - return blocks - } - - /** - * Get top count blocks ordered by height DESC. - * NOTE: Only used when trying to restore database integrity. The returned blocks may be unchained. - * @param {Number} count - * @return {Array} - */ - async getTopBlocks(count) { - const blocks = await this.db.blocks.top(count) - - await this.loadTransactionsForBlocks(blocks) - - return blocks - } - - /** - * Load all transactions for the given blocks - * @param {Array} blocks - * @return {void} - */ - async loadTransactionsForBlocks(blocks) { - if (!blocks.length) { - return - } - - const ids = blocks.map(block => block.id) - - let transactions = await this.db.transactions.latestByBlocks(ids) - transactions = transactions.map(tx => { - const data = Transaction.deserialize(tx.serialized.toString('hex')) - data.blockId = tx.blockId - return data - }) - - for (const block of blocks) { - if (block.numberOfTransactions > 0) { - block.transactions = transactions.filter( - transaction => transaction.blockId === block.id, - ) - } - } - } - - /** - * Get the 10 recent block ids. - * @return {[]String} - */ - async getRecentBlockIds() { - const state = app.resolve('state') - let blocks = state - .getLastBlockIds() - .reverse() - .slice(0, 10) - - if (blocks.length < 10) { - blocks = await this.db.blocks.recent() - blocks = blocks.map(block => block.id) - } - - return blocks - } - - /** - * Get the headers of blocks for the given offset and limit. - * @param {Number} offset - * @param {Number} limit - * @return {Array} - */ - async getBlockHeaders(offset, limit) { - const blocks = await this.db.blocks.headers(offset, offset + limit) - - return blocks.map(block => Block.serialize(block)) - } - - /** - * Get the cache object - * @return {Cache} - */ - getCache() { - return this.cache - } - - /** - * Run all migrations. - * @return {void} - */ - async __runMigrations() { - for (const migration of migrations) { - const { name } = path.parse(migration.file) - - if (name === '20180304100000-create-migrations-table') { - await this.query.none(migration) - } else { - const row = await this.db.migrations.findByName(name) - - if (row === null) { - logger.debug(`Migrating ${name}`) - - await this.query.none(migration) - - await this.db.migrations.create({ name }) - } - } - } - } - - /** - * Register all models. - * @return {void} - */ - async __registerModels() { - this.models = {} - - for (const [key, Value] of Object.entries(require('./models'))) { - this.models[key.toLowerCase()] = new Value(this.pgp) - } - } - - /** - * Register the query builder. - * @return {void} - */ - __registerQueryExecutor() { - this.query = new QueryExecutor(this) - } - - /** - * Register event listeners. - * @return {void} - */ - __registerListeners() { - super.__registerListeners() - - emitter.on('wallet.created.cold', async coldWallet => { - try { - const wallet = await this.db.wallets.findByAddress(coldWallet.address) - - if (wallet) { - Object.keys(wallet).forEach(key => { - if (['balance'].indexOf(key) !== -1) { - return - } - - coldWallet[key] = - key !== 'voteBalance' ? wallet[key] : new Bignum(wallet[key]) - }) - } - } catch (err) { - logger.error(err) - } - }) - - emitter.once('shutdown', async () => { - if (!this._spvFinished) { - // Prevent dirty wallets to be saved when SPV didn't finish - this.walletManager.clear() - } - }) - } -} diff --git a/packages/core-database-postgres/lib/defaults.js b/packages/core-database-postgres/lib/defaults.js deleted file mode 100644 index b6e0e4b74e..0000000000 --- a/packages/core-database-postgres/lib/defaults.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - initialization: { - capSQL: true, - promiseLib: require('bluebird'), - noLocking: process.env.NODE_ENV === 'test', - }, - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || `ark_${process.env.ARK_NETWORK_NAME}`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, -} diff --git a/packages/core-database-postgres/lib/index.js b/packages/core-database-postgres/lib/index.js deleted file mode 100644 index 6b128c71e4..0000000000 --- a/packages/core-database-postgres/lib/index.js +++ /dev/null @@ -1,33 +0,0 @@ -const PostgresConnection = require('./connection') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'database', - extends: '@arkecosystem/core-database', - async register(container, options) { - container.resolvePlugin('logger').info('Establishing Database Connection') - - const postgres = new PostgresConnection(options) - - const databaseManager = container.resolvePlugin('databaseManager') - await databaseManager.makeConnection(postgres) - - return databaseManager.connection() - }, - async deregister(container, options) { - container.resolvePlugin('logger').info('Closing Database Connection') - - return container.resolvePlugin('database').disconnect() - }, -} - -/** - * The files required to migrate the database. - * @type {Array} - */ -exports.migrations = require('./migrations') diff --git a/packages/core-database-postgres/lib/migrations/index.js b/packages/core-database-postgres/lib/migrations/index.js deleted file mode 100644 index 385f0b9502..0000000000 --- a/packages/core-database-postgres/lib/migrations/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const { loadQueryFile } = require('../utils') - -module.exports = [ - loadQueryFile(__dirname, './20180304100000-create-migrations-table.sql'), - loadQueryFile(__dirname, './20180305100000-create-wallets-table.sql'), - loadQueryFile(__dirname, './20180305200000-create-rounds-table.sql'), - loadQueryFile(__dirname, './20180305300000-create-blocks-table.sql'), - loadQueryFile(__dirname, './20180305400000-create-transactions-table.sql'), - loadQueryFile( - __dirname, - './20181129400000-add-block_id-index-to-transactions-table.sql', - ), - loadQueryFile( - __dirname, - './20181204100000-add-generator_public_key-index-to-blocks-table.sql', - ), - loadQueryFile( - __dirname, - './20181204200000-add-timestamp-index-to-blocks-table.sql', - ), - loadQueryFile( - __dirname, - './20181204300000-add-sender_public_key-index-to-transactions-table.sql', - ), - loadQueryFile( - __dirname, - './20181204400000-add-recipient_id-index-to-transactions-table.sql', - ), -] diff --git a/packages/core-database-postgres/lib/models/block.js b/packages/core-database-postgres/lib/models/block.js deleted file mode 100644 index 979f5ea1ad..0000000000 --- a/packages/core-database-postgres/lib/models/block.js +++ /dev/null @@ -1,72 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') -const Model = require('./model') - -module.exports = class Block extends Model { - /** - * The table associated with the model. - * @return {String} - */ - getTable() { - return 'blocks' - } - - /** - * The read-only structure with query-formatting columns. - * @return {Object} - */ - getColumnSet() { - return this.createColumnSet([ - { - name: 'id', - }, - { - name: 'version', - }, - { - name: 'timestamp', - }, - { - name: 'previous_block', - prop: 'previousBlock', - def: null, - }, - { - name: 'height', - }, - { - name: 'number_of_transactions', - prop: 'numberOfTransactions', - }, - { - name: 'total_amount', - prop: 'totalAmount', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'total_fee', - prop: 'totalFee', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'reward', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'payload_length', - prop: 'payloadLength', - }, - { - name: 'payload_hash', - prop: 'payloadHash', - }, - { - name: 'generator_public_key', - prop: 'generatorPublicKey', - }, - { - name: 'block_signature', - prop: 'blockSignature', - }, - ]) - } -} diff --git a/packages/core-database-postgres/lib/models/index.js b/packages/core-database-postgres/lib/models/index.js deleted file mode 100644 index dfbbdc2b42..0000000000 --- a/packages/core-database-postgres/lib/models/index.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - Block: require('./block'), - Migration: require('./migration'), - Round: require('./round'), - Transaction: require('./transaction'), - Wallet: require('./wallet'), -} diff --git a/packages/core-database-postgres/lib/models/migration.js b/packages/core-database-postgres/lib/models/migration.js deleted file mode 100644 index f5f80cd506..0000000000 --- a/packages/core-database-postgres/lib/models/migration.js +++ /dev/null @@ -1,23 +0,0 @@ -const Model = require('./model') - -module.exports = class Round extends Model { - /** - * The table associated with the model. - * @return {String} - */ - getTable() { - return 'migrations' - } - - /** - * The read-only structure with query-formatting columns. - * @return {Object} - */ - getColumnSet() { - return this.createColumnSet([ - { - name: 'name', - }, - ]) - } -} diff --git a/packages/core-database-postgres/lib/models/model.js b/packages/core-database-postgres/lib/models/model.js deleted file mode 100644 index 718ee30a9d..0000000000 --- a/packages/core-database-postgres/lib/models/model.js +++ /dev/null @@ -1,55 +0,0 @@ -const sql = require('sql') - -module.exports = class Model { - /** - * Create a new model instance. - * @param {Object} pgp - */ - constructor(pgp) { - this.pgp = pgp - } - - /** - * Return the model & table definition. - * @return {Object} - */ - query() { - return sql.define({ - name: this.getTable(), - columns: this.getColumnSet().columns.map(column => ({ - name: column.name, - prop: column.prop || column.name, - })), - }) - } - - /** - * Convert the "camelCase" keys to "snake_case". - * @return {Object} - */ - transform(model) { - const mappings = Object.entries(this.getMappings()) - - const transformed = {} - - for (const [original, mapping] of mappings) { - transformed[mapping] = model[original] - } - - return transformed - } - - /** - * Convert the "camelCase" keys to "snake_case". - * @param {Array} v - * @return {ColumnSet} - */ - createColumnSet(columns) { - return new this.pgp.helpers.ColumnSet(columns, { - table: { - table: this.getTable(), - schema: 'public', - }, - }) - } -} diff --git a/packages/core-database-postgres/lib/models/round.js b/packages/core-database-postgres/lib/models/round.js deleted file mode 100644 index c572f4997f..0000000000 --- a/packages/core-database-postgres/lib/models/round.js +++ /dev/null @@ -1,33 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') -const Model = require('./model') - -module.exports = class Round extends Model { - /** - * The table associated with the model. - * @return {String} - */ - getTable() { - return 'rounds' - } - - /** - * The read-only structure with query-formatting columns. - * @return {Object} - */ - getColumnSet() { - return this.createColumnSet([ - { - name: 'public_key', - prop: 'publicKey', - }, - { - name: 'balance', - prop: 'voteBalance', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'round', - }, - ]) - } -} diff --git a/packages/core-database-postgres/lib/models/transaction.js b/packages/core-database-postgres/lib/models/transaction.js deleted file mode 100644 index 98ab7f004a..0000000000 --- a/packages/core-database-postgres/lib/models/transaction.js +++ /dev/null @@ -1,64 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') -const Model = require('./model') - -module.exports = class Transaction extends Model { - /** - * The table associated with the model. - * @return {String} - */ - getTable() { - return 'transactions' - } - - /** - * The read-only structure with query-formatting columns. - * @return {Object} - */ - getColumnSet() { - return this.createColumnSet([ - { - name: 'id', - }, - { - name: 'version', - }, - { - name: 'block_id', - prop: 'blockId', - }, - { - name: 'sequence', - }, - { - name: 'timestamp', - }, - { - name: 'sender_public_key', - prop: 'senderPublicKey', - }, - { - name: 'recipient_id', - prop: 'recipientId', - }, - { - name: 'type', - }, - { - name: 'vendor_field_hex', - prop: 'vendorFieldHex', - }, - { - name: 'amount', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'fee', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'serialized', - init: col => Buffer.from(col.value, 'hex'), - }, - ]) - } -} diff --git a/packages/core-database-postgres/lib/models/wallet.js b/packages/core-database-postgres/lib/models/wallet.js deleted file mode 100644 index 72bd3faa51..0000000000 --- a/packages/core-database-postgres/lib/models/wallet.js +++ /dev/null @@ -1,55 +0,0 @@ -const { bignumify } = require('@arkecosystem/core-utils') -const Model = require('./model') - -module.exports = class WalletModel extends Model { - /** - * The table associated with the model. - * @return {String} - */ - getTable() { - return 'wallets' - } - - /** - * The read-only structure with query-formatting columns. - * @return {Object} - */ - getColumnSet() { - return this.createColumnSet([ - { - name: 'address', - }, - { - name: 'public_key', - prop: 'publicKey', - }, - { - name: 'second_public_key', - prop: 'secondPublicKey', - }, - { - name: 'vote', - }, - { - name: 'username', - }, - { - name: 'balance', - init: col => bignumify(col.value).toFixed(), - }, - { - name: 'vote_balance', - prop: 'voteBalance', - init: col => (col.value ? bignumify(col.value).toFixed() : null), - }, - { - name: 'produced_blocks', - prop: 'producedBlocks', - }, - { - name: 'missed_blocks', - prop: 'missedBlocks', - }, - ]) - } -} diff --git a/packages/core-database-postgres/lib/queries/index.js b/packages/core-database-postgres/lib/queries/index.js deleted file mode 100644 index 840e20000d..0000000000 --- a/packages/core-database-postgres/lib/queries/index.js +++ /dev/null @@ -1,76 +0,0 @@ -const { loadQueryFile } = require('../utils') - -module.exports = { - blocks: { - common: loadQueryFile(__dirname, './blocks/common.sql'), - count: loadQueryFile(__dirname, './blocks/count.sql'), - delete: loadQueryFile(__dirname, './blocks/delete.sql'), - findById: loadQueryFile(__dirname, './blocks/find-by-id.sql'), - headers: loadQueryFile(__dirname, './blocks/headers.sql'), - heightRange: loadQueryFile(__dirname, './blocks/height-range.sql'), - latest: loadQueryFile(__dirname, './blocks/latest.sql'), - recent: loadQueryFile(__dirname, './blocks/recent.sql'), - statistics: loadQueryFile(__dirname, './blocks/statistics.sql'), - top: loadQueryFile(__dirname, './blocks/top.sql'), - }, - migrations: { - create: loadQueryFile(__dirname, './migrations/create.sql'), - find: loadQueryFile(__dirname, './migrations/find.sql'), - }, - rounds: { - delete: loadQueryFile(__dirname, './rounds/delete.sql'), - find: loadQueryFile(__dirname, './rounds/find.sql'), - }, - spv: { - blockRewards: loadQueryFile(__dirname, './spv/block-rewards.sql'), - delegates: loadQueryFile(__dirname, './spv/delegates.sql'), - delegatesForgedBlocks: loadQueryFile( - __dirname, - './spv/delegates-forged-blocks.sql', - ), - delegatesRanks: loadQueryFile(__dirname, './spv/delegates-ranks.sql'), - lastForgedBlocks: loadQueryFile(__dirname, './spv/last-forged-blocks.sql'), - multiSignatures: loadQueryFile(__dirname, './spv/multi-signatures.sql'), - receivedTransactions: loadQueryFile( - __dirname, - './spv/received-transactions.sql', - ), - secondSignatures: loadQueryFile(__dirname, './spv/second-signatures.sql'), - sentTransactions: loadQueryFile(__dirname, './spv/sent-transactions.sql'), - votes: loadQueryFile(__dirname, './spv/votes.sql'), - }, - transactions: { - findByBlock: loadQueryFile(__dirname, './transactions/find-by-block.sql'), - latestByBlock: loadQueryFile( - __dirname, - './transactions/latest-by-block.sql', - ), - latestByBlocks: loadQueryFile( - __dirname, - './transactions/latest-by-blocks.sql', - ), - statistics: loadQueryFile(__dirname, './transactions/statistics.sql'), - forged: loadQueryFile(__dirname, './transactions/forged.sql'), - findById: loadQueryFile(__dirname, './transactions/find-by-id.sql'), - findManyById: loadQueryFile( - __dirname, - './transactions/find-many-by-id.sql', - ), - deleteByBlock: loadQueryFile( - __dirname, - './transactions/delete-by-block.sql', - ), - }, - wallets: { - all: loadQueryFile(__dirname, './wallets/all.sql'), - findByAddress: loadQueryFile(__dirname, './wallets/find-by-address.sql'), - findNegativeBalances: loadQueryFile( - __dirname, - './wallets/find-negative-balances.sql', - ), - findNegativeVoteBalances: loadQueryFile( - __dirname, - './wallets/find-negative-vote-balances.sql', - ), - }, -} diff --git a/packages/core-database-postgres/lib/queries/spv/last-forged-blocks.sql b/packages/core-database-postgres/lib/queries/spv/last-forged-blocks.sql deleted file mode 100644 index b2fe36352b..0000000000 --- a/packages/core-database-postgres/lib/queries/spv/last-forged-blocks.sql +++ /dev/null @@ -1,5 +0,0 @@ -SELECT id, - generator_public_key, - TIMESTAMP -FROM blocks -ORDER BY TIMESTAMP DESC LIMIT ${limit} diff --git a/packages/core-database-postgres/lib/queries/transactions/find-many-by-id.sql b/packages/core-database-postgres/lib/queries/transactions/find-many-by-id.sql deleted file mode 100644 index 5b20ec290a..0000000000 --- a/packages/core-database-postgres/lib/queries/transactions/find-many-by-id.sql +++ /dev/null @@ -1,4 +0,0 @@ -SELECT serialized, - block_id -FROM transactions -WHERE id IN (${ids:list}) diff --git a/packages/core-database-postgres/lib/repositories/blocks.js b/packages/core-database-postgres/lib/repositories/blocks.js deleted file mode 100644 index 61569cfd73..0000000000 --- a/packages/core-database-postgres/lib/repositories/blocks.js +++ /dev/null @@ -1,100 +0,0 @@ -const Repository = require('./repository') -const { Block } = require('../models') -const { blocks: sql } = require('../queries') - -module.exports = class BlocksRepository extends Repository { - /** - * Find a block by its ID. - * @param {Number} id - * @return {Promise} - */ - async findById(id) { - return this.db.one(sql.findById, { id }) - } - - /** - * Count the number of records in the database. - * @return {Promise} - */ - async count() { - return this.db.one(sql.count) - } - - /** - * Get all of the common blocks from the database. - * @param {Array} ids - * @return {Promise} - */ - async common(ids) { - return this.db.manyOrNone(sql.common, { ids }) - } - - /** - * Get all of the blocks within the given height range. - * @param {Number} start - * @param {Number} end - * @return {Promise} - */ - async headers(start, end) { - return this.db.many(sql.headers, { start, end }) - } - - /** - * Get all of the blocks within the given height range and order them by height. - * @param {Number} start - * @param {Number} end - * @return {Promise} - */ - async heightRange(start, end) { - return this.db.manyOrNone(sql.heightRange, { start, end }) - } - - /** - * Get the last created block from the database. - * @return {Promise} - */ - async latest() { - return this.db.oneOrNone(sql.latest) - } - - /** - * Get the 10 most recently created blocks from the database. - * @return {Promise} - */ - async recent() { - return this.db.many(sql.recent) - } - - /** - * Get statistics about all blocks from the database. - * @return {Promise} - */ - async statistics() { - return this.db.one(sql.statistics) - } - - /** - * Get top count blocks - * @return {Promise} - */ - async top(count) { - return this.db.many(sql.top, { top: count }) - } - - /** - * Delete the block from the database. - * @param {Number} id - * @return {Promise} - */ - async delete(id) { - return this.db.none(sql.delete, { id }) - } - - /** - * Get the model related to this repository. - * @return {Object} - */ - getModel() { - return new Block(this.pgp) - } -} diff --git a/packages/core-database-postgres/lib/repositories/index.js b/packages/core-database-postgres/lib/repositories/index.js deleted file mode 100644 index 59947e709d..0000000000 --- a/packages/core-database-postgres/lib/repositories/index.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - blocks: require('./blocks'), - migrations: require('./migrations'), - rounds: require('./rounds'), - transactions: require('./transactions'), - wallets: require('./wallets'), -} diff --git a/packages/core-database-postgres/lib/repositories/migrations.js b/packages/core-database-postgres/lib/repositories/migrations.js deleted file mode 100644 index a72275c6a7..0000000000 --- a/packages/core-database-postgres/lib/repositories/migrations.js +++ /dev/null @@ -1,22 +0,0 @@ -const Repository = require('./repository') -const { Migration } = require('../models') -const { migrations: sql } = require('../queries') - -module.exports = class MigrationsRepository extends Repository { - /** - * Find a migration by its name. - * @param {String} name - * @return {Promise} - */ - async findByName(name) { - return this.db.oneOrNone(sql.find, { name }) - } - - /** - * Get the model related to this repository. - * @return {Object} - */ - getModel() { - return new Migration(this.pgp) - } -} diff --git a/packages/core-database-postgres/lib/repositories/repository.js b/packages/core-database-postgres/lib/repositories/repository.js deleted file mode 100644 index e34f7f7ea0..0000000000 --- a/packages/core-database-postgres/lib/repositories/repository.js +++ /dev/null @@ -1,74 +0,0 @@ -module.exports = class Repository { - /** - * Create a new repository instance. - * @param {Object} db - * @param {Object} pgp - */ - constructor(db, pgp) { - this.db = db - this.pgp = pgp - this.model = this.getModel() - } - - /** - * Estimate the number of records in the table. - * @return {Promise} - */ - async estimate() { - return this.db.one( - `SELECT count_estimate('SELECT * FROM ${this.model.getTable()})`, - ) - } - - /** - * Run a truncate statement on the table. - * @return {Promise} - */ - async truncate() { - return this.db.none(`TRUNCATE ${this.model.getTable()} RESTART IDENTITY`) - } - - /** - * Create one or many instances of the related models. - * @param {Array|Object} item - * @return {Promise} - */ - async create(item) { - return this.db.none(this.__insertQuery(item)) - } - - /** - * Update one or many instances of the related models. - * @param {Array|Object} item - * @return {Promise} - */ - async update(item) { - return this.db.none(this.__updateQuery(item)) - } - - /** - * Get the model related to this repository. - * @return {Object} - */ - getModel() { - throw new Error('Method [getModel] not implemented!') - } - - /** - * Generate an "INSERT" query for the given data. - * @param {Array|Object} data - * @return {String} - */ - __insertQuery(data) { - return this.pgp.helpers.insert(data, this.model.getColumnSet()) - } - - /** - * Generate an "UPDATE" query for the given data. - * @param {Array|Object} data - * @return {String} - */ - __updateQuery(data) { - return this.pgp.helpers.update(data, this.model.getColumnSet()) - } -} diff --git a/packages/core-database-postgres/lib/repositories/rounds.js b/packages/core-database-postgres/lib/repositories/rounds.js deleted file mode 100644 index ea55b11f76..0000000000 --- a/packages/core-database-postgres/lib/repositories/rounds.js +++ /dev/null @@ -1,31 +0,0 @@ -const Repository = require('./repository') -const { Round } = require('../models') -const { rounds: sql } = require('../queries') - -module.exports = class RoundsRepository extends Repository { - /** - * Find a round by its ID. - * @param {Number} round - * @return {Promise} - */ - async findById(round) { - return this.db.manyOrNone(sql.find, { round }) - } - - /** - * Delete the round from the database. - * @param {Number} round - * @return {Promise} - */ - async delete(round) { - return this.db.none(sql.delete, { round }) - } - - /** - * Get the model related to this repository. - * @return {Object} - */ - getModel() { - return new Round(this.pgp) - } -} diff --git a/packages/core-database-postgres/lib/repositories/transactions.js b/packages/core-database-postgres/lib/repositories/transactions.js deleted file mode 100644 index 66cf3158c5..0000000000 --- a/packages/core-database-postgres/lib/repositories/transactions.js +++ /dev/null @@ -1,84 +0,0 @@ -const Repository = require('./repository') -const { Transaction } = require('../models') -const { transactions: sql } = require('../queries') - -module.exports = class TransactionsRepository extends Repository { - /** - * Find a transactions by its ID. - * @param {String} id - * @return {Promise} - */ - async findById(id) { - return this.db.oneOrNone(sql.findById, { id }) - } - - /** - * Find multiple transactionss by their IDs. - * @param {Array} ids - * @return {Promise} - */ - async findManyById(ids) { - return this.db.manyOrNone(sql.findManyById, { ids }) - } - - /** - * Find multiple transactionss by their block ID. - * @param {String} id - * @return {Promise} - */ - async findByBlock(id) { - return this.db.manyOrNone(sql.findByBlock, { id }) - } - - /** - * Find multiple transactionss by their block ID and order them by sequence. - * @param {Number} id - * @return {Promise} - */ - async latestByBlock(id) { - return this.db.manyOrNone(sql.latestByBlock, { id }) - } - - /** - * Find multiple transactionss by their block IDs and order them by sequence. - * @param {Array} ids - * @return {Promise} - */ - async latestByBlocks(ids) { - return this.db.manyOrNone(sql.latestByBlocks, { ids }) - } - - /** - * Get all of the forged transactions from the database. - * @param {Array} ids - * @return {Promise} - */ - async forged(ids) { - return this.db.manyOrNone(sql.forged, { ids }) - } - - /** - * Get statistics about all transactions from the database. - * @return {Promise} - */ - async statistics() { - return this.db.one(sql.statistics) - } - - /** - * Delete the transactions from the database. - * @param {Number} id - * @return {Promise} - */ - async deleteByBlock(id) { - return this.db.none(sql.deleteByBlock, { id }) - } - - /** - * Get the model related to this repository. - * @return {Object} - */ - getModel() { - return new Transaction(this.pgp) - } -} diff --git a/packages/core-database-postgres/lib/repositories/wallets.js b/packages/core-database-postgres/lib/repositories/wallets.js deleted file mode 100644 index 95a01041eb..0000000000 --- a/packages/core-database-postgres/lib/repositories/wallets.js +++ /dev/null @@ -1,62 +0,0 @@ -const Repository = require('./repository') -const { Wallet } = require('../models') -const { wallets: sql } = require('../queries') - -module.exports = class WalletsRepository extends Repository { - /** - * Get all of the wallets from the database. - * @return {Promise} - */ - async all() { - return this.db.manyOrNone(sql.all) - } - - /** - * Find a wallet by its address. - * @param {String} address - * @return {Promise} - */ - async findByAddress(address) { - return this.db.oneOrNone(sql.findByAddress, { address }) - } - - /** - * Get the count of wallets that have a negative balance. - * @return {Promise} - */ - async findNegativeBalances() { - return this.db.oneOrNone(sql.findNegativeBalances) - } - - /** - * Get the count of wallets that have a negative vote balance. - * @return {Promise} - */ - async findNegativeVoteBalances() { - return this.db.oneOrNone(sql.findNegativeVoteBalances) - } - - /** - * Create or update a record matching the attributes, and fill it with values. - * @param {Object} wallet - * @return {Promise} - */ - async updateOrCreate(wallet) { - const query = `${this.__insertQuery( - wallet, - )} ON CONFLICT(address) DO UPDATE SET ${this.pgp.helpers.sets( - wallet, - this.model.getColumnSet(), - )}` - - return this.db.none(query) - } - - /** - * Get the model related to this repository. - * @return {Object} - */ - getModel() { - return new Wallet(this.pgp) - } -} diff --git a/packages/core-database-postgres/lib/spv.js b/packages/core-database-postgres/lib/spv.js deleted file mode 100644 index 3266252c45..0000000000 --- a/packages/core-database-postgres/lib/spv.js +++ /dev/null @@ -1,325 +0,0 @@ -const { - Bignum, - models: { Transaction }, -} = require('@arkecosystem/crypto') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const config = app.resolvePlugin('config') -const queries = require('./queries') - -const genesisWallets = config.genesisBlock.transactions.map(tx => tx.senderId) - -module.exports = class SPV { - /** - * Create a new wallet builder instance. - * @param {SequelizeConnection} database - * @return {void} - */ - constructor(database) { - this.connection = database.connection - this.models = database.models - this.walletManager = database.walletManager - this.query = database.query - } - - /** - * Perform the SPV (Simple Payment Verification). - * @param {Number} height - * @return {void} - */ - async build(height) { - this.activeDelegates = config.getConstants(height).activeDelegates - - logger.printTracker('SPV', 1, 8, 'Received Transactions') - await this.__buildReceivedTransactions() - - logger.printTracker('SPV', 2, 8, 'Block Rewards') - await this.__buildBlockRewards() - - logger.printTracker('SPV', 3, 8, 'Last Forged Blocks') - await this.__buildLastForgedBlocks() - - logger.printTracker('SPV', 4, 8, 'Sent Transactions') - await this.__buildSentTransactions() - - logger.printTracker('SPV', 5, 8, 'Second Signatures') - await this.__buildSecondSignatures() - - logger.printTracker('SPV', 6, 8, 'Votes') - await this.__buildVotes() - - logger.printTracker('SPV', 7, 8, 'Delegates') - await this.__buildDelegates() - - logger.printTracker('SPV', 8, 8, 'MultiSignatures') - await this.__buildMultisignatures() - - logger.stopTracker('SPV', 8, 8) - logger.info( - `SPV rebuild finished, wallets in memory: ${ - Object.keys(this.walletManager.byAddress).length - }`, - ) - logger.info( - `Number of registered delegates: ${ - Object.keys(this.walletManager.byUsername).length - }`, - ) - - return this.__verifyWalletsConsistency() - } - - /** - * Load and apply received transactions to wallets. - * @return {void} - */ - async __buildReceivedTransactions() { - const transactions = await this.query.many(queries.spv.receivedTransactions) - - for (const transaction of transactions) { - const wallet = this.walletManager.findByAddress(transaction.recipientId) - - wallet - ? (wallet.balance = new Bignum(transaction.amount)) - : logger.warn( - `Lost cold wallet: ${transaction.recipientId} ${ - transaction.amount - }`, - ) - } - } - - /** - * Load and apply block rewards to wallets. - * @return {void} - */ - async __buildBlockRewards() { - const blocks = await this.query.many(queries.spv.blockRewards) - - for (const block of blocks) { - const wallet = this.walletManager.findByPublicKey( - block.generatorPublicKey, - ) - wallet.balance = wallet.balance.plus(block.reward) - } - } - - /** - * Load and apply last forged blocks to wallets. - * @return {void} - */ - async __buildLastForgedBlocks() { - const blocks = await this.query.many(queries.spv.lastForgedBlocks, { - limit: this.activeDelegates, - }) - - for (const block of blocks) { - const wallet = this.walletManager.findByPublicKey( - block.generatorPublicKey, - ) - wallet.lastBlock = block - } - } - - /** - * Load and apply sent transactions to wallets. - * @return {void} - */ - async __buildSentTransactions() { - const transactions = await this.query.many(queries.spv.sentTransactions) - - for (const transaction of transactions) { - const wallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - wallet.balance = wallet.balance - .minus(transaction.amount) - .minus(transaction.fee) - - if (wallet.balance.isLessThan(0) && !this.isGenesis(wallet)) { - logger.warn(`Negative balance: ${wallet}`) - } - } - } - - /** - * Used to determine if a wallet is a Genesis wallet. - * @return {Boolean} - */ - isGenesis(wallet) { - return genesisWallets.includes(wallet.address) - } - - /** - * Load and apply second signature transactions to wallets. - * @return {void} - */ - async __buildSecondSignatures() { - const transactions = await this.query.manyOrNone( - queries.spv.secondSignatures, - ) - - for (const transaction of transactions) { - const wallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - wallet.secondPublicKey = Transaction.deserialize( - transaction.serialized.toString('hex'), - ).asset.signature.publicKey - } - } - - /** - * Load and apply votes to wallets. - * @return {void} - */ - async __buildVotes() { - const transactions = await this.query.manyOrNone(queries.spv.votes) - - for (const transaction of transactions) { - const wallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - - if (!wallet.voted) { - const vote = Transaction.deserialize( - transaction.serialized.toString('hex'), - ).asset.votes[0] - - if (vote.startsWith('+')) { - wallet.vote = vote.slice(1) - } - - // NOTE: The "voted" property is only used within this loop to avoid an issue - // that results in not properly applying "unvote" transactions as the "vote" property - // would be empty in that case and return a false result. - wallet.voted = true - } - } - - this.walletManager.buildVoteBalances() - } - - /** - * Load and apply delegate usernames to wallets. - * @return {void} - */ - async __buildDelegates() { - // Register... - const transactions = await this.query.manyOrNone(queries.spv.delegates) - - transactions.forEach(transaction => { - const wallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - wallet.username = Transaction.deserialize( - transaction.serialized.toString('hex'), - ).asset.delegate.username - this.walletManager.reindex(wallet) - }) - - // Forged Blocks... - const forgedBlocks = await this.query.manyOrNone( - queries.spv.delegatesForgedBlocks, - ) - forgedBlocks.forEach(block => { - const wallet = this.walletManager.findByPublicKey( - block.generatorPublicKey, - ) - wallet.forgedFees = wallet.forgedFees.plus(block.totalFees) - wallet.forgedRewards = wallet.forgedRewards.plus(block.totalRewards) - wallet.producedBlocks = +block.totalProduced - }) - - // NOTE: This is highly NOT reliable, however the number of missed blocks - // is NOT used for the consensus - const delegates = await this.query.manyOrNone(queries.spv.delegatesRanks) - delegates.forEach((delegate, i) => { - const wallet = this.walletManager.findByPublicKey(delegate.publicKey) - wallet.missedBlocks = parseInt(delegate.missedBlocks) - wallet.rate = i + 1 - this.walletManager.reindex(wallet) - }) - } - - /** - * Load and apply multisignatures to wallets. - * @return {void} - */ - async __buildMultisignatures() { - const transactions = await this.query.manyOrNone( - queries.spv.multiSignatures, - ) - - for (const transaction of transactions) { - const wallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - - if (!wallet.multisignature) { - wallet.multisignature = Transaction.deserialize( - transaction.serialized.toString('hex'), - ).asset.multisignature - } - } - } - - /** - * Verify the consistency of the wallets table by comparing all records against - * the in memory wallets. - * NOTE: This is faster than rebuilding the entire table from scratch each time. - * @returns {Boolean} - */ - async __verifyWalletsConsistency() { - const dbWallets = await this.query.manyOrNone(queries.wallets.all) - const inMemoryWallets = this.walletManager.allByPublicKey() - - let detectedInconsistency = false - if (dbWallets.length !== inMemoryWallets.length) { - detectedInconsistency = true - } else { - for (const dbWallet of dbWallets) { - if (dbWallet.balance < 0 && !this.isGenesis(dbWallet)) { - detectedInconsistency = true - logger.warn( - `Wallet '${dbWallet.address}' has a negative balance of '${ - dbWallet.balance - }'`, - ) - break - } - - if (dbWallet.voteBalance < 0) { - detectedInconsistency = true - logger.warn( - `Wallet ${dbWallet.address} has a negative vote balance of '${ - dbWallet.voteBalance - }'`, - ) - break - } - - const inMemoryWallet = this.walletManager.findByPublicKey( - dbWallet.publicKey, - ) - - if ( - !inMemoryWallet.balance.isEqualTo(dbWallet.balance) || - !inMemoryWallet.voteBalance.isEqualTo(dbWallet.voteBalance) || - dbWallet.username !== inMemoryWallet.username - ) { - detectedInconsistency = true - break - } - } - } - - // Remove dirty flags when no inconsistency has been found - if (!detectedInconsistency) { - this.walletManager.clear() - } - - return !detectedInconsistency - } -} diff --git a/packages/core-database-postgres/lib/sql/query-executor.js b/packages/core-database-postgres/lib/sql/query-executor.js deleted file mode 100644 index 4a1d3b58a0..0000000000 --- a/packages/core-database-postgres/lib/sql/query-executor.js +++ /dev/null @@ -1,81 +0,0 @@ -module.exports = class QueryExecutor { - /** - * Create a new QueryExecutor instance. - * @param {[type]} connection - * @return {QueryBuilder} - */ - constructor(connection) { - this.connection = connection - } - - /** - * Execute the given query and expect no results. - * @param {QueryFile} query - * @param {Array} parameters - * @return {Promise} - */ - async none(query, parameters) { - return this.__executeQueryFile(query, parameters, 'none') - } - - /** - * Execute the given query and expect one result. - * @param {QueryFile} query - * @param {Array} parameters - * @return {Promise} - */ - async one(query, parameters) { - return this.__executeQueryFile(query, parameters, 'one') - } - - /** - * Execute the given query and expect one or no results. - * @param {QueryFile} query - * @param {Array} parameters - * @return {Promise} - */ - async oneOrNone(query, parameters) { - return this.__executeQueryFile(query, parameters, 'oneOrNone') - } - - /** - * Execute the given query and expect many results. - * @param {QueryFile} query - * @param {Array} parameters - * @return {Promise} - */ - async many(query, parameters) { - return this.__executeQueryFile(query, parameters, 'many') - } - - /** - * Execute the given query and expect many or no results. - * @param {QueryFile} query - * @param {Array} parameters - * @return {Promise} - */ - async manyOrNone(query, parameters) { - return this.__executeQueryFile(query, parameters, 'manyOrNone') - } - - /** - * Execute the given query and expect any results. - * @param {QueryFile} query - * @param {Array} parameters - * @return {Promise} - */ - async any(query, parameters) { - return this.__executeQueryFile(query, parameters, 'any') - } - - /** - * Execute the given query using the given method and parameters. - * @param {QueryFile} query - * @param {Array} parameters - * @param {String} method - * @return {QueryBuilder} - */ - async __executeQueryFile(query, parameters, method) { - return this.connection.db[method](query, parameters) - } -} diff --git a/packages/core-database-postgres/lib/utils/camelize-columns.js b/packages/core-database-postgres/lib/utils/camelize-columns.js deleted file mode 100644 index d0721f31d5..0000000000 --- a/packages/core-database-postgres/lib/utils/camelize-columns.js +++ /dev/null @@ -1,17 +0,0 @@ -/* eslint guard-for-in: "off" */ - -module.exports = (pgp, data) => { - const tmp = data[0] - - for (const prop in tmp) { - const camel = pgp.utils.camelize(prop) - - if (!(camel in tmp)) { - for (let i = 0; i < data.length; i++) { - const d = data[i] - d[camel] = d[prop] - delete d[prop] - } - } - } -} diff --git a/packages/core-database-postgres/lib/utils/index.js b/packages/core-database-postgres/lib/utils/index.js deleted file mode 100644 index a80bd79d5b..0000000000 --- a/packages/core-database-postgres/lib/utils/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - camelizeColumns: require('./camelize-columns'), - loadQueryFile: require('./load-query-file'), -} diff --git a/packages/core-database-postgres/lib/utils/load-query-file.js b/packages/core-database-postgres/lib/utils/load-query-file.js deleted file mode 100644 index 20302d61aa..0000000000 --- a/packages/core-database-postgres/lib/utils/load-query-file.js +++ /dev/null @@ -1,25 +0,0 @@ -const QueryFile = require('pg-promise').QueryFile -const path = require('path') - -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') - -module.exports = (directory, file) => { - const fullPath = path.join(directory, file) - - const options = { - minify: true, - params: { - schema: 'public', - }, - } - - const query = new QueryFile(fullPath, options) - - if (query.error) { - logger.error(query.error) - } - - return query -} diff --git a/packages/core-database-postgres/package.json b/packages/core-database-postgres/package.json index 97642fcc10..e3da866f71 100644 --- a/packages/core-database-postgres/package.json +++ b/packages/core-database-postgres/package.json @@ -1,35 +1,56 @@ { - "name": "@arkecosystem/core-database-postgres", - "description": "PostgreSQL integration for Ark Core", - "version": "0.2.1", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-database": "~0.2", - "@arkecosystem/core-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "bluebird": "^3.5.3", - "lodash.chunk": "^4.2.0", - "pg-promise": "^8.5.2", - "pluralize": "^7.0.0", - "sql": "^0.78.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-database-postgres", + "description": "PostgreSQL integration for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn copy && yarn compile", + "build:watch": "yarn clean && yarn copy && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "copy": "cd src/ && cpy './**/*.sql' --parents ../dist/ && cd ../", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-database": "^2.1.0", + "@arkecosystem/core-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/bluebird": "^3.5.25", + "@types/lodash.chunk": "^4.2.4", + "@types/pluralize": "^0.0.29", + "bluebird": "^3.5.3", + "cpy-cli": "^2.0.0", + "lodash.chunk": "^4.2.0", + "pg-promise": "^8.5.4", + "pluralize": "^7.0.0", + "sql": "^0.78.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-database-postgres/src/defaults.ts b/packages/core-database-postgres/src/defaults.ts new file mode 100644 index 0000000000..566937c84c --- /dev/null +++ b/packages/core-database-postgres/src/defaults.ts @@ -0,0 +1,14 @@ +export const defaults = { + initialization: { + capSQL: true, + promiseLib: require("bluebird"), + noLocking: process.env.NODE_ENV === "test", + }, + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_${process.env.CORE_NETWORK_NAME}`, + user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + password: process.env.CORE_DB_PASSWORD || "password", + }, +}; diff --git a/packages/core-database-postgres/src/index.ts b/packages/core-database-postgres/src/index.ts new file mode 100644 index 0000000000..b4d0f3e18c --- /dev/null +++ b/packages/core-database-postgres/src/index.ts @@ -0,0 +1,6 @@ +export * from "./postgres-connection"; +export * from "./migrations"; +export * from "./spv"; +export * from "./models"; +export * from "./repositories"; +export * from "./plugin"; diff --git a/packages/core-database-postgres/lib/migrations/20180304100000-create-migrations-table.sql b/packages/core-database-postgres/src/migrations/20180304100000-create-migrations-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20180304100000-create-migrations-table.sql rename to packages/core-database-postgres/src/migrations/20180304100000-create-migrations-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20180305100000-create-wallets-table.sql b/packages/core-database-postgres/src/migrations/20180305100000-create-wallets-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20180305100000-create-wallets-table.sql rename to packages/core-database-postgres/src/migrations/20180305100000-create-wallets-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20180305200000-create-rounds-table.sql b/packages/core-database-postgres/src/migrations/20180305200000-create-rounds-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20180305200000-create-rounds-table.sql rename to packages/core-database-postgres/src/migrations/20180305200000-create-rounds-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20180305300000-create-blocks-table.sql b/packages/core-database-postgres/src/migrations/20180305300000-create-blocks-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20180305300000-create-blocks-table.sql rename to packages/core-database-postgres/src/migrations/20180305300000-create-blocks-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20180305400000-create-transactions-table.sql b/packages/core-database-postgres/src/migrations/20180305400000-create-transactions-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20180305400000-create-transactions-table.sql rename to packages/core-database-postgres/src/migrations/20180305400000-create-transactions-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20181129400000-add-block_id-index-to-transactions-table.sql b/packages/core-database-postgres/src/migrations/20181129400000-add-block_id-index-to-transactions-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20181129400000-add-block_id-index-to-transactions-table.sql rename to packages/core-database-postgres/src/migrations/20181129400000-add-block_id-index-to-transactions-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20181204100000-add-generator_public_key-index-to-blocks-table.sql b/packages/core-database-postgres/src/migrations/20181204100000-add-generator_public_key-index-to-blocks-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20181204100000-add-generator_public_key-index-to-blocks-table.sql rename to packages/core-database-postgres/src/migrations/20181204100000-add-generator_public_key-index-to-blocks-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20181204200000-add-timestamp-index-to-blocks-table.sql b/packages/core-database-postgres/src/migrations/20181204200000-add-timestamp-index-to-blocks-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20181204200000-add-timestamp-index-to-blocks-table.sql rename to packages/core-database-postgres/src/migrations/20181204200000-add-timestamp-index-to-blocks-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20181204300000-add-sender_public_key-index-to-transactions-table.sql b/packages/core-database-postgres/src/migrations/20181204300000-add-sender_public_key-index-to-transactions-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20181204300000-add-sender_public_key-index-to-transactions-table.sql rename to packages/core-database-postgres/src/migrations/20181204300000-add-sender_public_key-index-to-transactions-table.sql diff --git a/packages/core-database-postgres/lib/migrations/20181204400000-add-recipient_id-index-to-transactions-table.sql b/packages/core-database-postgres/src/migrations/20181204400000-add-recipient_id-index-to-transactions-table.sql similarity index 100% rename from packages/core-database-postgres/lib/migrations/20181204400000-add-recipient_id-index-to-transactions-table.sql rename to packages/core-database-postgres/src/migrations/20181204400000-add-recipient_id-index-to-transactions-table.sql diff --git a/packages/core-database-postgres/src/migrations/index.ts b/packages/core-database-postgres/src/migrations/index.ts new file mode 100644 index 0000000000..d0e51a118e --- /dev/null +++ b/packages/core-database-postgres/src/migrations/index.ts @@ -0,0 +1,14 @@ +import { loadQueryFile } from "../utils"; + +export const migrations = [ + loadQueryFile(__dirname, "./20180304100000-create-migrations-table.sql"), + loadQueryFile(__dirname, "./20180305100000-create-wallets-table.sql"), + loadQueryFile(__dirname, "./20180305200000-create-rounds-table.sql"), + loadQueryFile(__dirname, "./20180305300000-create-blocks-table.sql"), + loadQueryFile(__dirname, "./20180305400000-create-transactions-table.sql"), + loadQueryFile(__dirname, "./20181129400000-add-block_id-index-to-transactions-table.sql"), + loadQueryFile(__dirname, "./20181204100000-add-generator_public_key-index-to-blocks-table.sql"), + loadQueryFile(__dirname, "./20181204200000-add-timestamp-index-to-blocks-table.sql"), + loadQueryFile(__dirname, "./20181204300000-add-sender_public_key-index-to-transactions-table.sql"), + loadQueryFile(__dirname, "./20181204400000-add-recipient_id-index-to-transactions-table.sql"), +]; diff --git a/packages/core-database-postgres/src/models/block.ts b/packages/core-database-postgres/src/models/block.ts new file mode 100644 index 0000000000..f537ab8b4e --- /dev/null +++ b/packages/core-database-postgres/src/models/block.ts @@ -0,0 +1,72 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import { Model } from "./model"; + +export class Block extends Model { + /** + * The table associated with the model. + * @return {String} + */ + public getTable() { + return "blocks"; + } + + /** + * The read-only structure with query-formatting columns. + * @return {Object} + */ + public getColumnSet() { + return this.createColumnSet([ + { + name: "id", + }, + { + name: "version", + }, + { + name: "timestamp", + }, + { + name: "previous_block", + prop: "previousBlock", + def: null, + }, + { + name: "height", + }, + { + name: "number_of_transactions", + prop: "numberOfTransactions", + }, + { + name: "total_amount", + prop: "totalAmount", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "total_fee", + prop: "totalFee", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "reward", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "payload_length", + prop: "payloadLength", + }, + { + name: "payload_hash", + prop: "payloadHash", + }, + { + name: "generator_public_key", + prop: "generatorPublicKey", + }, + { + name: "block_signature", + prop: "blockSignature", + }, + ]); + } +} diff --git a/packages/core-database-postgres/src/models/index.ts b/packages/core-database-postgres/src/models/index.ts new file mode 100644 index 0000000000..5fdb934052 --- /dev/null +++ b/packages/core-database-postgres/src/models/index.ts @@ -0,0 +1,6 @@ +export * from "./model"; +export * from "./block"; +export * from "./migration"; +export * from "./round"; +export * from "./transaction"; +export * from "./wallet"; diff --git a/packages/core-database-postgres/src/models/migration.ts b/packages/core-database-postgres/src/models/migration.ts new file mode 100644 index 0000000000..cf56eb259c --- /dev/null +++ b/packages/core-database-postgres/src/models/migration.ts @@ -0,0 +1,23 @@ +import { Model } from "./model"; + +export class Migration extends Model { + /** + * The table associated with the model. + * @return {String} + */ + public getTable() { + return "migrations"; + } + + /** + * The read-only structure with query-formatting columns. + * @return {Object} + */ + public getColumnSet() { + return this.createColumnSet([ + { + name: "name", + }, + ]); + } +} diff --git a/packages/core-database-postgres/src/models/model.ts b/packages/core-database-postgres/src/models/model.ts new file mode 100644 index 0000000000..6d401f9dca --- /dev/null +++ b/packages/core-database-postgres/src/models/model.ts @@ -0,0 +1,51 @@ +import sql from "sql"; + +export abstract class Model { + /** + * Create a new model instance. + * @param {Object} pgp + */ + constructor(public pgp) {} + + /** + * Get table name for model. + * @return {String} + */ + public abstract getTable(): string; + + /** + * Get table column names for model. + * @return {String[]} + */ + public abstract getColumnSet(): any; + + /** + * Return the model & table definition. + * @return {Object} + */ + public query(): any { + const { schema, columns } = this.getColumnSet(); + return sql.define({ + name: this.getTable(), + schema, + columns: columns.map(column => ({ + name: column.name, + prop: column.prop || column.name, + })), + }); + } + + /** + * Convert the "camelCase" keys to "snake_case". + * @return {ColumnSet} + * @param columns + */ + public createColumnSet(columns) { + return new this.pgp.helpers.ColumnSet(columns, { + table: { + table: this.getTable(), + schema: "public", + }, + }); + } +} diff --git a/packages/core-database-postgres/src/models/round.ts b/packages/core-database-postgres/src/models/round.ts new file mode 100644 index 0000000000..2ccd7a87bd --- /dev/null +++ b/packages/core-database-postgres/src/models/round.ts @@ -0,0 +1,33 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import { Model } from "./model"; + +export class Round extends Model { + /** + * The table associated with the model. + * @return {String} + */ + public getTable() { + return "rounds"; + } + + /** + * The read-only structure with query-formatting columns. + * @return {Object} + */ + public getColumnSet() { + return this.createColumnSet([ + { + name: "public_key", + prop: "publicKey", + }, + { + name: "balance", + prop: "voteBalance", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "round", + }, + ]); + } +} diff --git a/packages/core-database-postgres/src/models/transaction.ts b/packages/core-database-postgres/src/models/transaction.ts new file mode 100644 index 0000000000..e79870cfd4 --- /dev/null +++ b/packages/core-database-postgres/src/models/transaction.ts @@ -0,0 +1,64 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import { Model } from "./model"; + +export class Transaction extends Model { + /** + * The table associated with the model. + * @return {String} + */ + public getTable() { + return "transactions"; + } + + /** + * The read-only structure with query-formatting columns. + * @return {Object} + */ + public getColumnSet() { + return this.createColumnSet([ + { + name: "id", + }, + { + name: "version", + }, + { + name: "block_id", + prop: "blockId", + }, + { + name: "sequence", + }, + { + name: "timestamp", + }, + { + name: "sender_public_key", + prop: "senderPublicKey", + }, + { + name: "recipient_id", + prop: "recipientId", + }, + { + name: "type", + }, + { + name: "vendor_field_hex", + prop: "vendorFieldHex", + }, + { + name: "amount", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "fee", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "serialized", + init: col => Buffer.from(col.value, "hex"), + }, + ]); + } +} diff --git a/packages/core-database-postgres/src/models/wallet.ts b/packages/core-database-postgres/src/models/wallet.ts new file mode 100644 index 0000000000..52c9c043dd --- /dev/null +++ b/packages/core-database-postgres/src/models/wallet.ts @@ -0,0 +1,55 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import { Model } from "./model"; + +export class Wallet extends Model { + /** + * The table associated with the model. + * @return {String} + */ + public getTable() { + return "wallets"; + } + + /** + * The read-only structure with query-formatting columns. + * @return {Object} + */ + public getColumnSet() { + return this.createColumnSet([ + { + name: "address", + }, + { + name: "public_key", + prop: "publicKey", + }, + { + name: "second_public_key", + prop: "secondPublicKey", + }, + { + name: "vote", + }, + { + name: "username", + }, + { + name: "balance", + init: col => bignumify(col.value).toFixed(), + }, + { + name: "vote_balance", + prop: "voteBalance", + init: col => (col.value ? bignumify(col.value).toFixed() : null), + }, + { + name: "produced_blocks", + prop: "producedBlocks", + }, + { + name: "missed_blocks", + prop: "missedBlocks", + }, + ]); + } +} diff --git a/packages/core-database-postgres/src/plugin.ts b/packages/core-database-postgres/src/plugin.ts new file mode 100644 index 0000000000..2e1c39fd57 --- /dev/null +++ b/packages/core-database-postgres/src/plugin.ts @@ -0,0 +1,29 @@ +import { + DatabaseManager, databaseServiceFactory, WalletManager} from "@arkecosystem/core-database"; +import { Container, Database, Logger } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { PostgresConnection } from "./postgres-connection"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "database", + extends: "@arkecosystem/core-database", + async register(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("Establishing Database Connection"); + + const walletManager = new WalletManager(); + + const databaseManager = container.resolvePlugin("databaseManager"); + + const connection = await databaseManager.makeConnection(new PostgresConnection(options, walletManager)); + + return await databaseServiceFactory(options, walletManager, connection); + }, + async deregister(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("Closing Database Connection"); + + const databaseService = container.resolvePlugin("database"); + await databaseService.connection.disconnect(); + }, +}; diff --git a/packages/core-database-postgres/src/postgres-connection.ts b/packages/core-database-postgres/src/postgres-connection.ts new file mode 100644 index 0000000000..442a19eb98 --- /dev/null +++ b/packages/core-database-postgres/src/postgres-connection.ts @@ -0,0 +1,275 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import { roundCalculator } from "@arkecosystem/core-utils"; +import { models } from "@arkecosystem/crypto"; +import fs from "fs"; +import chunk from "lodash/chunk"; +import path from "path"; +import pgPromise from "pg-promise"; +import { migrations } from "./migrations"; +import { Model } from "./models"; +import { repositories } from "./repositories"; +import { MigrationsRepository } from "./repositories/migrations"; +import { SPV } from "./spv"; +import { QueryExecutor } from "./sql/query-executor"; +import { camelizeColumns } from "./utils"; + +export class PostgresConnection implements Database.IDatabaseConnection { + + public logger = app.resolvePlugin("logger"); + public models: { [key: string]: Model } = {}; + public query: QueryExecutor; + public db: any; + public blocksRepository: Database.IBlocksRepository; + public roundsRepository: Database.IRoundsRepository; + public transactionsRepository: Database.ITransactionsRepository; + public walletsRepository: Database.IWalletsRepository; + public pgp: any; + private emitter = app.resolvePlugin("event-emitter"); + private migrationsRepository : MigrationsRepository; + private cache: Map; + private queuedQueries: any[]; + + + public constructor(readonly options: any, private walletManager: Database.IWalletManager) { + + } + + + public async buildWallets(height: number) { + const spvPath = `${process.env.CORE_PATH_CACHE}/spv.json`; + + if (fs.existsSync(spvPath)) { + (fs as any).removeSync(spvPath); + + this.logger.info("Ark Core ended unexpectedly - resuming from where we left off :runner:"); + + return true; + } + + try { + const spv = new SPV(this.query, this.walletManager); + return await spv.build(height); + } catch (error) { + this.logger.error(error.stack); + } + + return false; + } + + public async commitQueuedQueries() { + if (!this.queuedQueries || this.queuedQueries.length === 0) { + return; + } + + this.logger.debug("Committing database transactions."); + + try { + await this.db.tx(t => t.batch(this.queuedQueries)); + } catch (error) { + this.logger.error(error); + + throw error; + } finally { + this.queuedQueries = null; + } + } + + public async connect() { + + this.emitter.emit(Database.DatabaseEvents.PRE_CONNECT); + const initialization = { + receive(data, result, e) { + camelizeColumns(pgp, data); + }, + extend(object) { + for (const repository of Object.keys(repositories)) { + object[repository] = new repositories[repository](object, pgp); + } + }, + }; + + const pgp = pgPromise({ ...this.options.initialization, ...initialization }); + + this.pgp = pgp; + this.db = this.pgp(this.options.connection); + } + + public async deleteBlock(block: models.Block) { + try { + const queries = [this.transactionsRepository.deleteByBlockId(block.data.id), this.blocksRepository.delete(block.data.id)]; + + await this.db.tx(t => t.batch(queries)); + } catch (error) { + this.logger.error(error.stack); + + throw error; + } + } + + public async disconnect() { + this.logger.debug("Disconnecting from database"); + this.emitter.emit(Database.DatabaseEvents.PRE_DISCONNECT); + + try { + await this.commitQueuedQueries(); + this.cache.clear(); + } catch (error) { + this.logger.warn("Issue in commiting blocks, database might be corrupted"); + this.logger.warn(error.message); + } + + await this.pgp.end(); + this.emitter.emit(Database.DatabaseEvents.POST_DISCONNECT); + this.logger.debug("Disconnected from database"); + } + + public enqueueDeleteBlock(block: models.Block): any { + const queries = [this.transactionsRepository.deleteByBlockId(block.data.id), this.blocksRepository.delete(block.data.id)]; + + this.enqueueQueries(queries); + } + + public enqueueDeleteRound(height: number): any { + const { round, nextRound, maxDelegates } = roundCalculator.calculateRound(height); + + if (nextRound === round + 1 && height >= maxDelegates) { + this.enqueueQueries([this.roundsRepository.delete(nextRound)]); + } + } + + public enqueueSaveBlock(block: models.Block): any { + const queries = [this.blocksRepository.insert(block.data)]; + + if (block.transactions.length > 0) { + queries.push(this.transactionsRepository.insert(block.transactions)); + } + + this.enqueueQueries(queries); + } + + public async make(): Promise { + if (this.db) { + throw new Error("Database connection already initialised"); + } + + this.logger.debug("Connecting to database"); + + this.queuedQueries = null; + this.cache = new Map(); + + try { + await this.connect(); + this.exposeRepositories(); + await this.registerQueryExecutor(); + await this.runMigrations(); + await this.registerModels(); + this.logger.debug("Connected to database."); + this.emitter.emit(Database.DatabaseEvents.POST_CONNECT); + + return this; + } catch (error) { + app.forceExit("Unable to connect to the database!", error); + } + + return null; + } + + public async saveBlock(block: models.Block) { + try { + const queries = [this.blocksRepository.insert(block.data)]; + + if (block.transactions.length > 0) { + queries.push(this.transactionsRepository.insert(block.transactions)); + } + + await this.db.tx(t => t.batch(queries)); + } catch (err) { + this.logger.error(err.message); + } + } + + public async saveWallets(wallets: any[], force?: boolean) { + if (force) { + // all wallets to be updated, performance is better without upsert + await this.walletsRepository.truncate(); + + try { + const chunks = chunk(wallets, 5000).map(c => this.walletsRepository.insert(c)); // this 5000 figure should be configurable... + await this.db.tx(t => t.batch(chunks)); + } catch (error) { + this.logger.error(error.stack); + } + } else { + // NOTE: The list of delegates is calculated in-memory against the WalletManager, + // so it is safe to perform the costly UPSERT non-blocking during round change only: + // 'await saveWallets(false)' -> 'saveWallets(false)' + try { + const queries = wallets.map(wallet => this.walletsRepository.updateOrCreate(wallet)); + await this.db.tx(t => t.batch(queries)); + } catch (error) { + this.logger.error(error.stack); + } + } + } + + /** + * Run all migrations. + * @return {void} + */ + + private async runMigrations() { + for (const migration of migrations) { + const { name } = path.parse(migration.file); + + if (name === "20180304100000-create-migrations-table") { + await this.query.none(migration); + } else { + const row = await this.migrationsRepository.findByName(name); + + if (row === null) { + this.logger.debug(`Migrating ${name}`); + + await this.query.none(migration); + + await this.migrationsRepository.insert({ name }); + } + } + } + } + + + /** + * Register all models. + * @return {void} + */ + private async registerModels() { + for (const [key, Value] of Object.entries(require("./models"))) { + this.models[key.toLowerCase()] = new (Value as any)(this.pgp); + } + } + + /** + * Register the query builder. + * @return {void} + */ + private registerQueryExecutor() { + this.query = new QueryExecutor(this); + } + + private enqueueQueries(queries) { + if (!this.queuedQueries) { + this.queuedQueries = []; + } + + (this.queuedQueries as any).push(...queries); + } + + private exposeRepositories() { + this.blocksRepository = this.db.blocks; + this.transactionsRepository = this.db.transactions; + this.roundsRepository = this.db.rounds; + this.walletsRepository = this.db.wallets; + this.migrationsRepository = this.db.migrations; + } +} diff --git a/packages/core-database-postgres/lib/queries/blocks/common.sql b/packages/core-database-postgres/src/queries/blocks/common.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/common.sql rename to packages/core-database-postgres/src/queries/blocks/common.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/count.sql b/packages/core-database-postgres/src/queries/blocks/count.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/count.sql rename to packages/core-database-postgres/src/queries/blocks/count.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/delete.sql b/packages/core-database-postgres/src/queries/blocks/delete.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/delete.sql rename to packages/core-database-postgres/src/queries/blocks/delete.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/find-by-id.sql b/packages/core-database-postgres/src/queries/blocks/find-by-id.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/find-by-id.sql rename to packages/core-database-postgres/src/queries/blocks/find-by-id.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/headers.sql b/packages/core-database-postgres/src/queries/blocks/headers.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/headers.sql rename to packages/core-database-postgres/src/queries/blocks/headers.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/height-range.sql b/packages/core-database-postgres/src/queries/blocks/height-range.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/height-range.sql rename to packages/core-database-postgres/src/queries/blocks/height-range.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/latest.sql b/packages/core-database-postgres/src/queries/blocks/latest.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/latest.sql rename to packages/core-database-postgres/src/queries/blocks/latest.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/recent.sql b/packages/core-database-postgres/src/queries/blocks/recent.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/recent.sql rename to packages/core-database-postgres/src/queries/blocks/recent.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/statistics.sql b/packages/core-database-postgres/src/queries/blocks/statistics.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/statistics.sql rename to packages/core-database-postgres/src/queries/blocks/statistics.sql diff --git a/packages/core-database-postgres/lib/queries/blocks/top.sql b/packages/core-database-postgres/src/queries/blocks/top.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/blocks/top.sql rename to packages/core-database-postgres/src/queries/blocks/top.sql diff --git a/packages/core-database-postgres/src/queries/index.ts b/packages/core-database-postgres/src/queries/index.ts new file mode 100644 index 0000000000..9490800c32 --- /dev/null +++ b/packages/core-database-postgres/src/queries/index.ts @@ -0,0 +1,51 @@ +import { loadQueryFile } from "../utils"; + +export const queries = { + blocks: { + common: loadQueryFile(__dirname, "./blocks/common.sql"), + count: loadQueryFile(__dirname, "./blocks/count.sql"), + delete: loadQueryFile(__dirname, "./blocks/delete.sql"), + findById: loadQueryFile(__dirname, "./blocks/find-by-id.sql"), + headers: loadQueryFile(__dirname, "./blocks/headers.sql"), + heightRange: loadQueryFile(__dirname, "./blocks/height-range.sql"), + latest: loadQueryFile(__dirname, "./blocks/latest.sql"), + recent: loadQueryFile(__dirname, "./blocks/recent.sql"), + statistics: loadQueryFile(__dirname, "./blocks/statistics.sql"), + top: loadQueryFile(__dirname, "./blocks/top.sql"), + }, + migrations: { + create: loadQueryFile(__dirname, "./migrations/create.sql"), + find: loadQueryFile(__dirname, "./migrations/find.sql"), + }, + rounds: { + delete: loadQueryFile(__dirname, "./rounds/delete.sql"), + find: loadQueryFile(__dirname, "./rounds/find.sql"), + }, + spv: { + blockRewards: loadQueryFile(__dirname, "./spv/block-rewards.sql"), + delegates: loadQueryFile(__dirname, "./spv/delegates.sql"), + delegatesForgedBlocks: loadQueryFile(__dirname, "./spv/delegates-forged-blocks.sql"), + delegatesRanks: loadQueryFile(__dirname, "./spv/delegates-ranks.sql"), + lastForgedBlocks: loadQueryFile(__dirname, "./spv/last-forged-blocks.sql"), + multiSignatures: loadQueryFile(__dirname, "./spv/multi-signatures.sql"), + receivedTransactions: loadQueryFile(__dirname, "./spv/received-transactions.sql"), + secondSignatures: loadQueryFile(__dirname, "./spv/second-signatures.sql"), + sentTransactions: loadQueryFile(__dirname, "./spv/sent-transactions.sql"), + votes: loadQueryFile(__dirname, "./spv/votes.sql"), + }, + transactions: { + findByBlock: loadQueryFile(__dirname, "./transactions/find-by-block.sql"), + latestByBlock: loadQueryFile(__dirname, "./transactions/latest-by-block.sql"), + latestByBlocks: loadQueryFile(__dirname, "./transactions/latest-by-blocks.sql"), + statistics: loadQueryFile(__dirname, "./transactions/statistics.sql"), + forged: loadQueryFile(__dirname, "./transactions/forged.sql"), + findById: loadQueryFile(__dirname, "./transactions/find-by-id.sql"), + deleteByBlock: loadQueryFile(__dirname, "./transactions/delete-by-block.sql"), + }, + wallets: { + all: loadQueryFile(__dirname, "./wallets/all.sql"), + findByAddress: loadQueryFile(__dirname, "./wallets/find-by-address.sql"), + findNegativeBalances: loadQueryFile(__dirname, "./wallets/find-negative-balances.sql"), + findNegativeVoteBalances: loadQueryFile(__dirname, "./wallets/find-negative-vote-balances.sql"), + }, +}; diff --git a/packages/core-database-postgres/lib/queries/migrations/create.sql b/packages/core-database-postgres/src/queries/migrations/create.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/migrations/create.sql rename to packages/core-database-postgres/src/queries/migrations/create.sql diff --git a/packages/core-database-postgres/lib/queries/migrations/find.sql b/packages/core-database-postgres/src/queries/migrations/find.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/migrations/find.sql rename to packages/core-database-postgres/src/queries/migrations/find.sql diff --git a/packages/core-database-postgres/lib/queries/rounds/delete.sql b/packages/core-database-postgres/src/queries/rounds/delete.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/rounds/delete.sql rename to packages/core-database-postgres/src/queries/rounds/delete.sql diff --git a/packages/core-database-postgres/lib/queries/rounds/find.sql b/packages/core-database-postgres/src/queries/rounds/find.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/rounds/find.sql rename to packages/core-database-postgres/src/queries/rounds/find.sql diff --git a/packages/core-database-postgres/lib/queries/spv/block-rewards.sql b/packages/core-database-postgres/src/queries/spv/block-rewards.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/block-rewards.sql rename to packages/core-database-postgres/src/queries/spv/block-rewards.sql diff --git a/packages/core-database-postgres/lib/queries/spv/delegates-forged-blocks.sql b/packages/core-database-postgres/src/queries/spv/delegates-forged-blocks.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/delegates-forged-blocks.sql rename to packages/core-database-postgres/src/queries/spv/delegates-forged-blocks.sql diff --git a/packages/core-database-postgres/lib/queries/spv/delegates-ranks.sql b/packages/core-database-postgres/src/queries/spv/delegates-ranks.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/delegates-ranks.sql rename to packages/core-database-postgres/src/queries/spv/delegates-ranks.sql diff --git a/packages/core-database-postgres/lib/queries/spv/delegates.sql b/packages/core-database-postgres/src/queries/spv/delegates.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/delegates.sql rename to packages/core-database-postgres/src/queries/spv/delegates.sql diff --git a/packages/core-database-postgres/src/queries/spv/last-forged-blocks.sql b/packages/core-database-postgres/src/queries/spv/last-forged-blocks.sql new file mode 100644 index 0000000000..c05ec7452c --- /dev/null +++ b/packages/core-database-postgres/src/queries/spv/last-forged-blocks.sql @@ -0,0 +1,11 @@ +SELECT id, + height, + generator_public_key, + TIMESTAMP +FROM blocks +WHERE height IN ( + SELECT MAX(height) AS last_block_height + FROM blocks + GROUP BY generator_public_key +) +ORDER BY TIMESTAMP DESC diff --git a/packages/core-database-postgres/lib/queries/spv/multi-signatures.sql b/packages/core-database-postgres/src/queries/spv/multi-signatures.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/multi-signatures.sql rename to packages/core-database-postgres/src/queries/spv/multi-signatures.sql diff --git a/packages/core-database-postgres/lib/queries/spv/received-transactions.sql b/packages/core-database-postgres/src/queries/spv/received-transactions.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/received-transactions.sql rename to packages/core-database-postgres/src/queries/spv/received-transactions.sql diff --git a/packages/core-database-postgres/lib/queries/spv/second-signatures.sql b/packages/core-database-postgres/src/queries/spv/second-signatures.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/second-signatures.sql rename to packages/core-database-postgres/src/queries/spv/second-signatures.sql diff --git a/packages/core-database-postgres/lib/queries/spv/sent-transactions.sql b/packages/core-database-postgres/src/queries/spv/sent-transactions.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/sent-transactions.sql rename to packages/core-database-postgres/src/queries/spv/sent-transactions.sql diff --git a/packages/core-database-postgres/lib/queries/spv/votes.sql b/packages/core-database-postgres/src/queries/spv/votes.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/spv/votes.sql rename to packages/core-database-postgres/src/queries/spv/votes.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/delete-by-block.sql b/packages/core-database-postgres/src/queries/transactions/delete-by-block.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/delete-by-block.sql rename to packages/core-database-postgres/src/queries/transactions/delete-by-block.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/find-by-block.sql b/packages/core-database-postgres/src/queries/transactions/find-by-block.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/find-by-block.sql rename to packages/core-database-postgres/src/queries/transactions/find-by-block.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/find-by-id.sql b/packages/core-database-postgres/src/queries/transactions/find-by-id.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/find-by-id.sql rename to packages/core-database-postgres/src/queries/transactions/find-by-id.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/forged.sql b/packages/core-database-postgres/src/queries/transactions/forged.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/forged.sql rename to packages/core-database-postgres/src/queries/transactions/forged.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/latest-by-block.sql b/packages/core-database-postgres/src/queries/transactions/latest-by-block.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/latest-by-block.sql rename to packages/core-database-postgres/src/queries/transactions/latest-by-block.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/latest-by-blocks.sql b/packages/core-database-postgres/src/queries/transactions/latest-by-blocks.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/latest-by-blocks.sql rename to packages/core-database-postgres/src/queries/transactions/latest-by-blocks.sql diff --git a/packages/core-database-postgres/lib/queries/transactions/statistics.sql b/packages/core-database-postgres/src/queries/transactions/statistics.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/transactions/statistics.sql rename to packages/core-database-postgres/src/queries/transactions/statistics.sql diff --git a/packages/core-database-postgres/lib/queries/wallets/all.sql b/packages/core-database-postgres/src/queries/wallets/all.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/wallets/all.sql rename to packages/core-database-postgres/src/queries/wallets/all.sql diff --git a/packages/core-database-postgres/lib/queries/wallets/find-by-address.sql b/packages/core-database-postgres/src/queries/wallets/find-by-address.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/wallets/find-by-address.sql rename to packages/core-database-postgres/src/queries/wallets/find-by-address.sql diff --git a/packages/core-database-postgres/lib/queries/wallets/find-negative-balances.sql b/packages/core-database-postgres/src/queries/wallets/find-negative-balances.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/wallets/find-negative-balances.sql rename to packages/core-database-postgres/src/queries/wallets/find-negative-balances.sql diff --git a/packages/core-database-postgres/lib/queries/wallets/find-negative-vote-balances.sql b/packages/core-database-postgres/src/queries/wallets/find-negative-vote-balances.sql similarity index 100% rename from packages/core-database-postgres/lib/queries/wallets/find-negative-vote-balances.sql rename to packages/core-database-postgres/src/queries/wallets/find-negative-vote-balances.sql diff --git a/packages/core-database-postgres/src/repositories/blocks.ts b/packages/core-database-postgres/src/repositories/blocks.ts new file mode 100644 index 0000000000..a2e77a3c98 --- /dev/null +++ b/packages/core-database-postgres/src/repositories/blocks.ts @@ -0,0 +1,104 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { Block } from "../models"; +import { queries } from "../queries"; +import { Repository } from "./repository"; + +const { blocks: sql } = queries; + +export class BlocksRepository extends Repository implements Database.IBlocksRepository { + /** + * Find a block by its ID. + * @param {Number} id + * @return {Promise} + */ + public async findById(id) { + return this.db.oneOrNone(sql.findById, { id }); + } + + /** + * Count the number of records in the database. + * @return {Promise} + */ + public async count() { + const { count } = await this.db.one(sql.count); + return count ; + } + + /** + * Get all of the common blocks from the database. + * @param {Array} ids + * @return {Promise} + */ + public async common(ids) { + return this.db.manyOrNone(sql.common, { ids }); + } + + /** + * Get all of the blocks within the given height range. + * @param {Number} start + * @param {Number} end + * @return {Promise} + */ + public async headers(start, end) { + return this.db.many(sql.headers, { start, end }); + } + + /** + * Get all of the blocks within the given height range and order them by height. + * @param {Number} start + * @param {Number} end + * @return {Promise} + */ + public async heightRange(start, end) { + return this.db.manyOrNone(sql.heightRange, { start, end }); + } + + /** + * Get the last created block from the database. + * @return {Promise} + */ + public async latest() { + return this.db.oneOrNone(sql.latest); + } + + /** + * Get the 10 most recently created blocks from the database. + * @return {Promise} + */ + public async recent() { + return this.db.many(sql.recent); + } + + /** + * Get statistics about all blocks from the database. + * @return {Promise} + */ + public async statistics() { + return this.db.one(sql.statistics); + } + + /** + * Get top count blocks + * @return {Promise} + */ + public async top(count) { + return this.db.many(sql.top, { top: count }); + } + + /** + * Delete the block from the database. + * @param {Number} id + * @return {Promise} + */ + public async delete(id) { + return this.db.none(sql.delete, { id }); + } + + /** + * Get the model related to this repository. + * @return {Block} + */ + public getModel() { + return new Block(this.pgp); + } +} diff --git a/packages/core-database-postgres/src/repositories/index.ts b/packages/core-database-postgres/src/repositories/index.ts new file mode 100644 index 0000000000..5dae791f43 --- /dev/null +++ b/packages/core-database-postgres/src/repositories/index.ts @@ -0,0 +1,13 @@ +import { BlocksRepository } from "./blocks"; +import { MigrationsRepository } from "./migrations"; +import { RoundsRepository } from "./rounds"; +import { TransactionsRepository } from "./transactions"; +import { WalletsRepository } from "./wallets"; + +export const repositories = { + blocks: BlocksRepository, + migrations: MigrationsRepository, + rounds: RoundsRepository, + transactions: TransactionsRepository, + wallets: WalletsRepository, +}; diff --git a/packages/core-database-postgres/src/repositories/migrations.ts b/packages/core-database-postgres/src/repositories/migrations.ts new file mode 100644 index 0000000000..e50de6418e --- /dev/null +++ b/packages/core-database-postgres/src/repositories/migrations.ts @@ -0,0 +1,24 @@ +import { Migration } from "../models"; +import { queries } from "../queries"; +import { Repository } from "./repository"; + +const { migrations: sql } = queries; + +export class MigrationsRepository extends Repository { + /** + * Find a migration by its name. + * @param {String} name + * @return {Promise} + */ + public async findByName(name) { + return this.db.oneOrNone(sql.find, { name }); + } + + /** + * Get the model related to this repository. + * @return {Migration} + */ + public getModel() { + return new Migration(this.pgp); + } +} diff --git a/packages/core-database-postgres/src/repositories/repository.ts b/packages/core-database-postgres/src/repositories/repository.ts new file mode 100644 index 0000000000..63de711e58 --- /dev/null +++ b/packages/core-database-postgres/src/repositories/repository.ts @@ -0,0 +1,73 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { Model } from "../models"; + +export abstract class Repository implements Database.IRepository { + protected model: Model; + + /** + * Create a new repository instance. + * @param {Object} db + * @param {Object} pgp + */ + constructor(public db, public pgp) { + this.model = this.getModel(); + } + + /** + * Get the model related to this repository. + * @return {Model} + */ + public abstract getModel(): Model; + + /** + * Estimate the number of records in the table. + * @return {Promise} + */ + public async estimate() { + return this.db.one(`SELECT count_estimate('SELECT * FROM ${this.model.getTable()})`); + } + + /** + * Run a truncate statement on the table. + * @return {Promise} + */ + public async truncate() { + return this.db.none(`TRUNCATE ${this.model.getTable()} RESTART IDENTITY`); + } + + /** + * Create one or many instances of the related models. + * @param {Array|Object} items + * @return {Promise} + */ + public async insert(items) { + return this.db.none(this.__insertQuery(items)); + } + + /** + * Update one or many instances of the related models. + * @param {Array|Object} items + * @return {Promise} + */ + public async update(items) { + return this.db.none(this.__updateQuery(items)); + } + + /** + * Generate an "INSERT" query for the given data. + * @param {Array|Object} data + * @return {String} + */ + public __insertQuery(data) { + return this.pgp.helpers.insert(data, this.model.getColumnSet()); + } + + /** + * Generate an "UPDATE" query for the given data. + * @param {Array|Object} data + * @return {String} + */ + public __updateQuery(data) { + return this.pgp.helpers.update(data, this.model.getColumnSet()); + } +} diff --git a/packages/core-database-postgres/src/repositories/rounds.ts b/packages/core-database-postgres/src/repositories/rounds.ts new file mode 100644 index 0000000000..06602185de --- /dev/null +++ b/packages/core-database-postgres/src/repositories/rounds.ts @@ -0,0 +1,34 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { Round } from "../models"; +import { queries } from "../queries"; +import { Repository } from "./repository"; + +const { rounds: sql } = queries; + +export class RoundsRepository extends Repository implements Database.IRoundsRepository { + /** + * Find a round by its ID. + * @param {Number} round + * @return {Promise} + */ + public async findById(round) { + return this.db.manyOrNone(sql.find, { round }); + } + + /** + * Delete the round from the database. + * @param {Number} round + * @return {Promise} + */ + public async delete(round) { + return this.db.none(sql.delete, { round }); + } + + /** + * Get the model related to this repository. + * @return {Round} + */ + public getModel() { + return new Round(this.pgp); + } +} diff --git a/packages/core-database-postgres/src/repositories/transactions.ts b/packages/core-database-postgres/src/repositories/transactions.ts new file mode 100644 index 0000000000..843f71b241 --- /dev/null +++ b/packages/core-database-postgres/src/repositories/transactions.ts @@ -0,0 +1,78 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { Transaction } from "../models"; +import { queries } from "../queries"; +import { Repository } from "./repository"; + +const { transactions: sql } = queries; + +export class TransactionsRepository extends Repository implements Database.ITransactionsRepository { + /** + * Find a transactions by its ID. + * @param {String} id + * @return {Promise} + */ + public async findById(id) { + return this.db.oneOrNone(sql.findById, { id }); + } + + /** + * Find multiple transactionss by their block ID. + * @param {String} id + * @return {Promise} + */ + public async findByBlockId(id) { + return this.db.manyOrNone(sql.findByBlock, { id }); + } + + /** + * Find multiple transactionss by their block ID and order them by sequence. + * @param {Number} id + * @return {Promise} + */ + public async latestByBlock(id) { + return this.db.manyOrNone(sql.latestByBlock, { id }); + } + + /** + * Find multiple transactionss by their block IDs and order them by sequence. + * @param {Array} ids + * @return {Promise} + */ + public async latestByBlocks(ids) { + return this.db.manyOrNone(sql.latestByBlocks, { ids }); + } + + /** + * Get all of the forged transactions from the database. + * @param {Array} ids + * @return {Promise} + */ + public async forged(ids) { + return this.db.manyOrNone(sql.forged, { ids }); + } + + /** + * Get statistics about all transactions from the database. + * @return {Promise} + */ + public async statistics() { + return this.db.one(sql.statistics); + } + + /** + * Delete the transactions from the database. + * @param {Number} id + * @return {Promise} + */ + public async deleteByBlockId(id) { + return this.db.none(sql.deleteByBlock, { id }); + } + + /** + * Get the model related to this repository. + * @return {Transaction} + */ + public getModel() { + return new Transaction(this.pgp); + } +} diff --git a/packages/core-database-postgres/src/repositories/wallets.ts b/packages/core-database-postgres/src/repositories/wallets.ts new file mode 100644 index 0000000000..a95d4eba19 --- /dev/null +++ b/packages/core-database-postgres/src/repositories/wallets.ts @@ -0,0 +1,63 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { Wallet } from "../models"; +import { queries } from "../queries"; +import { Repository } from "./repository"; + +const { wallets: sql } = queries; + +export class WalletsRepository extends Repository implements Database.IWalletsRepository { + /** + * Get all of the wallets from the database. + * @return {Promise} + */ + public async all() { + return this.db.manyOrNone(sql.all); + } + + /** + * Find a wallet by its address. + * @param {String} address + * @return {Promise} + */ + public async findByAddress(address) { + return this.db.oneOrNone(sql.findByAddress, { address }); + } + + /** + * Get the count of wallets that have a negative balance. + * @return {Promise} + */ + public async tallyWithNegativeBalance() { + return this.db.oneOrNone(sql.findNegativeBalances); + } + + /** + * Get the count of wallets that have a negative vote balance. + * @return {Promise} + */ + public async tallyWithNegativeVoteBalance() { + return this.db.oneOrNone(sql.findNegativeVoteBalances); + } + + /** + * Create or update a record matching the attributes, and fill it with values. + * @param {Object} wallet + * @return {Promise} + */ + public async updateOrCreate(wallet) { + const query = `${this.__insertQuery(wallet)} ON CONFLICT(address) DO UPDATE SET ${this.pgp.helpers.sets( + wallet, + this.model.getColumnSet(), + )}`; + + return this.db.none(query); + } + + /** + * Get the model related to this repository. + * @return {Object} + */ + public getModel() { + return new Wallet(this.pgp); + } +} diff --git a/packages/core-database-postgres/src/spv.ts b/packages/core-database-postgres/src/spv.ts new file mode 100644 index 0000000000..fdb1998618 --- /dev/null +++ b/packages/core-database-postgres/src/spv.ts @@ -0,0 +1,263 @@ +import { Bignum, models } from "@arkecosystem/crypto"; +const { Transaction } = models; + +import { app } from "@arkecosystem/core-container"; +import { Database, Logger } from "@arkecosystem/core-interfaces"; +import { queries } from "./queries"; +import { QueryExecutor } from "./sql/query-executor"; + +const logger = app.resolvePlugin("logger"); +const config = app.getConfig(); + +const genesisWallets = config.get("genesisBlock.transactions").map(tx => tx.senderId); + +export class SPV { + constructor(private query: QueryExecutor, private walletManager: Database.IWalletManager) {} + + /** + * Perform the SPV (Simple Payment Verification). + * @param {Number} height + * @return {void} + */ + public async build(height) { + + logger.info("SPV Step 1 of 8: Received Transactions"); + await this.__buildReceivedTransactions(); + + logger.info("SPV Step 2 of 8: Block Rewards"); + await this.__buildBlockRewards(); + + logger.info("SPV Step 3 of 8: Last Forged Blocks"); + await this.__buildLastForgedBlocks(); + + logger.info("SPV Step 4 of 8: Sent Transactions"); + await this.__buildSentTransactions(); + + logger.info("SPV Step 5 of 8: Second Signatures"); + await this.__buildSecondSignatures(); + + logger.info("SPV Step 6 of 8: Votes"); + await this.__buildVotes(); + + logger.info("SPV Step 7 of 8: Delegates"); + await this.__buildDelegates(); + + logger.info("SPV Step 8 of 8: MultiSignatures"); + await this.__buildMultisignatures(); + + logger.info(`SPV rebuild finished, wallets in memory: ${Object.keys(this.walletManager.allByAddress()).length}`); + logger.info(`Number of registered delegates: ${Object.keys(this.walletManager.allByUsername()).length}`); + + return this.__verifyWalletsConsistency(); + } + + /** + * Load and apply received transactions to wallets. + * @return {void} + */ + public async __buildReceivedTransactions() { + const transactions = await this.query.many(queries.spv.receivedTransactions); + + for (const transaction of transactions) { + const wallet = this.walletManager.findByAddress(transaction.recipientId); + + wallet + ? (wallet.balance = new Bignum(transaction.amount)) + : logger.warn(`Lost cold wallet: ${transaction.recipientId} ${transaction.amount}`); + } + } + + /** + * Load and apply block rewards to wallets. + * @return {void} + */ + public async __buildBlockRewards() { + const blocks = await this.query.many(queries.spv.blockRewards); + + for (const block of blocks) { + const wallet = this.walletManager.findByPublicKey(block.generatorPublicKey); + wallet.balance = wallet.balance.plus(block.reward); + } + } + + /** + * Load and apply last forged blocks to wallets. + * @return {void} + */ + public async __buildLastForgedBlocks() { + const blocks = await this.query.many(queries.spv.lastForgedBlocks); + + for (const block of blocks) { + const wallet = this.walletManager.findByPublicKey(block.generatorPublicKey); + wallet.lastBlock = block; + } + } + + /** + * Load and apply sent transactions to wallets. + * @return {void} + */ + public async __buildSentTransactions() { + const transactions = await this.query.many(queries.spv.sentTransactions); + + for (const transaction of transactions) { + const wallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + wallet.balance = wallet.balance.minus(transaction.amount).minus(transaction.fee); + + if (wallet.balance.isLessThan(0) && !this.isGenesis(wallet)) { + logger.warn(`Negative balance: ${wallet}`); + } + } + } + + /** + * Used to determine if a wallet is a Genesis wallet. + * @return {Boolean} + */ + public isGenesis(wallet) { + return genesisWallets.includes(wallet.address); + } + + /** + * Load and apply second signature transactions to wallets. + * @return {void} + */ + public async __buildSecondSignatures() { + const transactions = await this.query.manyOrNone(queries.spv.secondSignatures); + + for (const transaction of transactions) { + const wallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + wallet.secondPublicKey = Transaction.deserialize( + transaction.serialized.toString("hex"), + ).asset.signature.publicKey; + } + } + + /** + * Load and apply votes to wallets. + * @return {void} + */ + public async __buildVotes() { + const transactions = await this.query.manyOrNone(queries.spv.votes); + + for (const transaction of transactions) { + const wallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + + if (!wallet.voted) { + const vote = Transaction.deserialize(transaction.serialized.toString("hex")).asset.votes[0]; + + if (vote.startsWith("+")) { + wallet.vote = vote.slice(1); + } + + // NOTE: The "voted" property is only used within this loop to avoid an issue + // that results in not properly applying "unvote" transactions as the "vote" property + // would be empty in that case and return a false result. + wallet.voted = true; + } + } + + this.walletManager.buildVoteBalances(); + } + + /** + * Load and apply delegate usernames to wallets. + * @return {void} + */ + public async __buildDelegates() { + // Register... + const transactions = await this.query.manyOrNone(queries.spv.delegates); + + transactions.forEach(transaction => { + const wallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + wallet.username = Transaction.deserialize(transaction.serialized.toString("hex")).asset.delegate.username; + this.walletManager.reindex(wallet); + }); + + // Forged Blocks... + const forgedBlocks = await this.query.manyOrNone(queries.spv.delegatesForgedBlocks); + forgedBlocks.forEach(block => { + const wallet = this.walletManager.findByPublicKey(block.generatorPublicKey); + wallet.forgedFees = wallet.forgedFees.plus(block.totalFees); + wallet.forgedRewards = wallet.forgedRewards.plus(block.totalRewards); + wallet.producedBlocks = +block.totalProduced; + }); + + // NOTE: This is highly NOT reliable, however the number of missed blocks + // is NOT used for the consensus + const delegates = await this.query.manyOrNone(queries.spv.delegatesRanks); + delegates.forEach((delegate, i) => { + const wallet = this.walletManager.findByPublicKey(delegate.publicKey); + wallet.missedBlocks = +delegate.missedBlocks; + // TODO: unknown property 'rate' being access on Wallet class + (wallet as any).rate = i + 1; + this.walletManager.reindex(wallet); + }); + } + + /** + * Load and apply multisignatures to wallets. + * @return {void} + */ + public async __buildMultisignatures() { + const transactions = await this.query.manyOrNone(queries.spv.multiSignatures); + + for (const transaction of transactions) { + const wallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + + if (!wallet.multisignature) { + wallet.multisignature = Transaction.deserialize( + transaction.serialized.toString("hex"), + ).asset.multisignature; + } + } + } + + /** + * Verify the consistency of the wallets table by comparing all records against + * the in memory wallets. + * NOTE: This is faster than rebuilding the entire table from scratch each time. + * @returns {Boolean} + */ + public async __verifyWalletsConsistency() { + const dbWallets = await this.query.manyOrNone(queries.wallets.all); + const inMemoryWallets = this.walletManager.allByPublicKey(); + + let detectedInconsistency = false; + if (dbWallets.length !== inMemoryWallets.length) { + detectedInconsistency = true; + } else { + for (const dbWallet of dbWallets) { + if (dbWallet.balance < 0 && !this.isGenesis(dbWallet)) { + detectedInconsistency = true; + logger.warn(`Wallet '${dbWallet.address}' has a negative balance of '${dbWallet.balance}'`); + break; + } + + if (dbWallet.voteBalance < 0) { + detectedInconsistency = true; + logger.warn(`Wallet ${dbWallet.address} has a negative vote balance of '${dbWallet.voteBalance}'`); + break; + } + + const inMemoryWallet = this.walletManager.findByPublicKey(dbWallet.publicKey); + + if ( + !inMemoryWallet.balance.isEqualTo(dbWallet.balance) || + !inMemoryWallet.voteBalance.isEqualTo(dbWallet.voteBalance) || + dbWallet.username !== inMemoryWallet.username + ) { + detectedInconsistency = true; + break; + } + } + } + + // Remove dirty flags when no inconsistency has been found + if (!detectedInconsistency) { + this.walletManager.clear(); + } + + return !detectedInconsistency; + } +} diff --git a/packages/core-database-postgres/src/sql/query-executor.ts b/packages/core-database-postgres/src/sql/query-executor.ts new file mode 100644 index 0000000000..123be4115e --- /dev/null +++ b/packages/core-database-postgres/src/sql/query-executor.ts @@ -0,0 +1,81 @@ +import { PostgresConnection } from "../postgres-connection"; + +export class QueryExecutor { + /** + * Create a new QueryExecutor instance. + * @param {[type]} connection + * @return {QueryBuilder} + */ + constructor(public connection: PostgresConnection) {} + + /** + * Execute the given query and expect no results. + * @param {QueryFile} query + * @param {Array} parameters + * @return {Promise} + */ + public async none(query, parameters = null) { + return this.__executeQueryFile(query, parameters, "none"); + } + + /** + * Execute the given query and expect one result. + * @param {QueryFile} query + * @param {Array} parameters + * @return {Promise} + */ + public async one(query, parameters = null) { + return this.__executeQueryFile(query, parameters, "one"); + } + + /** + * Execute the given query and expect one or no results. + * @param {QueryFile} query + * @param {Array} parameters + * @return {Promise} + */ + public async oneOrNone(query, parameters = null) { + return this.__executeQueryFile(query, parameters, "oneOrNone"); + } + + /** + * Execute the given query and expect many results. + * @param {QueryFile} query + * @param {Array} parameters + * @return {Promise} + */ + public async many(query, parameters = null) { + return this.__executeQueryFile(query, parameters, "many"); + } + + /** + * Execute the given query and expect many or no results. + * @param {QueryFile} query + * @param {Array} parameters + * @return {Promise} + */ + public async manyOrNone(query, parameters = null) { + return this.__executeQueryFile(query, parameters, "manyOrNone"); + } + + /** + * Execute the given query and expect any results. + * @param {QueryFile} query + * @param {Array} parameters + * @return {Promise} + */ + public async any(query, parameters = null) { + return this.__executeQueryFile(query, parameters, "any"); + } + + /** + * Execute the given query using the given method and parameters. + * @param {QueryFile} query + * @param {Array} parameters + * @param {String} method + * @return {QueryBuilder} + */ + public async __executeQueryFile(query, parameters, method) { + return this.connection.db[method](query, parameters); + } +} diff --git a/packages/core-database-postgres/src/utils/camelize-columns.ts b/packages/core-database-postgres/src/utils/camelize-columns.ts new file mode 100644 index 0000000000..09ecf415e0 --- /dev/null +++ b/packages/core-database-postgres/src/utils/camelize-columns.ts @@ -0,0 +1,17 @@ +/* tslint:disable:forin prefer-for-of*/ + +export function camelizeColumns(pgp, data) { + const tmp = data[0]; + + for (const prop in tmp) { + const camel = pgp.utils.camelize(prop); + + if (!(camel in tmp)) { + for (let i = 0; i < data.length; i++) { + const d = data[i]; + d[camel] = d[prop]; + delete d[prop]; + } + } + } +} diff --git a/packages/core-database-postgres/src/utils/index.ts b/packages/core-database-postgres/src/utils/index.ts new file mode 100644 index 0000000000..89d7824488 --- /dev/null +++ b/packages/core-database-postgres/src/utils/index.ts @@ -0,0 +1,4 @@ +import { camelizeColumns } from "./camelize-columns"; +import { loadQueryFile } from "./load-query-file"; + +export { camelizeColumns, loadQueryFile }; diff --git a/packages/core-database-postgres/src/utils/load-query-file.ts b/packages/core-database-postgres/src/utils/load-query-file.ts new file mode 100644 index 0000000000..04fef821f7 --- /dev/null +++ b/packages/core-database-postgres/src/utils/load-query-file.ts @@ -0,0 +1,25 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import path from "path"; +import { QueryFile } from "pg-promise"; + +const logger = app.resolvePlugin("logger"); + +export function loadQueryFile(directory, file) { + const fullPath = path.join(directory, file); + + const options = { + minify: true, + params: { + schema: "public", + }, + }; + + const query = new QueryFile(fullPath, options); + + if (query.error) { + logger.error(query.error.toString()); + } + + return query; +} diff --git a/packages/core-database-postgres/tsconfig.json b/packages/core-database-postgres/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-database-postgres/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-database/CHANGELOG.md b/packages/core-database/CHANGELOG.md deleted file mode 100644 index a413403460..0000000000 --- a/packages/core-database/CHANGELOG.md +++ /dev/null @@ -1,46 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Database rollback -- Block exceptions -- Common blocks -- More graceful handling of shutdown - -### Changed - -- Build delegate list in-memory to reduce database load -- Perform vote balance calculations in-memory to reduce database load -- Handle numbers as `BigNumber` instances -- Reduced complexity and duplicated logic -- Improved method names to more clearly show their intent -- Calculate previous rounds in-memory rather then hitting the database -- non-blocking wallet saving -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Wrong documentation -- Bad method calls for `sync/async` methods -- Only commit data when `saveBlockCommit` is called -- Properly log the transaction audit -- Properly update delegate ranks -- Only save dirty wallets -- Various memory leaks -- Forger order on mainnet -- Delegate registration handling - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-database/LICENSE b/packages/core-database/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-database/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-database/README.md b/packages/core-database/README.md index 9e7bec4119..92f27b92dc 100644 --- a/packages/core-database/README.md +++ b/packages/core-database/README.md @@ -14,10 +14,12 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Erwann Gentric](https://github.com/air1one) +- [François-Xavier Thoorens](https://github.com/fix) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-database/__tests__/__fixtures__/database-connection-stub.ts b/packages/core-database/__tests__/__fixtures__/database-connection-stub.ts new file mode 100644 index 0000000000..34459b572c --- /dev/null +++ b/packages/core-database/__tests__/__fixtures__/database-connection-stub.ts @@ -0,0 +1,53 @@ +// tslint:disable:no-empty + +import { Database } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; + +export class DatabaseConnectionStub implements Database.IDatabaseConnection { + public blocksRepository: Database.IBlocksRepository; + public roundsRepository: Database.IRoundsRepository; + public transactionsRepository: Database.ITransactionsRepository; + public walletsRepository: Database.IWalletsRepository; + public options: any; + + public buildWallets(height: number): Promise { + return undefined; + } + + public commitQueuedQueries(): any { + } + + public connect(): Promise { + return undefined; + } + + public deleteBlock(block: models.Block): Promise { + return undefined; + } + + public disconnect(): Promise { + return undefined; + } + + public enqueueDeleteBlock(block: models.Block): any { + } + + public enqueueDeleteRound(height: number): any { + } + + public enqueueSaveBlock(block: models.Block): any { + return null; + } + + public async make(): Promise { + return this; + } + + public saveBlock(block: models.Block): Promise { + return undefined; + } + + public saveWallets(wallets: any[], force?: boolean): Promise { + return undefined; + } +} diff --git a/packages/core-database/__tests__/__fixtures__/state-storage-stub.ts b/packages/core-database/__tests__/__fixtures__/state-storage-stub.ts new file mode 100644 index 0000000000..947a552faf --- /dev/null +++ b/packages/core-database/__tests__/__fixtures__/state-storage-stub.ts @@ -0,0 +1,56 @@ +/* tslint:disable:no-empty */ +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; + +export class StateStorageStub implements Blockchain.IStateStorage { + public cacheTransactions(transactions: models.ITransactionData[]): { added: models.ITransactionData[]; notAdded: models.ITransactionData[] } { + return undefined; + } + + public clear(): void { + } + + public clearWakeUpTimeout(): void { + } + + public getCachedTransactionIds(): string[] { + return []; + } + + public getCommonBlocks(ids: string[]): models.IBlockData[] { + return []; + } + + public getLastBlock(): models.Block | null { + return undefined; + } + + public getLastBlockIds(): string[] { + return []; + } + + public getLastBlocks(): models.Block[] { + return []; + } + + public getLastBlocksByHeight(start: number, end?: number): models.IBlockData[] { + return []; + } + + public pingBlock(incomingBlock: models.IBlockData): boolean { + return false; + } + + public pushPingBlock(block: models.IBlockData): void { + } + + public removeCachedTransactionIds(transactionIds: string[]): void { + } + + public reset(): void { + } + + public setLastBlock(block: models.Block): void { + } + +} diff --git a/packages/core-database/__tests__/__fixtures__/wallets.json b/packages/core-database/__tests__/__fixtures__/wallets.json index 61d49f59e8..4424bea579 100644 --- a/packages/core-database/__tests__/__fixtures__/wallets.json +++ b/packages/core-database/__tests__/__fixtures__/wallets.json @@ -1,14 +1,14 @@ [ - { - "address": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", - "publicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788" - }, - { - "address": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - }, - { - "address": "fake-address", - "publicKey": "fake-publicKey" - } + { + "address": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + "publicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788" + }, + { + "address": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" + }, + { + "address": "fake-address", + "publicKey": "fake-publicKey" + } ] diff --git a/packages/core-database/__tests__/__support__/setup.js b/packages/core-database/__tests__/__support__/setup.js deleted file mode 100644 index a2358e432c..0000000000 --- a/packages/core-database/__tests__/__support__/setup.js +++ /dev/null @@ -1,20 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -exports.setUp = async () => { - jest.setTimeout(60000) - - process.env.ARK_SKIP_BLOCKCHAIN = true - - await appHelper.setUp({ - exit: '@arkecosystem/core-blockchain', - exclude: [ - '@arkecosystem/core-p2p', - '@arkecosystem/core-transaction-pool-mem', - ], - }) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-database/__tests__/__support__/setup.ts b/packages/core-database/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..faba7ee97d --- /dev/null +++ b/packages/core-database/__tests__/__support__/setup.ts @@ -0,0 +1,22 @@ +import { app } from "@arkecosystem/core-container"; +import "@arkecosystem/core-test-utils"; +import { setUpContainer } from "@arkecosystem/core-test-utils/src/helpers/container"; + +export const setUp = async () => { + jest.setTimeout(60000); + + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + + return await setUpContainer({ + exit: "@arkecosystem/core-blockchain", + exclude: [ + "@arkecosystem/core-p2p", + "@arkecosystem/core-transaction-pool", + "@arkecosystem/core-database-postgres", + ], + }); +}; + +export const tearDown = async () => { + await app.tearDown(); +}; diff --git a/packages/core-database/__tests__/database-service.test.ts b/packages/core-database/__tests__/database-service.test.ts new file mode 100644 index 0000000000..399397e000 --- /dev/null +++ b/packages/core-database/__tests__/database-service.test.ts @@ -0,0 +1,221 @@ +import { Container, Database, EventEmitter } from "@arkecosystem/core-interfaces"; +import { Bignum, constants, models, transactionBuilder } from "@arkecosystem/crypto"; +import "jest-extended"; +import { WalletManager } from "../src"; +import { DatabaseService } from "../src/database-service"; +import { DatabaseConnectionStub } from "./__fixtures__/database-connection-stub"; +import { StateStorageStub } from "./__fixtures__/state-storage-stub"; +import { setUp, tearDown } from "./__support__/setup"; + +const { Block, Transaction, Wallet } = models; + +const { ARKTOSHI, TransactionTypes } = constants; + +let connection : Database.IDatabaseConnection; +let databaseService : DatabaseService; +let walletManager : Database.IWalletManager; +let genesisBlock : models.Block; +let container: Container.IContainer; +let emitter : EventEmitter.EventEmitter; + + +beforeAll(async () => { + container = await setUp(); + emitter = container.resolvePlugin("event-emitter"); + genesisBlock = new Block(require("@arkecosystem/core-test-utils/src/config/testnet/genesisBlock.json")); + connection = new DatabaseConnectionStub(); + walletManager = new WalletManager(); +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(()=> { + jest.restoreAllMocks() +}); + +function createService() { + return new DatabaseService({}, connection, walletManager, null, null); +} + +describe('Database Service', () => { + it('should listen for emitter events during constructor', () => { + jest.spyOn(emitter, 'on'); + jest.spyOn(emitter, 'once'); + + databaseService = createService(); + + + expect(emitter.on).toHaveBeenCalledWith('state:started', expect.toBeFunction()); + expect(emitter.on).toHaveBeenCalledWith('wallet.created.cold', expect.toBeFunction()); + expect(emitter.once).toHaveBeenCalledWith('shutdown', expect.toBeFunction()); + }); + + describe('applyBlock', () => { + it('should applyBlock', async () => { + jest.spyOn(walletManager, 'applyBlock').mockImplementation( (block) => block ); + jest.spyOn(emitter, 'emit'); + + + databaseService = createService(); + jest.spyOn(databaseService, 'applyRound').mockImplementation(() => null); // test applyRound logic separately + + await databaseService.applyBlock(genesisBlock); + + + expect(walletManager.applyBlock).toHaveBeenCalledWith(genesisBlock); + expect(emitter.emit).toHaveBeenCalledWith('block.applied', genesisBlock.data); + genesisBlock.transactions.forEach(tx => expect(emitter.emit).toHaveBeenCalledWith('transaction.applied', tx.data)); + }) + }); + + describe('getBlocksForRound', () => { + it('should fetch blocks using lastBlock in state-storage', async() => { + const stateStorageStub = new StateStorageStub(); + jest.spyOn(stateStorageStub, 'getLastBlock').mockReturnValue(null); + jest.spyOn(container, 'has').mockReturnValue(true); + jest.spyOn(container, 'resolve').mockReturnValue(stateStorageStub); + + databaseService = createService(); + jest.spyOn(databaseService, 'getLastBlock').mockReturnValue(null); + + + const blocks = await databaseService.getBlocksForRound(); + + + expect(blocks).toBeEmpty(); + expect(stateStorageStub.getLastBlock).toHaveBeenCalled(); + expect(databaseService.getLastBlock).not.toHaveBeenCalled(); + + }); + + it('should fetch blocks using lastBlock in database', async () => { + jest.spyOn(container, 'has').mockReturnValue(false); + + databaseService = createService(); + jest.spyOn(databaseService, 'getLastBlock').mockReturnValue(null); + + + const blocks = await databaseService.getBlocksForRound(); + + + expect(blocks).toBeEmpty(); + expect(databaseService.getLastBlock).toHaveBeenCalled(); + }); + + it('should fetch blocks from lastBlock height', async () => { + databaseService = createService(); + + jest.spyOn(databaseService, 'getLastBlock').mockReturnValue(genesisBlock); + jest.spyOn(databaseService, 'getBlocks').mockReturnValue([]); + jest.spyOn(container, 'has').mockReturnValue(false); + + + const blocks = await databaseService.getBlocksForRound(); + + + expect(blocks).toBeEmpty(); + expect(databaseService.getBlocks).toHaveBeenCalledWith(1, container.getConfig().getMilestone(genesisBlock.data.height).activeDelegates); + }) + }); + + /* TODO: Testing a method that's private. This needs a replacement by testing a public method instead */ + + describe("calcPreviousActiveDelegates", () => { + it("should calculate the previous delegate list", async () => { + walletManager = new WalletManager(); + const initialHeight = 52; + + // Create delegates + for (const transaction of genesisBlock.transactions) { + if (transaction.type === TransactionTypes.DelegateRegistration) { + const wallet = walletManager.findByPublicKey(transaction.senderPublicKey); + wallet.username = Transaction.deserialize( + transaction.serialized.toString(), + ).asset.delegate.username; + walletManager.reindex(wallet); + } + } + + const keys = { + passphrase: "this is a secret passphrase", + publicKey: "02c71ab1a1b5b7c278145382eb0b535249483b3c4715a4fe6169d40388bbb09fa7", + privateKey: "dcf4ead2355090279aefba91540f32e93b15c541ecb48ca73071f161b4f3e2e3", + address: "D64cbDctaiADEH7NREnvRQGV27bnb1v2kE", + }; + + // Beginning of round 2 with all delegates 0 vote balance. + const delegatesRound2 = walletManager.loadActiveDelegateList(51, initialHeight); + + // Prepare sender wallet + const sender = new Wallet(keys.address); + sender.publicKey = keys.publicKey; + sender.canApply = jest.fn(() => true); + walletManager.reindex(sender); + + // Apply 51 blocks, where each increases the vote balance of a delegate to + // reverse the current delegate order. + const blocksInRound = []; + for (let i = 0; i < 51; i++) { + const transfer = transactionBuilder + .transfer() + .amount(i * ARKTOSHI) + .recipientId(delegatesRound2[i].address) + .sign(keys.passphrase) + .build(); + + // Vote for itself + walletManager.findByPublicKey(delegatesRound2[i].publicKey).vote = delegatesRound2[i].publicKey; + // walletManager.byPublicKey[delegatesRound2[i].publicKey].vote = delegatesRound2[i].publicKey; + + const block = Block.create( + { + version: 0, + timestamp: 0, + height: initialHeight + i, + numberOfTransactions: 1, + totalAmount: transfer.amount, + totalFee: new Bignum(0.1), + reward: new Bignum(2), + payloadLength: 0, + payloadHash: "a".repeat(64), + transactions: [transfer], + }, + keys, + ); + + block.data.generatorPublicKey = keys.publicKey; + walletManager.applyBlock(block); + + blocksInRound.push(block); + } + + // The delegates from round 2 are now reversed in rank in round 3. + const delegatesRound3 = walletManager.loadActiveDelegateList(51, initialHeight + 51); + for (let i = 0; i < delegatesRound3.length; i++) { + expect(delegatesRound3[i].rate).toBe(i + 1); + expect(delegatesRound3[i].publicKey).toBe(delegatesRound2[delegatesRound3.length - i - 1].publicKey); + } + + + jest.spyOn(databaseService, 'getBlocksForRound').mockReturnValue(blocksInRound); + databaseService.walletManager = walletManager; + + // Necessary for revertRound to not blow up. + walletManager.allByUsername = jest.fn(() => { + const usernames = Object.values((walletManager as any).byUsername); + usernames.push(sender); + return usernames; + }); + + // Finally recalculate the round 2 list and compare against the original list + const restoredDelegatesRound2 = await (databaseService as any).calcPreviousActiveDelegates(2); + + for (let i = 0; i < restoredDelegatesRound2.length; i++) { + expect(restoredDelegatesRound2[i].rate).toBe(i + 1); + expect(restoredDelegatesRound2[i].publicKey).toBe(delegatesRound2[i].publicKey); + } + }); + }); +}); diff --git a/packages/core-database/__tests__/interface.test.js b/packages/core-database/__tests__/interface.test.js deleted file mode 100644 index 499cd679fc..0000000000 --- a/packages/core-database/__tests__/interface.test.js +++ /dev/null @@ -1,453 +0,0 @@ -const { Block, Transaction, Wallet } = require('@arkecosystem/crypto').models -const { Bignum, transactionBuilder } = require('@arkecosystem/crypto') -const { - ARKTOSHI, - TRANSACTION_TYPES, -} = require('@arkecosystem/crypto').constants -const app = require('./__support__/setup') - -let ConnectionInterface -let connectionInterface -let genesisBlock // eslint-disable-line no-unused-vars - -beforeAll(async done => { - await app.setUp() - - ConnectionInterface = require('../lib/interface') - connectionInterface = new ConnectionInterface() - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - - done() -}) - -afterAll(async done => { - await app.tearDown() - - done() -}) - -describe('Connection Interface', () => { - it('should be an object', () => { - expect(connectionInterface).toBeInstanceOf(ConnectionInterface) - }) - - describe('getConnection', () => { - it('should be a function', () => { - expect(connectionInterface.getConnection).toBeFunction() - }) - - it('should return the set connection', () => { - connectionInterface.connection = 'fake-connection' - - expect(connectionInterface.getConnection()).toBe('fake-connection') - }) - }) - - describe('connect', () => { - it('should be a function', () => { - expect(connectionInterface.connect).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.connect()).rejects.toThrowError( - 'Method [connect] not implemented!', - ) - }) - }) - - describe('disconnect', () => { - it('should be a function', () => { - expect(connectionInterface.disconnect).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.disconnect()).rejects.toThrowError( - 'Method [disconnect] not implemented!', - ) - }) - }) - - describe('getActiveDelegates', () => { - it('should be a function', () => { - expect(connectionInterface.getActiveDelegates).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect( - connectionInterface.getActiveDelegates(), - ).rejects.toThrowError('Method [getActiveDelegates] not implemented!') - }) - }) - - describe('buildWallets', () => { - it('should be a function', () => { - expect(connectionInterface.buildWallets).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.buildWallets()).rejects.toThrowError( - 'Method [buildWallets] not implemented!', - ) - }) - }) - - describe('saveWallets', () => { - it('should be a function', () => { - expect(connectionInterface.saveWallets).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.saveWallets()).rejects.toThrowError( - 'Method [saveWallets] not implemented!', - ) - }) - }) - - describe('saveBlock', () => { - it('should be a function', () => { - expect(connectionInterface.saveBlock).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.saveBlock()).rejects.toThrowError( - 'Method [saveBlock] not implemented!', - ) - }) - }) - - describe('enqueueSaveBlock', () => { - it('should be a function', () => { - expect(connectionInterface.enqueueSaveBlock).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(connectionInterface.enqueueSaveBlock).toThrow( - 'Method [enqueueSaveBlock] not implemented!', - ) - }) - }) - - describe('enqueueDeleteBlock', () => { - it('should be a function', () => { - expect(connectionInterface.enqueueDeleteBlock).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(connectionInterface.enqueueDeleteBlock).toThrow( - 'Method [enqueueDeleteBlock] not implemented!', - ) - }) - }) - - describe('enqueueDeleteRound', () => { - it('should be a function', () => { - expect(connectionInterface.enqueueDeleteRound).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(connectionInterface.enqueueDeleteRound).toThrow( - 'Method [enqueueDeleteRound] not implemented!', - ) - }) - }) - - describe('commitQueuedQueries', () => { - it('should be a function', () => { - expect(connectionInterface.commitQueuedQueries).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect( - connectionInterface.commitQueuedQueries(), - ).rejects.toThrowError('Method [commitQueuedQueries] not implemented!') - }) - }) - - describe('deleteBlock', () => { - it('should be a function', () => { - expect(connectionInterface.deleteBlock).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.deleteBlock()).rejects.toThrowError( - 'Method [deleteBlock] not implemented!', - ) - }) - }) - - describe('getBlock', () => { - it('should be a function', () => { - expect(connectionInterface.getBlock).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.getBlock()).rejects.toThrowError( - 'Method [getBlock] not implemented!', - ) - }) - }) - - describe('getLastBlock', () => { - it('should be a function', () => { - expect(connectionInterface.getLastBlock).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.getLastBlock()).rejects.toThrowError( - 'Method [getLastBlock] not implemented!', - ) - }) - }) - - describe('getBlocks', () => { - it('should be a function', () => { - expect(connectionInterface.getBlocks).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.getBlocks()).rejects.toThrowError( - 'Method [getBlocks] not implemented!', - ) - }) - }) - - describe('getRecentBlockIds', () => { - it('should be a function', () => { - expect(connectionInterface.getRecentBlockIds).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect( - connectionInterface.getRecentBlockIds(), - ).rejects.toThrowError('Method [getRecentBlockIds] not implemented!') - }) - }) - - describe('saveRound', () => { - it('should be a function', () => { - expect(connectionInterface.saveRound).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.saveRound()).rejects.toThrowError( - 'Method [saveRound] not implemented!', - ) - }) - }) - - describe('deleteRound', () => { - it('should be a function', () => { - expect(connectionInterface.deleteRound).toBeFunction() - }) - - it('should throw an exception', async () => { - await expect(connectionInterface.deleteRound()).rejects.toThrowError( - 'Method [deleteRound] not implemented!', - ) - }) - }) - - describe('updateDelegateStats', () => { - it('should be a function', () => { - expect(connectionInterface.updateDelegateStats).toBeFunction() - }) - }) - - describe.skip('applyRound', () => { - it('should be a function', () => { - expect(connectionInterface.applyRound).toBeFunction() - }) - }) - - describe.skip('revertRound', () => { - it('should be a function', () => { - expect(connectionInterface.revertRound).toBeFunction() - }) - }) - - describe.skip('validateDelegate', () => { - it('should be a function', () => { - expect(connectionInterface.validateDelegate).toBeFunction() - }) - }) - - describe.skip('validateForkedBlock', () => { - it('should be a function', () => { - expect(connectionInterface.validateForkedBlock).toBeFunction() - }) - }) - - describe.skip('applyBlock', () => { - it('should be a function', () => { - expect(connectionInterface.applyBlock).toBeFunction() - }) - }) - - describe.skip('revertBlock', () => { - it('should be a function', () => { - expect(connectionInterface.revertBlock).toBeFunction() - }) - }) - - describe.skip('verifyTransaction', () => { - it('should be a function', () => { - expect(connectionInterface.verifyTransaction).toBeFunction() - }) - }) - - describe('__calcPreviousActiveDelegates', () => { - it('should be a function', () => { - expect(connectionInterface.__calcPreviousActiveDelegates).toBeFunction() - }) - - it('should calculate the previous delegate list', async () => { - const walletManager = new (require('../lib/wallet-manager'))() - const initialHeight = 52 - - // Create delegates - for (const transaction of genesisBlock.transactions) { - if (transaction.type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - const wallet = walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - wallet.username = Transaction.deserialize( - transaction.serialized.toString('hex'), - ).asset.delegate.username - walletManager.reindex(wallet) - } - } - - const keys = { - passphrase: 'this is a secret passphrase', - publicKey: - '02c71ab1a1b5b7c278145382eb0b535249483b3c4715a4fe6169d40388bbb09fa7', - privateKey: - 'dcf4ead2355090279aefba91540f32e93b15c541ecb48ca73071f161b4f3e2e3', - address: 'D64cbDctaiADEH7NREnvRQGV27bnb1v2kE', - } - - // Beginning of round 2 with all delegates 0 vote balance. - const delegatesRound2 = walletManager.loadActiveDelegateList( - 51, - initialHeight, - ) - - // Prepare sender wallet - const sender = new Wallet(keys.address) - sender.publicKey = keys.publicKey - sender.canApply = jest.fn(() => true) - walletManager.reindex(sender) - - // Apply 51 blocks, where each increases the vote balance of a delegate to - // reverse the current delegate order. - const blocksInRound = [] - for (let i = 0; i < 51; i++) { - const transfer = transactionBuilder - .transfer() - .amount(i * ARKTOSHI) - .recipientId(delegatesRound2[i].address) - .sign(keys.passphrase) - .build() - - // Vote for itself - walletManager.byPublicKey[delegatesRound2[i].publicKey].vote = - delegatesRound2[i].publicKey - - const block = Block.create( - { - version: 0, - timestamp: 0, - height: initialHeight + i, - numberOfTransactions: 0, - totalAmount: transfer.amount, - totalFee: new Bignum(0.1), - reward: new Bignum(2), - payloadLength: 32 * 0, - payloadHash: '', - transactions: [transfer], - }, - keys, - ) - - block.data.generatorPublicKey = keys.publicKey - walletManager.applyBlock(block) - - blocksInRound.push(block) - } - - // The delegates from round 2 are now reversed in rank in round 3. - const delegatesRound3 = walletManager.loadActiveDelegateList( - 51, - initialHeight + 51, - ) - for (let i = 0; i < delegatesRound3.length; i++) { - expect(delegatesRound3[i].rate).toBe(i + 1) - expect(delegatesRound3[i].publicKey).toBe( - delegatesRound2[delegatesRound3.length - i - 1].publicKey, - ) - } - - const connection = new ConnectionInterface() - connection.__getBlocksForRound = jest.fn(async () => blocksInRound) - connection.walletManager = walletManager - - // Necessary for revertRound to not blow up. - walletManager.allByUsername = jest.fn(() => { - const usernames = Object.values(walletManager.byUsername) - usernames.push(sender) - return usernames - }) - - // Finally recalculate the round 2 list and compare against the original list - const restoredDelegatesRound2 = await connection.__calcPreviousActiveDelegates( - 2, - ) - - for (let i = 0; i < restoredDelegatesRound2.length; i++) { - expect(restoredDelegatesRound2[i].rate).toBe(i + 1) - expect(restoredDelegatesRound2[i].publicKey).toBe( - delegatesRound2[i].publicKey, - ) - } - }) - }) - - describe('_registerWalletManager', () => { - it('should be a function', () => { - expect(connectionInterface._registerWalletManager).toBeFunction() - }) - - it('should register the wallet manager', () => { - expect(connectionInterface).not.toHaveProperty('walletManager') - - connectionInterface._registerWalletManager() - - expect(connectionInterface).toHaveProperty('walletManager') - }) - }) - - describe('_registerRepositories', () => { - it('should be a function', () => { - expect(connectionInterface._registerRepositories).toBeFunction() - }) - - it('should register the repositories', async () => { - await expect(connectionInterface).not.toHaveProperty('wallets') - await expect(connectionInterface).not.toHaveProperty('delegates') - - connectionInterface._registerRepositories() - - await expect(connectionInterface).toHaveProperty('wallets') - await expect(connectionInterface.wallets).toBeInstanceOf( - require('../lib/repositories/wallets'), - ) - - await expect(connectionInterface).toHaveProperty('delegates') - await expect(connectionInterface.delegates).toBeInstanceOf( - require('../lib/repositories/delegates'), - ) - }) - }) -}) diff --git a/packages/core-database/__tests__/repositories/delegates.test.js b/packages/core-database/__tests__/repositories/delegates.test.js deleted file mode 100644 index a6511dff96..0000000000 --- a/packages/core-database/__tests__/repositories/delegates.test.js +++ /dev/null @@ -1,348 +0,0 @@ -const { - Bignum, - crypto, - constants: { ARKTOSHI }, -} = require('@arkecosystem/crypto') -const { Block } = require('@arkecosystem/crypto').models -const { delegateCalculator } = require('@arkecosystem/core-utils') -const app = require('../__support__/setup') - -let genesisBlock -let repository -let walletManager - -beforeAll(async done => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - - done() -}) - -afterAll(async done => { - await app.tearDown() - - done() -}) - -beforeEach(async done => { - walletManager = new (require('../../lib/wallet-manager'))() - repository = new (require('../../lib/repositories/delegates'))({ - walletManager, - }) - - done() -}) - -function generateWallets() { - return genesisBlock.transactions.map(transaction => { - const address = crypto.getAddress(transaction.senderPublicKey) - - return { - address, - publicKey: `publicKey-${address}`, - secondPublicKey: `secondPublicKey-${address}`, - vote: `vote-${address}`, - username: `username-${address}`, - balance: new Bignum(100), - voteBalance: new Bignum(200), - } - }) -} - -describe('Delegate Repository', () => { - it('should be an object', () => { - expect(repository).toBeObject() - }) - - describe('getLocalDelegates', () => { - const delegates = [ - { username: 'delegate-0' }, - { username: 'delegate-1' }, - { username: 'delegate-2' }, - ] - const wallets = [ - delegates[0], - {}, - delegates[1], - { username: '' }, - delegates[2], - {}, - ] - - it('should be a function', () => { - expect(repository.getLocalDelegates).toBeFunction() - }) - - it('should return the local wallets of the connection that are delegates', () => { - repository.connection.walletManager.all = jest.fn(() => wallets) - - expect(repository.getLocalDelegates()).toEqual( - expect.arrayContaining(delegates), - ) - expect(repository.connection.walletManager.all).toHaveBeenCalled() - }) - }) - - describe('findAll', () => { - it('should be a function', () => { - expect(repository.findAll).toBeFunction() - }) - - it('should be ok without params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll() - expect(count).toBe(52) - expect(rows).toHaveLength(52) - }) - - it('should be ok with params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ offset: 10, limit: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (no offset)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ limit: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (offset = 0)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ offset: 0, limit: 12 }) - expect(count).toBe(52) - expect(rows).toHaveLength(12) - }) - - it('should be ok with params (no limit)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ offset: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(42) - }) - }) - - describe('paginate', () => { - it('should be a function', () => { - expect(repository.paginate).toBeFunction() - }) - - it('should be ok without params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.paginate() - expect(count).toBe(52) - expect(rows).toHaveLength(52) - }) - - it('should be ok with params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.paginate({ offset: 10, limit: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (no offset)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.paginate({ limit: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (offset = 0)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.paginate({ offset: 0, limit: 12 }) - expect(count).toBe(52) - expect(rows).toHaveLength(12) - }) - - it('should be ok with params (no limit)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.paginate({ offset: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(42) - }) - }) - - describe('search', () => { - it('should be a function', () => { - expect(repository.search).toBeFunction() - }) - - it('should search by exact username match', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.search({ - username: 'username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn', - }) - - expect(count).toBe(1) - expect(rows).toHaveLength(1) - }) - - it('should search that username contains the string', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.search({ username: 'username' }) - - expect(count).toBe(52) - expect(rows).toHaveLength(52) - }) - - describe('when no results', () => { - it('should be ok', () => { - const { count, rows } = repository.search({ - username: 'unknown-dummy-username', - }) - - expect(count).toBe(0) - expect(rows).toHaveLength(0) - }) - }) - - it('should be ok with params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.search({ - username: 'username', - offset: 10, - limit: 10, - }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (no offset)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.search({ - username: 'username', - limit: 10, - }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (offset = 0)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.search({ - username: 'username', - offset: 0, - limit: 12, - }) - expect(count).toBe(52) - expect(rows).toHaveLength(12) - }) - - it('should be ok with params (no limit)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.search({ - username: 'username', - offset: 10, - }) - expect(count).toBe(52) - expect(rows).toHaveLength(42) - }) - }) - - describe('findById', () => { - const expectWallet = key => { - const wallets = generateWallets() - walletManager.index(wallets) - - const wallet = repository.findById(wallets[0][key]) - expect(wallet).toBeObject() - expect(wallet.address).toBe(wallets[0].address) - expect(wallet.publicKey).toBe(wallets[0].publicKey) - expect(wallet.username).toBe(wallets[0].username) - } - - it('should be a function', () => { - expect(repository.findById).toBeFunction() - }) - - it('should be ok with an address', () => { - expectWallet('address') - }) - - it('should be ok with a publicKey', () => { - expectWallet('publicKey') - }) - - it('should be ok with a username', () => { - expectWallet('username') - }) - }) - - describe('getActiveAtHeight', () => { - it('should be a function', () => { - expect(repository.getActiveAtHeight).toBeFunction() - }) - - it('should be ok', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const delegate = { - username: 'test', - publicKey: 'test', - voteBalance: new Bignum(10000 * ARKTOSHI), - producedBlocks: 1000, - missedBlocks: 500, - } - const height = 1 - - repository.connection.getActiveDelegates = jest.fn(() => [delegate]) - repository.connection.wallets = { - findById: jest.fn(() => delegate), - } - - const results = repository.getActiveAtHeight(height) - - expect(results).toBeArray() - expect(results[0].username).toBeString() - expect(results[0].approval).toBeNumber() - expect(results[0].productivity).toBeNumber() - expect(results[0].approval).toBe( - delegateCalculator.calculateApproval(delegate, height), - ) - expect(results[0].productivity).toBe( - delegateCalculator.calculateProductivity(delegate), - ) - }) - }) -}) diff --git a/packages/core-database/__tests__/repositories/delegates.test.ts b/packages/core-database/__tests__/repositories/delegates.test.ts new file mode 100644 index 0000000000..1470ae85ce --- /dev/null +++ b/packages/core-database/__tests__/repositories/delegates.test.ts @@ -0,0 +1,287 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { delegateCalculator } from "@arkecosystem/core-utils"; +import { Bignum, constants, crypto, models } from "@arkecosystem/crypto"; +import genesisBlockTestnet from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { DelegatesRepository, WalletsRepository } from "../../src"; +import { DatabaseService } from "../../src/database-service"; +import { setUp, tearDown } from "../__support__/setup"; + +const { ARKTOSHI } = constants; +const { Block } = models; + +let genesisBlock; +let repository; + +let walletsRepository : Database.IWalletsBusinessRepository; +let walletManager: Database.IWalletManager; +let databaseService: Database.IDatabaseService; + +beforeAll(async done => { + await setUp(); + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(genesisBlockTestnet); + + done(); +}); + +afterAll(async done => { + await tearDown(); + + done(); +}); + +beforeEach(async done => { + const { WalletManager } = require("../../src/wallet-manager"); + walletManager = new WalletManager(); + + repository = new DelegatesRepository(() => databaseService); + walletsRepository = new WalletsRepository(() => databaseService); + databaseService = new DatabaseService(null, null, walletManager, walletsRepository, repository); + + done(); +}); + +function generateWallets() { + return genesisBlock.transactions.map((transaction, index) => { + const address = crypto.getAddress(transaction.senderPublicKey); + + return { + address, + publicKey: `publicKey-${address}`, + secondPublicKey: `secondPublicKey-${address}`, + vote: `vote-${address}`, + username: `username-${address}`, + balance: new Bignum(100), + voteBalance: new Bignum(200), + rate: index + 1, + }; + }); +} + +describe("Delegate Repository", () => { + describe("getLocalDelegates", () => { + const delegates = [{ username: "delegate-0" }, { username: "delegate-1" }, { username: "delegate-2" }]; + const wallets = [delegates[0], {}, delegates[1], { username: "" }, delegates[2], {}]; + + it("should return the local wallets of the connection that are delegates", () => { + jest.spyOn(walletManager, 'allByAddress').mockReturnValue(wallets); + + const actualDelegates = repository.getLocalDelegates(); + + expect(actualDelegates).toEqual(expect.arrayContaining(delegates)); + expect(walletManager.allByAddress).toHaveBeenCalled(); + }); + }); + + describe("findAll", () => { + it("should be ok without params", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll(); + expect(count).toBe(52); + expect(rows).toHaveLength(52); + expect(rows.sort((a, b) => a.rate < b.rate)).toEqual(rows); + }); + + it("should be ok with params", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ offset: 10, limit: 10, orderBy: "rate:desc" }); + expect(count).toBe(52); + expect(rows).toHaveLength(10); + expect(rows.sort((a, b) => a.rate > b.rate)).toEqual(rows); + }); + + it("should be ok with params (no offset)", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ limit: 10 }); + expect(count).toBe(52); + expect(rows).toHaveLength(10); + }); + + it("should be ok with params (offset = 0)", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ offset: 0, limit: 12 }); + expect(count).toBe(52); + expect(rows).toHaveLength(12); + }); + + it("should be ok with params (no limit)", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ offset: 10 }); + expect(count).toBe(52); + expect(rows).toHaveLength(42); + }); + }); + + describe("search", () => { + beforeEach(() => { + const wallets = generateWallets(); + walletManager.index(wallets); + }); + + describe("by `username`", () => { + it("should search by exact match", () => { + const username = "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn"; + const { count, rows } = repository.search({ username }); + + expect(count).toBe(1); + expect(rows).toHaveLength(1); + expect(rows[0].username).toEqual(username); + }); + + it("should search that username contains the string", () => { + const { count, rows } = repository.search({ username: "username" }); + + expect(count).toBe(52); + expect(rows).toHaveLength(52); + }); + + describe('when a username is "undefined"', () => { + it("should return it", () => { + // Index a wallet with username "undefined" + walletManager.allByAddress()[0].username = 'undefined'; + + const username = "undefined"; + const { count, rows } = repository.search({ username }); + + expect(count).toBe(1); + expect(rows).toHaveLength(1); + expect(rows[0].username).toEqual(username); + }); + }); + + describe("when the username does not exist", () => { + it("should return no results", () => { + const { count, rows } = repository.search({ + username: "unknown-dummy-username", + }); + + expect(count).toBe(0); + expect(rows).toHaveLength(0); + }); + }); + + it("should be ok with params", () => { + const { count, rows } = repository.search({ + username: "username", + offset: 10, + limit: 10, + }); + expect(count).toBe(52); + expect(rows).toHaveLength(10); + }); + + it("should be ok with params (no offset)", () => { + const { count, rows } = repository.search({ + username: "username", + limit: 10, + }); + expect(count).toBe(52); + expect(rows).toHaveLength(10); + }); + + it("should be ok with params (offset = 0)", () => { + const { count, rows } = repository.search({ + username: "username", + offset: 0, + limit: 12, + }); + expect(count).toBe(52); + expect(rows).toHaveLength(12); + }); + + it("should be ok with params (no limit)", () => { + const { count, rows } = repository.search({ + username: "username", + offset: 10, + }); + expect(count).toBe(52); + expect(rows).toHaveLength(42); + }); + }); + + describe("when searching without params", () => { + it("should return all results", () => { + const { count, rows } = repository.search({}); + + expect(count).toBe(52); + expect(rows).toHaveLength(52); + }); + + describe('when a username is "undefined"', () => { + it("should return all results", () => { + // Index a wallet with username "undefined" + walletManager.allByAddress()[0].username = "undefined"; + + const { count, rows } = repository.search({}); + expect(count).toBe(52); + expect(rows).toHaveLength(52); + }); + }); + }); + }); + + describe("findById", () => { + const expectWallet = key => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const wallet = repository.findById(wallets[0][key]); + expect(wallet).toBeObject(); + expect(wallet.address).toBe(wallets[0].address); + expect(wallet.publicKey).toBe(wallets[0].publicKey); + expect(wallet.username).toBe(wallets[0].username); + }; + + it("should be ok with an address", () => { + expectWallet("address"); + }); + + it("should be ok with a publicKey", () => { + expectWallet("publicKey"); + }); + + it("should be ok with a username", () => { + expectWallet("username"); + }); + }); + + describe("getActiveAtHeight", () => { + it("should be ok", async () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const delegate = { + username: "test", + publicKey: "test", + voteBalance: new Bignum(10000 * ARKTOSHI), + producedBlocks: 1000, + missedBlocks: 500, + }; + const height = 1; + + jest.spyOn(databaseService, 'getActiveDelegates').mockReturnValue([delegate]); + jest.spyOn(walletsRepository, 'findById').mockReturnValue(delegate); + + const results = await repository.getActiveAtHeight(height); + + expect(results).toBeArray(); + expect(results[0].username).toBeString(); + expect(results[0].approval).toBeNumber(); + expect(results[0].productivity).toBeNumber(); + expect(results[0].approval).toBe(delegateCalculator.calculateApproval(delegate, height)); + expect(results[0].productivity).toBe(delegateCalculator.calculateProductivity(delegate)); + }); + }); +}); diff --git a/packages/core-database/__tests__/repositories/utils/filter-rows.test.js b/packages/core-database/__tests__/repositories/utils/filter-rows.test.js deleted file mode 100644 index ad92e0f46a..0000000000 --- a/packages/core-database/__tests__/repositories/utils/filter-rows.test.js +++ /dev/null @@ -1,119 +0,0 @@ -const app = require('../../__support__/setup') - -let filterRows - -beforeAll(async done => { - await app.setUp() - - filterRows = require('../../../lib/repositories/utils/filter-rows') - - done() -}) - -afterAll(async done => { - await app.tearDown() - - done() -}) - -describe('Filter Rows', () => { - const rows = [ - { a: 1, b: 2, c: [] }, - { - a: 2, b: 2, c: ['dummy-1'], d: ['dummy-0'], - }, - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - { - a: 2, b: 4, c: ['dummy-2'], d: 'dummy-0', - }, - { a: 3, b: 4, c: ['DUMMY-1'] }, - ] - - describe('exact', () => { - it('match objects with the same value than the parameter', () => { - expect(filterRows(rows, { a: 1 }, { exact: ['a'] })).toEqual([ - { a: 1, b: 2, c: [] }, - ]) - expect(filterRows(rows, { a: 3 }, { exact: ['a'] })).toEqual([ - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - { a: 3, b: 4, c: ['DUMMY-1'] }, - ]) - expect(filterRows(rows, { a: 3, b: 3 }, { exact: ['a'] })).toEqual([ - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - { a: 3, b: 4, c: ['DUMMY-1'] }, - ]) - expect(filterRows(rows, { a: 3, b: 3 }, { exact: ['a', 'b'] })).toEqual([ - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - ]) - }) - }) - - describe('between', () => { - it('match objects that include a value beween two parameters (included)', () => { - expect(filterRows(rows, { a: { from: 3 } }, { between: ['a'] })).toEqual([ - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - { a: 3, b: 4, c: ['DUMMY-1'] }, - ]) - expect( - filterRows(rows, { a: { from: 2, to: 2 } }, { between: ['a'] }), - ).toEqual([ - { - a: 2, b: 2, c: ['dummy-1'], d: ['dummy-0'], - }, - { - a: 2, b: 4, c: ['dummy-2'], d: 'dummy-0', - }, - ]) - expect(filterRows(rows, { a: { to: 2 } }, { between: ['a'] })).toEqual([ - { a: 1, b: 2, c: [] }, - { - a: 2, b: 2, c: ['dummy-1'], d: ['dummy-0'], - }, - { - a: 2, b: 4, c: ['dummy-2'], d: 'dummy-0', - }, - ]) - expect( - filterRows(rows, { b: { from: 3, to: 4 } }, { between: ['b'] }), - ).toEqual([ - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - { - a: 2, b: 4, c: ['dummy-2'], d: 'dummy-0', - }, - { a: 3, b: 4, c: ['DUMMY-1'] }, - ]) - }) - }) - - // This filter is not used yet - describe('any', () => { - it('match objects that include some values of the parameters', () => { - expect(filterRows(rows, { c: ['dummy-1'] }, { any: ['c'] })).toEqual([ - { - a: 2, b: 2, c: ['dummy-1'], d: ['dummy-0'], - }, - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - ]) - expect( - filterRows(rows, { c: ['dummy-1'], d: ['dummy-0'] }, { any: ['c'] }), - ).toEqual([ - { - a: 2, b: 2, c: ['dummy-1'], d: ['dummy-0'], - }, - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - ]) - expect( - filterRows( - rows, - { c: ['dummy-1'], d: ['dummy-0'] }, - { any: ['c', 'd'] }, - ), - ).toEqual([ - { - a: 2, b: 2, c: ['dummy-1'], d: ['dummy-0'], - }, - { a: 3, b: 3, c: ['dummy-3', 'dummy-1', 'dummy-4'] }, - ]) - }) - }) -}) diff --git a/packages/core-database/__tests__/repositories/utils/filter-rows.test.ts b/packages/core-database/__tests__/repositories/utils/filter-rows.test.ts new file mode 100644 index 0000000000..42a70699ca --- /dev/null +++ b/packages/core-database/__tests__/repositories/utils/filter-rows.test.ts @@ -0,0 +1,87 @@ +import "jest-extended"; + +import { setUp, tearDown } from "../../__support__/setup"; + +let filterRows; + +beforeAll(async done => { + await setUp(); + + filterRows = require("../../../src/repositories/utils/filter-rows"); + + done(); +}); + +afterAll(async done => { + await tearDown(); + + done(); +}); + +describe("Filter Rows", () => { + const rows = [ + { a: 1, b: 2, c: [] }, + { + a: 2, + b: 2, + c: ["dummy-1"], + d: ["dummy-0"], + e: "value-e-1", + }, + { a: 3, b: 3, c: ["dummy-3", "dummy-1", "dummy-4"] }, + { + a: 2, + b: 4, + c: ["dummy-2"], + d: "dummy-0", + e: "value-e-2", + }, + { a: 3, b: 4, c: ["DUMMY-1"] }, + { + c: ["nop"], + d: "nop", + e: "value-e-3", + }, + ]; + + describe("exact", () => { + it("match objects with the same value than the parameter", () => { + expect(filterRows(rows, { a: 1 }, { exact: ["a"] })).toEqual([rows[0]]); + expect(filterRows(rows, { a: 3 }, { exact: ["a"] })).toEqual([rows[2], rows[4]]); + expect(filterRows(rows, { a: 3, b: 3 }, { exact: ["a"] })).toEqual([rows[2], rows[4]]); + expect(filterRows(rows, { a: 3, b: 3 }, { exact: ["a", "b"] })).toEqual([rows[2]]); + }); + }); + + describe("between", () => { + it("match objects that include a value beween two parameters (included)", () => { + expect(filterRows(rows, { a: { from: 3 } }, { between: ["a"] })).toEqual([rows[2], rows[4]]); + expect(filterRows(rows, { a: { from: 2, to: 2 } }, { between: ["a"] })).toEqual([rows[1], rows[3]]); + expect(filterRows(rows, { a: { to: 2 } }, { between: ["a"] })).toEqual([rows[0], rows[1], rows[3]]); + expect(filterRows(rows, { b: { from: 3, to: 4 } }, { between: ["b"] })).toEqual([ + rows[2], + rows[3], + rows[4], + ]); + }); + }); + + describe("in", () => { + it("match objects that include some values of the parameters", () => { + expect(filterRows(rows, { e: ["value-e-99"] }, { in: ["e"] })).toEqual([]); + expect(filterRows(rows, { e: ["value-e-1", "value-e-3"] }, { in: ["e"] })).toEqual([rows[1], rows[5]]); + }); + }); + + // This filter is not used yet + describe("any", () => { + it("match objects that include some values of the parameters", () => { + expect(filterRows(rows, { c: ["dummy-1"] }, { any: ["c"] })).toEqual([rows[1], rows[2]]); + expect(filterRows(rows, { c: ["dummy-1"], d: ["dummy-0"] }, { any: ["c"] })).toEqual([rows[1], rows[2]]); + expect(filterRows(rows, { c: ["dummy-1"], d: ["dummy-0"] }, { any: ["c", "d"] })).toEqual([ + rows[1], + rows[2], + ]); + }); + }); +}); diff --git a/packages/core-database/__tests__/repositories/wallets.test.js b/packages/core-database/__tests__/repositories/wallets.test.js deleted file mode 100644 index fef65f64aa..0000000000 --- a/packages/core-database/__tests__/repositories/wallets.test.js +++ /dev/null @@ -1,404 +0,0 @@ -const uniq = require('lodash/uniq') -const compact = require('lodash/compact') -const { Bignum, crypto } = require('@arkecosystem/crypto') -const { Block } = require('@arkecosystem/crypto').models -const app = require('../__support__/setup') - -let genesisBlock -let genesisSenders -let repository -let walletManager - -beforeAll(async done => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - genesisSenders = uniq( - compact(genesisBlock.transactions.map(tx => tx.senderPublicKey)), - ) - - done() -}) - -afterAll(async done => { - await app.tearDown() - - done() -}) - -beforeEach(async done => { - walletManager = new (require('../../lib/wallet-manager'))() - repository = new (require('../../lib/repositories/wallets'))({ - walletManager, - }) - - done() -}) - -function generateWallets() { - return genesisSenders.map(senderPublicKey => ({ - address: crypto.getAddress(senderPublicKey), - })) -} - -function generateVotes() { - return genesisSenders.map(senderPublicKey => ({ - address: crypto.getAddress(senderPublicKey), - vote: genesisBlock.transactions[0].senderPublicKey, - })) -} - -function generateFullWallets() { - return genesisSenders.map(senderPublicKey => { - const address = crypto.getAddress(senderPublicKey) - - return { - address, - publicKey: `publicKey-${address}`, - secondPublicKey: `secondPublicKey-${address}`, - vote: `vote-${address}`, - username: `username-${address}`, - balance: 100, - voteBalance: 200, - } - }) -} - -describe('Wallet Repository', () => { - it('should be an object', () => { - expect(repository).toBeObject() - }) - - describe('all', () => { - it('should be a function', () => { - expect(repository.all).toBeFunction() - }) - - it('should return the local wallets of the connection', () => { - repository.connection.walletManager.all = jest.fn() - repository.all() - expect(repository.connection.walletManager.all).toHaveBeenCalled() - }) - }) - - describe('findAll', () => { - it('should be a function', () => { - expect(repository.findAll).toBeFunction() - }) - - it('should be ok without params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll() - expect(count).toBe(52) - expect(rows).toHaveLength(52) - }) - - it('should be ok with params', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ offset: 10, limit: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (no offset)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ limit: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (offset = 0)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ offset: 0, limit: 12 }) - expect(count).toBe(52) - expect(rows).toHaveLength(12) - }) - - it('should be ok with params (no limit)', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - const { count, rows } = repository.findAll({ offset: 10 }) - expect(count).toBe(52) - expect(rows).toHaveLength(42) - }) - }) - - describe('findAllByVote', () => { - const vote = 'dummy-sender-public-key' - - beforeEach(() => { - const wallets = generateVotes() - wallets.forEach((wallet, i) => { - if (i < 17) { - wallet.vote = vote - } - }) - walletManager.index(wallets) - }) - - it('should be a function', () => { - expect(repository.findAllByVote).toBeFunction() - }) - - it('should be ok without params', () => { - const { count, rows } = repository.findAllByVote(vote) - expect(count).toBe(17) - expect(rows).toHaveLength(17) - }) - - it('should be ok with params', () => { - const { count, rows } = repository.findAllByVote(vote, { - offset: 10, - limit: 10, - }) - expect(count).toBe(17) - expect(rows).toHaveLength(7) - }) - - it('should be ok with params (no offset)', () => { - const { count, rows } = repository.findAllByVote(vote, { limit: 10 }) - expect(count).toBe(17) - expect(rows).toHaveLength(10) - }) - - it('should be ok with params (offset = 0)', () => { - const { count, rows } = repository.findAllByVote(vote, { - offset: 0, - limit: 1, - }) - expect(count).toBe(17) - expect(rows).toHaveLength(1) - }) - - it('should be ok with params (no limit)', () => { - const { count, rows } = repository.findAllByVote(vote, { offset: 30 }) - expect(count).toBe(17) - expect(rows).toHaveLength(0) - }) - }) - - describe('findById', () => { - const expectWallet = key => { - const wallets = generateFullWallets() - walletManager.index(wallets) - - const wallet = repository.findById(wallets[0][key]) - expect(wallet).toBeObject() - expect(wallet.address).toBe(wallets[0].address) - expect(wallet.publicKey).toBe(wallets[0].publicKey) - expect(wallet.username).toBe(wallets[0].username) - } - - it('should be a function', () => { - expect(repository.findById).toBeFunction() - }) - - it('should be ok with an address', () => { - expectWallet('address') - }) - - it('should be ok with a publicKey', () => { - expectWallet('publicKey') - }) - - it('should be ok with a username', () => { - expectWallet('username') - }) - }) - - describe('count', () => { - it('should be a function', () => { - expect(repository.count).toBeFunction() - }) - - it('should be ok', () => { - const wallets = generateWallets() - walletManager.index(wallets) - - expect(repository.count()).toBe(52) - }) - }) - - describe('top', () => { - beforeEach(() => { - walletManager.reindex({ address: 'dummy-1', balance: new Bignum(1000) }) - walletManager.reindex({ address: 'dummy-2', balance: new Bignum(2000) }) - walletManager.reindex({ address: 'dummy-3', balance: new Bignum(3000) }) - }) - - it('should be a function', () => { - expect(repository.top).toBeFunction() - }) - - it('should be ok without params', () => { - const { count, rows } = repository.top() - - expect(count).toBe(3) - expect(rows.length).toBe(3) - expect(rows[0].balance).toEqual(new Bignum(3000)) - expect(rows[1].balance).toEqual(new Bignum(2000)) - expect(rows[2].balance).toEqual(new Bignum(1000)) - }) - - it('should be ok with params', () => { - const { count, rows } = repository.top({ offset: 1, limit: 2 }) - - expect(count).toBe(3) - expect(rows.length).toBe(2) - expect(rows[0].balance).toEqual(new Bignum(2000)) - expect(rows[1].balance).toEqual(new Bignum(1000)) - }) - - it('should be ok with params (offset = 0)', () => { - const { count, rows } = repository.top({ offset: 0, limit: 2 }) - - expect(count).toBe(3) - expect(rows.length).toBe(2) - expect(rows[0].balance).toEqual(new Bignum(3000)) - expect(rows[1].balance).toEqual(new Bignum(2000)) - }) - - it('should be ok with params (no offset)', () => { - const { count, rows } = repository.top({ limit: 2 }) - - expect(count).toBe(3) - expect(rows.length).toBe(2) - expect(rows[0].balance).toEqual(new Bignum(3000)) - expect(rows[1].balance).toEqual(new Bignum(2000)) - }) - - it('should be ok with params (no limit)', () => { - const { count, rows } = repository.top({ offset: 1 }) - - expect(count).toBe(3) - expect(rows.length).toBe(2) - expect(rows[0].balance).toEqual(new Bignum(2000)) - expect(rows[1].balance).toEqual(new Bignum(1000)) - }) - - it('should be ok with legacy', () => { - const { count, rows } = repository.top({}, true) - - expect(count).toBe(3) - expect(rows.length).toBe(3) - expect(rows[0].balance).toEqual(new Bignum(3000)) - expect(rows[1].balance).toEqual(new Bignum(2000)) - expect(rows[2].balance).toEqual(new Bignum(1000)) - }) - }) - - describe('search', () => { - const expectSearch = (params, rows = 1, count = 1) => { - const wallets = repository.search(params) - expect(wallets).toBeObject() - - expect(wallets).toHaveProperty('count') - expect(wallets.count).toBeNumber() - expect(wallets.count).toBe(count) - - expect(wallets).toHaveProperty('rows') - expect(wallets.rows).toBeArray() - expect(wallets.rows).not.toBeEmpty() - - expect(wallets.count).toBe(rows) - } - - it('should be a function', () => { - expect(repository.search).toBeFunction() - }) - - it('should search wallets by the specified address', () => { - const wallets = generateFullWallets() - walletManager.index(wallets) - - expectSearch({ address: wallets[0].address }) - }) - - it('should search wallets by the specified publicKey', () => { - const wallets = generateFullWallets() - walletManager.index(wallets) - - expectSearch({ publicKey: wallets[0].publicKey }) - }) - - it('should search wallets by the specified secondPublicKey', () => { - const wallets = generateFullWallets() - walletManager.index(wallets) - - expectSearch({ secondPublicKey: wallets[0].secondPublicKey }) - }) - - it('should search wallets by the specified vote', () => { - const wallets = generateFullWallets() - walletManager.index(wallets) - - expectSearch({ vote: wallets[0].vote }) - }) - - it('should search wallets by the specified username', () => { - const wallets = generateFullWallets() - walletManager.index(wallets) - - expectSearch({ username: wallets[0].username }) - }) - - it('should search wallets by the specified closed inverval (included) of balance', () => { - const wallets = generateFullWallets() - wallets.forEach((wallet, i) => { - if (i < 13) { - wallet.balance = 53 - } else if (i < 36) { - wallet.balance = 99 - } - }) - walletManager.index(wallets) - - expectSearch( - { - balance: { - from: 53, - to: 99, - }, - }, - 36, - 36, - ) - }) - - it('should search wallets by the specified closed interval (included) of voteBalance', () => { - const wallets = generateFullWallets() - wallets.forEach((wallet, i) => { - if (i < 17) { - wallet.voteBalance = 12 - } else if (i < 29) { - wallet.voteBalance = 17 - } - }) - walletManager.index(wallets) - - expectSearch( - { - voteBalance: { - from: 11, - to: 18, - }, - }, - 29, - 29, - ) - }) - }) -}) diff --git a/packages/core-database/__tests__/repositories/wallets.test.ts b/packages/core-database/__tests__/repositories/wallets.test.ts new file mode 100644 index 0000000000..2ddea64513 --- /dev/null +++ b/packages/core-database/__tests__/repositories/wallets.test.ts @@ -0,0 +1,405 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { Bignum, crypto, models } from "@arkecosystem/crypto"; +import compact from "lodash/compact"; +import uniq from "lodash/uniq"; +import genesisBlockTestnet from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; +import { setUp, tearDown } from "../__support__/setup"; + +import { WalletsRepository } from "../../src"; +import { DatabaseService } from "../../src/database-service"; + +const { Block, Wallet } = models; + +let genesisBlock; +let genesisSenders; +let repository; +let walletManager: Database.IWalletManager; +let databaseService: Database.IDatabaseService; + +beforeAll(async done => { + await setUp(); + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(genesisBlockTestnet); + genesisSenders = uniq(compact(genesisBlock.transactions.map(tx => tx.senderPublicKey))); + + done(); +}); + +afterAll(async done => { + await tearDown(); + + done(); +}); + +beforeEach(async done => { + const { WalletManager } = require("../../src/wallet-manager"); + walletManager = new WalletManager(); + + repository = new WalletsRepository(() => databaseService); + + databaseService = new DatabaseService(null, null, walletManager, repository, null); + + done(); +}); + +function generateWallets() { + return genesisSenders.map(senderPublicKey => ({ + address: crypto.getAddress(senderPublicKey), + })); +} + +function generateVotes() { + return genesisSenders.map(senderPublicKey => ({ + address: crypto.getAddress(senderPublicKey), + vote: genesisBlock.transactions[0].senderPublicKey, + })); +} + +function generateFullWallets() { + return genesisSenders.map(senderPublicKey => { + const address = crypto.getAddress(senderPublicKey); + + return { + address, + publicKey: `publicKey-${address}`, + secondPublicKey: `secondPublicKey-${address}`, + vote: `vote-${address}`, + username: `username-${address}`, + balance: 100, + voteBalance: 200, + }; + }); +} + +describe("Wallet Repository", () => { + describe("all", () => { + it("should return the local wallets of the connection", () => { + jest.spyOn(walletManager, 'allByAddress').mockReturnValue(null); + + repository.all(); + + expect(walletManager.allByAddress).toHaveBeenCalled(); + }); + }); + + describe("findAll", () => { + it("should be ok without params", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll(); + expect(count).toBe(52); + expect(rows).toHaveLength(52); + }); + + it("should be ok with params", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ offset: 10, limit: 10 }); + expect(count).toBe(52); + expect(rows).toHaveLength(10); + }); + + it("should be ok with params (no offset)", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ limit: 10 }); + expect(count).toBe(52); + expect(rows).toHaveLength(10); + }); + + it("should be ok with params (offset = 0)", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ offset: 0, limit: 12 }); + expect(count).toBe(52); + expect(rows).toHaveLength(12); + }); + + it("should be ok with params (no limit)", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + const { count, rows } = repository.findAll({ offset: 10 }); + expect(count).toBe(52); + expect(rows).toHaveLength(42); + }); + }); + + describe("findAllByVote", () => { + const vote = "dummy-sender-public-key"; + + beforeEach(() => { + const wallets = generateVotes(); + wallets.forEach((wallet, i) => { + if (i < 17) { + wallet.vote = vote; + } + }); + walletManager.index(wallets); + }); + + it("should be ok without params", () => { + const { count, rows } = repository.findAllByVote(vote); + expect(count).toBe(17); + expect(rows).toHaveLength(17); + }); + + it("should be ok with params", () => { + const { count, rows } = repository.findAllByVote(vote, { + offset: 10, + limit: 10, + }); + expect(count).toBe(17); + expect(rows).toHaveLength(7); + }); + + it("should be ok with params (no offset)", () => { + const { count, rows } = repository.findAllByVote(vote, { limit: 10 }); + expect(count).toBe(17); + expect(rows).toHaveLength(10); + }); + + it("should be ok with params (offset = 0)", () => { + const { count, rows } = repository.findAllByVote(vote, { + offset: 0, + limit: 1, + }); + expect(count).toBe(17); + expect(rows).toHaveLength(1); + }); + + it("should be ok with params (no limit)", () => { + const { count, rows } = repository.findAllByVote(vote, { offset: 30 }); + expect(count).toBe(17); + expect(rows).toHaveLength(0); + }); + }); + + describe("findById", () => { + const expectWallet = key => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + const wallet = repository.findById(wallets[0][key]); + expect(wallet).toBeObject(); + expect(wallet.address).toBe(wallets[0].address); + expect(wallet.publicKey).toBe(wallets[0].publicKey); + expect(wallet.username).toBe(wallets[0].username); + }; + + it("should be ok with an address", () => { + expectWallet("address"); + }); + + it("should be ok with a publicKey", () => { + expectWallet("publicKey"); + }); + + it("should be ok with a username", () => { + expectWallet("username"); + }); + }); + + describe("count", () => { + it("should be ok", () => { + const wallets = generateWallets(); + walletManager.index(wallets); + + expect(repository.count()).toBe(52); + }); + }); + + describe("top", () => { + + beforeEach(() => { + [ + { address: 'dummy-1', balance: new Bignum(1000) }, + { address: 'dummy-2', balance: new Bignum(2000) }, + { address: 'dummy-3', balance: new Bignum(3000) }, + ].forEach(o => { + const wallet = new Wallet(o.address); + wallet.balance = o.balance; + walletManager.reindex(wallet); + }); + }); + + it("should be ok without params", () => { + const { count, rows } = repository.top(); + + expect(count).toBe(3); + expect(rows.length).toBe(3); + expect(rows[0].balance).toEqual(new Bignum(3000)); + expect(rows[1].balance).toEqual(new Bignum(2000)); + expect(rows[2].balance).toEqual(new Bignum(1000)); + }); + + it("should be ok with params", () => { + const { count, rows } = repository.top({ offset: 1, limit: 2 }); + + expect(count).toBe(3); + expect(rows.length).toBe(2); + expect(rows[0].balance).toEqual(new Bignum(2000)); + expect(rows[1].balance).toEqual(new Bignum(1000)); + }); + + it("should be ok with params (offset = 0)", () => { + const { count, rows } = repository.top({ offset: 0, limit: 2 }); + + expect(count).toBe(3); + expect(rows.length).toBe(2); + expect(rows[0].balance).toEqual(new Bignum(3000)); + expect(rows[1].balance).toEqual(new Bignum(2000)); + }); + + it("should be ok with params (no offset)", () => { + const { count, rows } = repository.top({ limit: 2 }); + + expect(count).toBe(3); + expect(rows.length).toBe(2); + expect(rows[0].balance).toEqual(new Bignum(3000)); + expect(rows[1].balance).toEqual(new Bignum(2000)); + }); + + it("should be ok with params (no limit)", () => { + const { count, rows } = repository.top({ offset: 1 }); + + expect(count).toBe(3); + expect(rows.length).toBe(2); + expect(rows[0].balance).toEqual(new Bignum(2000)); + expect(rows[1].balance).toEqual(new Bignum(1000)); + }); + + it("should be ok with legacy", () => { + const { count, rows } = repository.top({}, true); + + expect(count).toBe(3); + expect(rows.length).toBe(3); + expect(rows[0].balance).toEqual(new Bignum(3000)); + expect(rows[1].balance).toEqual(new Bignum(2000)); + expect(rows[2].balance).toEqual(new Bignum(1000)); + }); + }); + + describe("search", () => { + const expectSearch = (params, rows = 1, count = 1) => { + const wallets = repository.search(params); + expect(wallets).toBeObject(); + + expect(wallets).toHaveProperty("count"); + expect(wallets.count).toBeNumber(); + expect(wallets.count).toBe(count); + + expect(wallets).toHaveProperty("rows"); + expect(wallets.rows).toBeArray(); + expect(wallets.rows).not.toBeEmpty(); + + expect(wallets.count).toBe(rows); + }; + + it("should search wallets by the specified address", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + expectSearch({ address: wallets[0].address }); + }); + + it("should search wallets by several addresses", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + const addresses = [wallets[1].address, wallets[3].address, wallets[9].address]; + expectSearch({ addresses }, 3, 3); + }); + + describe("when searching by `address` and `addresses`", () => { + it("should search wallets only by `address`", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + const { address } = wallets[0]; + const addresses = [wallets[1].address, wallets[3].address, wallets[9].address]; + expectSearch({ address, addresses }, 1, 1); + }); + }); + + it("should search wallets by the specified publicKey", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + expectSearch({ publicKey: wallets[0].publicKey }); + }); + + it("should search wallets by the specified secondPublicKey", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + expectSearch({ secondPublicKey: wallets[0].secondPublicKey }); + }); + + it("should search wallets by the specified vote", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + expectSearch({ vote: wallets[0].vote }); + }); + + it("should search wallets by the specified username", () => { + const wallets = generateFullWallets(); + walletManager.index(wallets); + + expectSearch({ username: wallets[0].username }); + }); + + it("should search wallets by the specified closed inverval (included) of balance", () => { + const wallets = generateFullWallets(); + wallets.forEach((wallet, i) => { + if (i < 13) { + wallet.balance = 53; + } else if (i < 36) { + wallet.balance = 99; + } + }); + walletManager.index(wallets); + + expectSearch( + { + balance: { + from: 53, + to: 99, + }, + }, + 36, + 36, + ); + }); + + it("should search wallets by the specified closed interval (included) of voteBalance", () => { + const wallets = generateFullWallets(); + wallets.forEach((wallet, i) => { + if (i < 17) { + wallet.voteBalance = 12; + } else if (i < 29) { + wallet.voteBalance = 17; + } + }); + walletManager.index(wallets); + + expectSearch( + { + voteBalance: { + from: 11, + to: 18, + }, + }, + 29, + 29, + ); + }); + }); +}); diff --git a/packages/core-database/__tests__/wallet-manager.test.js b/packages/core-database/__tests__/wallet-manager.test.js deleted file mode 100644 index b033e223ed..0000000000 --- a/packages/core-database/__tests__/wallet-manager.test.js +++ /dev/null @@ -1,551 +0,0 @@ -/* eslint max-len: "off" */ - -const { Block, Transaction, Wallet } = require('@arkecosystem/crypto').models -const { Bignum, crypto, transactionBuilder } = require('@arkecosystem/crypto') -const { - ARKTOSHI, - TRANSACTION_TYPES, -} = require('@arkecosystem/crypto').constants - -const genTransfer = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const genDelegateReg = require('@arkecosystem/core-test-utils/lib/generators/transactions/delegate') -const gen2ndSignature = require('@arkecosystem/core-test-utils/lib/generators/transactions/signature') -const genvote = require('@arkecosystem/core-test-utils/lib/generators/transactions/vote') -const block3 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.2-100')[1] -const app = require('./__support__/setup') - -const block = new Block(block3) -const walletData1 = require('./__fixtures__/wallets.json')[0] -const walletData2 = require('./__fixtures__/wallets.json')[1] - -let genesisBlock // eslint-disable-line no-unused-vars -let walletManager - -beforeAll(async done => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - - walletManager = new (require('../lib/wallet-manager'))() - - done() -}) - -beforeEach(() => { - walletManager = new (require('../lib/wallet-manager'))() -}) - -afterAll(async done => { - await app.tearDown() - - done() -}) - -describe('Wallet Manager', () => { - it('should be an object', () => { - expect(walletManager).toBeObject() - }) - - describe('reset', () => { - it('should be a function', () => { - expect(walletManager.reset).toBeFunction() - }) - - it('should reset the index', () => { - const wallet = new Wallet(walletData1.address) - - walletManager.reindex(wallet) - expect(walletManager.all()).toEqual([wallet]) - - walletManager.reset() - expect(walletManager.all()).toEqual([]) - }) - }) - - describe('reindex', () => { - it('should be a function', () => { - expect(walletManager.reindex).toBeFunction() - }) - - it('should index the wallets', () => { - const wallet = new Wallet(walletData1.address) - - expect(walletManager.all()).toEqual([]) - - walletManager.reindex(wallet) - expect(walletManager.all()).toEqual([wallet]) - }) - }) - - describe('applyBlock', () => { - let delegateMock - let block2 - - const delegatePublicKey = block3.generatorPublicKey // '0299deebff24ebf2bb53ad78f3ea3ada5b3c8819132e191b02c263ee4aa4af3d9b' - - const txs = [] - for (let i = 0; i < 3; i++) { - txs[i] = transactionBuilder - .vote() - .sign(Math.random().toString(36)) - .votesAsset([`+${delegatePublicKey}`]) - .build() - } - - beforeEach(() => { - delegateMock = { applyBlock: jest.fn(), publicKey: delegatePublicKey } - walletManager.findByPublicKey = jest.fn(() => delegateMock) - walletManager.applyTransaction = jest.fn() - walletManager.revertTransaction = jest.fn() - - const { data } = block - data.transactions = [] - data.transactions.push(txs[0]) - data.transactions.push(txs[1]) - data.transactions.push(txs[2]) - block2 = new Block(data) - - walletManager.reindex(delegateMock) - }) - - it('should be a function', () => { - expect(walletManager.applyBlock).toBeFunction() - }) - - it('should apply sequentially the transactions of the block', async () => { - await walletManager.applyBlock(block2) - - block2.transactions.forEach((transaction, i) => { - expect(walletManager.applyTransaction.mock.calls[i][0]).toBe( - block2.transactions[i], - ) - }) - }) - - it('should apply the block data to the delegate', async () => { - await walletManager.applyBlock(block) - - expect(delegateMock.applyBlock).toHaveBeenCalledWith(block.data) - }) - - describe('when 1 transaction fails while applying it', () => { - it('should revert sequentially (from last to first) all the transactions of the block', async () => { - walletManager.applyTransaction = jest.fn(transaction => { - if (transaction === block2.transactions[2]) { - throw new Error('Fake error') - } - }) - - expect(block2.transactions.length).toBe(3) - - try { - await walletManager.applyBlock(block2) - - expect(null).toBe('this should fail if no error is thrown') - } catch (_error) { - expect(walletManager.revertTransaction).toHaveBeenCalledTimes(2) - block2.transactions.slice(0, 1).forEach((transaction, i) => { - expect( - walletManager.revertTransaction.mock.calls[1 - i][0], - ).toEqual(block2.transactions[i]) - }) - } - }) - - it('throws the Error', async () => { - walletManager.applyTransaction = jest.fn(transaction => { - throw new Error('Fake error') - }) - - try { - await walletManager.applyBlock(block2) - - expect(null).toBe('this should fail if no error is thrown') - } catch (error) { - expect(error).toBeInstanceOf(Error) - expect(error.message).toBe('Fake error') - } - }) - }) - - describe.skip('the delegate of the block is not indexed', () => { - describe('not genesis block', () => { - it('throw an Error', () => {}) - }) - - describe('genesis block', () => { - it('generates a new wallet', () => {}) - }) - }) - }) - - describe.skip('revertBlock', () => { - it('should be a function', () => { - expect(walletManager.revertBlock).toBeFunction() - }) - - it('should revert all transactions of the block', () => {}) - - it('should revert the block of the delegate', () => {}) - }) - - describe('applyTransaction', () => { - it('should be a function', () => { - expect(walletManager.applyTransaction).toBeFunction() - }) - - describe('when the recipient is a cold wallet', () => {}) - - const transfer = genTransfer( - 'testnet', - Math.random().toString(36), - walletData2.address, - 96579, - 1, - )[0] - const delegateReg = genDelegateReg( - 'testnet', - Math.random().toString(36), - 1, - )[0] - const secondSign = gen2ndSignature( - 'testnet', - Math.random().toString(36), - 1, - )[0] - const vote = genvote( - 'testnet', - Math.random().toString(36), - walletData2.publicKey, - 1, - )[0] - describe.each` - type | transaction | amount | balanceSuccess | balanceFail - ${'transfer'} | ${transfer} | ${new Bignum(96579)} | ${new Bignum(1 * ARKTOSHI)} | ${Bignum.ONE} - ${'delegate'} | ${delegateReg} | ${Bignum.ZERO} | ${new Bignum(30 * ARKTOSHI)} | ${Bignum.ONE} - ${'2nd sign'} | ${secondSign} | ${Bignum.ZERO} | ${new Bignum(10 * ARKTOSHI)} | ${Bignum.ONE} - ${'vote'} | ${vote} | ${Bignum.ZERO} | ${new Bignum(5 * ARKTOSHI)} | ${Bignum.ONE} - `( - 'when the transaction is a $type', - ({ type, transaction, amount, balanceSuccess, balanceFail }) => { - let sender - let recipient - - beforeEach(() => { - sender = new Wallet(walletData1.address) - recipient = new Wallet(walletData2.address) - recipient.publicKey = walletData2.publicKey - - sender.publicKey = transaction.senderPublicKey - - walletManager.reindex(sender) - walletManager.reindex(recipient) - - walletManager.__isDelegate = jest.fn(() => true) // for vote transaction - }) - - it('should apply the transaction to the sender & recipient', async () => { - sender.balance = balanceSuccess - - expect(+sender.balance.toFixed()).toBe(+balanceSuccess) - expect(+recipient.balance.toFixed()).toBe(0) - - await walletManager.applyTransaction(transaction) - - expect(sender.balance).toEqual( - balanceSuccess.minus(amount).minus(transaction.fee), - ) - - if (type === 'transfer') { - expect(recipient.balance).toEqual(amount) - } - }) - - it('should fail if the transaction cannot be applied', async () => { - sender.balance = balanceFail - - expect(+sender.balance.toFixed()).toBe(+balanceFail) - expect(+recipient.balance.toFixed()).toBe(0) - - try { - expect(async () => { - await walletManager.applyTransaction(transaction) - }).toThrow(/apply transaction/) - - expect(null).toBe('this should fail if no error is thrown') - } catch (error) { - expect(+sender.balance.toFixed()).toBe(+balanceFail) - expect(+recipient.balance.toFixed()).toBe(0) - } - }) - }, - ) - }) - - describe('revertTransaction', () => { - it('should be a function', () => { - expect(walletManager.revertTransaction).toBeFunction() - }) - - it('should revert the transaction from the sender & recipient', async () => { - const transaction = new Transaction({ - type: TRANSACTION_TYPES.TRANSFER, - amount: 245098000000000, - fee: 0, - recipientId: 'AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri', - timestamp: 0, - asset: {}, - senderPublicKey: - '035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788', - signature: - '304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d', - id: 'db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd', - senderId: 'APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn', - }) - - const sender = walletManager.findByPublicKey( - transaction.data.senderPublicKey, - ) - const recipient = walletManager.findByAddress( - transaction.data.recipientId, - ) - recipient.balance = transaction.data.amount - - expect(sender.balance).toEqual(Bignum.ZERO) - expect(recipient.balance).toEqual(transaction.data.amount) - - await walletManager.revertTransaction(transaction) - - expect(sender.balance).toEqual(transaction.data.amount) - expect(recipient.balance).toEqual(Bignum.ZERO) - }) - }) - - describe('findByAddress', () => { - it('should be a function', () => { - expect(walletManager.findByAddress).toBeFunction() - }) - - it('should index it by address', () => { - const wallet = new Wallet(walletData1.address) - - walletManager.reindex(wallet) - expect(walletManager.byAddress[wallet.address]).toBe(wallet) - }) - - it('should return it by address', () => { - const wallet = new Wallet(walletData1.address) - - walletManager.reindex(wallet) - expect(walletManager.findByAddress(wallet.address).address).toBe( - wallet.address, - ) - }) - }) - - describe('findByPublicKey', () => { - it('should be a function', () => { - expect(walletManager.findByPublicKey).toBeFunction() - }) - - it('should index it by publicKey', () => { - const wallet = new Wallet(walletData1.address) - wallet.publicKey = walletData1.publicKey - - walletManager.reindex(wallet) - expect(walletManager.byPublicKey[wallet.publicKey]).toBe(wallet) - }) - - it('should return it by publicKey', () => { - const wallet = new Wallet(walletData1.address) - wallet.publicKey = 'dummy-public-key' - - walletManager.reindex(wallet) - expect(walletManager.findByPublicKey(wallet.publicKey).publicKey).toBe( - wallet.publicKey, - ) - }) - }) - - describe('findByUsername', () => { - it('should be a function', () => { - expect(walletManager.findByUsername).toBeFunction() - }) - - it('should index it by username', () => { - const wallet = new Wallet(walletData1.address) - wallet.username = 'dummy-username' - - walletManager.reindex(wallet) - expect(walletManager.byUsername[wallet.username]).toBe(wallet) - }) - - it('should return it by username', () => { - const wallet = new Wallet(walletData1.address) - wallet.username = 'dummy-username' - - walletManager.reindex(wallet) - expect(walletManager.findByUsername(wallet.username).username).toBe( - wallet.username, - ) - }) - }) - - describe('all', () => { - it('should be a function', () => { - expect(walletManager.all).toBeFunction() - }) - - it('should return indexed', () => { - const wallet1 = new Wallet(walletData1.address) - walletManager.reindex(wallet1) - - const wallet2 = new Wallet(walletData2.address) - walletManager.reindex(wallet2) - - expect(walletManager.all()).toEqual([wallet1, wallet2]) - }) - }) - - describe('__canBePurged', () => { - it('should be removed if all criteria are satisfied', async () => { - const wallet = new Wallet(walletData1.address) - - expect(walletManager.__canBePurged(wallet)).toBeTrue() - }) - - it('should not be removed if wallet.secondPublicKey is set', async () => { - const wallet = new Wallet(walletData1.address) - wallet.secondPublicKey = 'secondPublicKey' - - expect(wallet.secondPublicKey).toBe('secondPublicKey') - expect(walletManager.__canBePurged(wallet)).toBeFalse() - }) - - it('should not be removed if wallet.multisignature is set', async () => { - const wallet = new Wallet(walletData1.address) - wallet.multisignature = 'multisignature' - - expect(wallet.multisignature).toBe('multisignature') - expect(walletManager.__canBePurged(wallet)).toBeFalse() - }) - - it('should not be removed if wallet.username is set', async () => { - const wallet = new Wallet(walletData1.address) - wallet.username = 'username' - - expect(wallet.username).toBe('username') - expect(walletManager.__canBePurged(wallet)).toBeFalse() - }) - }) - - describe('purgeEmptyNonDelegates', () => { - it('should be a function', () => { - expect(walletManager.purgeEmptyNonDelegates).toBeFunction() - }) - - it('should be purged if all criteria are satisfied', async () => { - const wallet1 = new Wallet(walletData1.address) - wallet1.publicKey = 'dummy-1-publicKey' - walletManager.reindex(wallet1) - - const wallet2 = new Wallet(walletData2.address) - wallet2.username = 'username' - - walletManager.reindex(wallet2) - - walletManager.purgeEmptyNonDelegates() - - expect(walletManager.all()).toEqual([wallet2]) - }) - - it('should not be purged if wallet.secondPublicKey is set', async () => { - const wallet1 = new Wallet(walletData1.address) - wallet1.publicKey = 'dummy-1-publicKey' - wallet1.secondPublicKey = 'dummy-1-secondPublicKey' - walletManager.reindex(wallet1) - - const wallet2 = new Wallet(walletData2.address) - wallet2.username = 'username' - - walletManager.reindex(wallet2) - - walletManager.purgeEmptyNonDelegates() - - expect(walletManager.all()).toEqual([wallet1, wallet2]) - }) - - it('should not be purged if wallet.multisignature is set', async () => { - const wallet1 = new Wallet(walletData1.address) - wallet1.publicKey = 'dummy-1-publicKey' - wallet1.multisignature = 'dummy-1-multisignature' - walletManager.reindex(wallet1) - - const wallet2 = new Wallet(walletData2.address) - wallet2.username = 'username' - - walletManager.reindex(wallet2) - - walletManager.purgeEmptyNonDelegates() - - expect(walletManager.all()).toEqual([wallet1, wallet2]) - }) - - it('should not be purged if wallet.username is set', async () => { - const wallet1 = new Wallet(walletData1.address) - wallet1.publicKey = 'dummy-1-publicKey' - wallet1.username = 'dummy-1-username' - walletManager.reindex(wallet1) - - const wallet2 = new Wallet(walletData2.address) - wallet2.username = 'username' - - walletManager.reindex(wallet2) - - walletManager.purgeEmptyNonDelegates() - - expect(walletManager.all()).toEqual([wallet1, wallet2]) - }) - }) - - describe('buildVoteBalances', () => { - it('should be a function', () => { - expect(walletManager.buildVoteBalances).toBeFunction() - }) - - it('should update vote balance of delegates', async () => { - for (let i = 0; i < 5; i++) { - const delegateKey = i.toString().repeat(66) - const delegate = { - address: crypto.getAddress(delegateKey), - publicKey: delegateKey, - username: `delegate${i}`, - voteBalance: Bignum.ZERO, - } - - const voter = { - address: crypto.getAddress((i + 5).toString().repeat(66)), - balance: new Bignum((i + 1) * 1000 * ARKTOSHI), - publicKey: `v${delegateKey}`, - vote: delegateKey, - } - - walletManager.index([delegate, voter]) - } - - walletManager.buildVoteBalances() - - const delegates = walletManager.allByUsername() - for (let i = 0; i < 5; i++) { - const delegate = delegates[4 - i] - expect(delegate.voteBalance).toEqual( - new Bignum((5 - i) * 1000 * ARKTOSHI), - ) - } - }) - }) -}) diff --git a/packages/core-database/__tests__/wallet-manager.test.ts b/packages/core-database/__tests__/wallet-manager.test.ts new file mode 100644 index 0000000000..b28f908cc9 --- /dev/null +++ b/packages/core-database/__tests__/wallet-manager.test.ts @@ -0,0 +1,430 @@ +/* tslint:disable:max-line-length no-empty */ +import { Database } from "@arkecosystem/core-interfaces"; +import { fixtures, generators } from "@arkecosystem/core-test-utils"; +import { Bignum, constants, crypto, models, transactionBuilder } from "@arkecosystem/crypto"; +import { IMultiSignatureAsset } from "@arkecosystem/crypto/dist/models"; +import genesisBlockTestnet from "../../core-test-utils/src/config/testnet/genesisBlock.json"; +import wallets from "./__fixtures__/wallets.json"; +import { setUp, tearDown } from "./__support__/setup"; + +const { Block, Transaction, Wallet } = models; +const { ARKTOSHI, TransactionTypes } = constants; + +const { generateDelegateRegistration, generateSecondSignature, generateTransfers, generateVote } = generators; + +const block3 = fixtures.blocks2to100[1]; +const block = new Block(block3); + +const walletData1 = wallets[0]; +const walletData2 = wallets[1]; + +let genesisBlock; +let walletManager : Database.IWalletManager; + +beforeAll(async done => { + await setUp(); + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(genesisBlockTestnet); + + const { WalletManager } = require("../src/wallet-manager"); + walletManager = new WalletManager(); + + done(); +}); + +beforeEach(() => { + const { WalletManager } = require("../src/wallet-manager"); + walletManager = new WalletManager(); +}); + +afterAll(async done => { + await tearDown(); + + done(); +}); + +describe("Wallet Manager", () => { + describe("reset", () => { + it("should reset the index", () => { + const wallet = new Wallet(walletData1.address); + + walletManager.reindex(wallet); + expect(walletManager.allByAddress()).toEqual([wallet]); + + walletManager.reset(); + expect(walletManager.allByAddress()).toEqual([]); + }); + }); + + describe("reindex", () => { + it("should index the wallets", () => { + const wallet = new Wallet(walletData1.address); + + expect(walletManager.allByAddress()).toEqual([]); + + walletManager.reindex(wallet); + expect(walletManager.allByAddress()).toEqual([wallet]); + }); + }); + + describe("applyBlock", () => { + let delegateMock; + let block2; + + const delegatePublicKey = block3.generatorPublicKey; // '0299deebff24ebf2bb53ad78f3ea3ada5b3c8819132e191b02c263ee4aa4af3d9b' + + const txs = []; + for (let i = 0; i < 3; i++) { + txs[i] = transactionBuilder + .vote() + .sign(Math.random().toString(36)) + .votesAsset([`+${delegatePublicKey}`]) + .build(); + } + + beforeEach(() => { + delegateMock = { applyBlock: jest.fn(), publicKey: delegatePublicKey }; + jest.spyOn(walletManager, 'findByPublicKey').mockReturnValue(delegateMock); + jest.spyOn(walletManager, 'applyTransaction').mockImplementation(); + jest.spyOn(walletManager, 'revertTransaction').mockImplementation(); + + const { data } = block; + data.transactions = []; + data.transactions.push(txs[0]); + data.transactions.push(txs[1]); + data.transactions.push(txs[2]); + block2 = new Block(data); + + walletManager.reindex(delegateMock); + }); + + it("should apply sequentially the transactions of the block", async () => { + await walletManager.applyBlock(block2); + + block2.transactions.forEach((transaction, i) => { + expect(walletManager.applyTransaction).toHaveBeenNthCalledWith(i+1, block2.transactions[i]) + }); + }); + + it("should apply the block data to the delegate", async () => { + await walletManager.applyBlock(block); + + expect(delegateMock.applyBlock).toHaveBeenCalledWith(block.data); + }); + + describe("when 1 transaction fails while applying it", () => { + it("should revert sequentially (from last to first) all the transactions of the block", async () => { + jest.spyOn(walletManager, 'applyTransaction').mockImplementation( (tx) => { + if (tx === block2.transactions[2]) { + throw new Error("Fake error"); + } + }); + + expect(block2.transactions.length).toBe(3); + + try { + await walletManager.applyBlock(block2); + + expect(null).toBe("this should fail if no error is thrown"); + } catch (error) { + expect(walletManager.revertTransaction).toHaveBeenCalledTimes(2); + block2.transactions.slice(0, 1).forEach((transaction, i, total) => { + expect(walletManager.revertTransaction).toHaveBeenNthCalledWith(total.length+1 - i, block2.transactions[i]); + }); + } + }); + + it("throws the Error", async () => { + walletManager.applyTransaction = jest.fn(tx => { + throw new Error("Fake error"); + }); + + try { + await walletManager.applyBlock(block2); + + expect(null).toBe("this should fail if no error is thrown"); + } catch (error) { + expect(error).toBeInstanceOf(Error); + expect(error.message).toBe("Fake error"); + } + }); + }); + + describe.skip("the delegate of the block is not indexed", () => { + describe("not genesis block", () => { + it("throw an Error", () => {}); + }); + + describe("genesis block", () => { + it("generates a new wallet", () => {}); + }); + }); + }); + + describe.skip("revertBlock", () => { + it("should revert all transactions of the block", () => {}); + + it("should revert the block of the delegate", () => {}); + }); + + describe("applyTransaction", () => { + describe("when the recipient is a cold wallet", () => {}); + + const transfer = generateTransfers("testnet", Math.random().toString(36), walletData2.address, 96579, 1)[0]; + const delegateReg = generateDelegateRegistration("testnet", Math.random().toString(36), 1)[0]; + const secondSign = generateSecondSignature("testnet", Math.random().toString(36), 1)[0]; + const vote = generateVote("testnet", Math.random().toString(36), walletData2.publicKey, 1)[0]; + describe.each` + type | transaction | amount | balanceSuccess | balanceFail + ${"transfer"} | ${transfer} | ${new Bignum(96579)} | ${new Bignum(ARKTOSHI)} | ${Bignum.ONE} + ${"delegate"} | ${delegateReg} | ${Bignum.ZERO} | ${new Bignum(30 * ARKTOSHI)} | ${Bignum.ONE} + ${"2nd sign"} | ${secondSign} | ${Bignum.ZERO} | ${new Bignum(10 * ARKTOSHI)} | ${Bignum.ONE} + ${"vote"} | ${vote} | ${Bignum.ZERO} | ${new Bignum(5 * ARKTOSHI)} | ${Bignum.ONE} + `("when the transaction is a $type", ({ type, transaction, amount, balanceSuccess, balanceFail }) => { + let sender; + let recipient; + + beforeEach(() => { + sender = new Wallet(walletData1.address); + recipient = new Wallet(walletData2.address); + recipient.publicKey = walletData2.publicKey; + + sender.publicKey = transaction.senderPublicKey; + + walletManager.reindex(sender); + walletManager.reindex(recipient); + + jest.spyOn(walletManager, 'isDelegate').mockReturnValue(true); + }); + + it("should apply the transaction to the sender & recipient", async () => { + sender.balance = balanceSuccess; + + expect(+sender.balance.toFixed()).toBe(+balanceSuccess); + expect(+recipient.balance.toFixed()).toBe(0); + + await walletManager.applyTransaction(transaction); + + expect(sender.balance).toEqual(balanceSuccess.minus(amount).minus(transaction.fee)); + + if (type === "transfer") { + expect(recipient.balance).toEqual(amount); + } + }); + + it("should fail if the transaction cannot be applied", async () => { + sender.balance = balanceFail; + + expect(+sender.balance.toFixed()).toBe(+balanceFail); + expect(+recipient.balance.toFixed()).toBe(0); + + try { + expect(async () => { + await walletManager.applyTransaction(transaction); + }).toThrow(/apply transaction/); + + expect(null).toBe("this should fail if no error is thrown"); + } catch (error) { + expect(+sender.balance.toFixed()).toBe(+balanceFail); + expect(+recipient.balance.toFixed()).toBe(0); + } + }); + }); + }); + + describe("revertTransaction", () => { + it("should revert the transaction from the sender & recipient", async () => { + const transaction = new Transaction({ + type: TransactionTypes.Transfer, + amount: new Bignum(245098000000000), + fee: 0, + recipientId: "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + timestamp: 0, + asset: {}, + senderPublicKey: "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + signature: + "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", + id: "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", + }); + + const sender = walletManager.findByPublicKey(transaction.data.senderPublicKey); + const recipient = walletManager.findByAddress(transaction.data.recipientId); + recipient.balance = new Bignum(transaction.data.amount); + + expect(sender.balance).toEqual(Bignum.ZERO); + expect(recipient.balance).toEqual(transaction.data.amount); + + await walletManager.revertTransaction(transaction); + + expect(sender.balance).toEqual(transaction.data.amount); + expect(recipient.balance).toEqual(Bignum.ZERO); + }); + }); + + describe("findByAddress", () => { + it("should return it by address", () => { + const wallet = new Wallet(walletData1.address); + + walletManager.reindex(wallet); + expect(walletManager.findByAddress(wallet.address).address).toBe(wallet.address); + }); + }); + + describe("findByPublicKey", () => { + it("should return it by publicKey", () => { + const wallet = new Wallet(walletData1.address); + wallet.publicKey = "dummy-public-key"; + + walletManager.reindex(wallet); + expect(walletManager.findByPublicKey(wallet.publicKey).publicKey).toBe(wallet.publicKey); + }); + }); + + describe("findByUsername", () => { + it("should return it by username", () => { + const wallet = new Wallet(walletData1.address); + wallet.username = "dummy-username"; + + walletManager.reindex(wallet); + expect(walletManager.findByUsername(wallet.username).username).toBe(wallet.username); + }); + }); + + describe("all", () => { + it("should return indexed", () => { + const wallet1 = new Wallet(walletData1.address); + walletManager.reindex(wallet1); + + const wallet2 = new Wallet(walletData2.address); + walletManager.reindex(wallet2); + + expect(walletManager.allByAddress()).toEqual([wallet1, wallet2]); + }); + }); + + describe("canBePurged", () => { + it("should be removed if all criteria are satisfied", async () => { + const wallet = new Wallet(walletData1.address); + + expect(walletManager.canBePurged(wallet)).toBeTrue(); + }); + + it("should not be removed if wallet.secondPublicKey is set", async () => { + const wallet = new Wallet(walletData1.address); + wallet.secondPublicKey = "secondPublicKey"; + + expect(wallet.secondPublicKey).toBe("secondPublicKey"); + expect(walletManager.canBePurged(wallet)).toBeFalse(); + }); + + it("should not be removed if wallet.multisignature is set", async () => { + const wallet = new Wallet(walletData1.address); + wallet.multisignature = {} as IMultiSignatureAsset; + + expect(wallet.multisignature).toEqual({}); + expect(walletManager.canBePurged(wallet)).toBeFalse(); + }); + + it("should not be removed if wallet.username is set", async () => { + const wallet = new Wallet(walletData1.address); + wallet.username = "username"; + + expect(wallet.username).toBe("username"); + expect(walletManager.canBePurged(wallet)).toBeFalse(); + }); + }); + + describe("purgeEmptyNonDelegates", () => { + it("should be purged if all criteria are satisfied", async () => { + const wallet1 = new Wallet(walletData1.address); + wallet1.publicKey = "dummy-1-publicKey"; + walletManager.reindex(wallet1); + + const wallet2 = new Wallet(walletData2.address); + wallet2.username = "username"; + + walletManager.reindex(wallet2); + + walletManager.purgeEmptyNonDelegates(); + + expect(walletManager.allByAddress()).toEqual([wallet2]); + }); + + it("should not be purged if wallet.secondPublicKey is set", async () => { + const wallet1 = new Wallet(walletData1.address); + wallet1.publicKey = "dummy-1-publicKey"; + wallet1.secondPublicKey = "dummy-1-secondPublicKey"; + walletManager.reindex(wallet1); + + const wallet2 = new Wallet(walletData2.address); + wallet2.username = "username"; + + walletManager.reindex(wallet2); + + walletManager.purgeEmptyNonDelegates(); + + expect(walletManager.allByAddress()).toEqual([wallet1, wallet2]); + }); + + it("should not be purged if wallet.multisignature is set", async () => { + const wallet1 = new Wallet(walletData1.address); + wallet1.publicKey = "dummy-1-publicKey"; + wallet1.multisignature = {} as IMultiSignatureAsset; + walletManager.reindex(wallet1); + + const wallet2 = new Wallet(walletData2.address); + wallet2.username = "username"; + + walletManager.reindex(wallet2); + + walletManager.purgeEmptyNonDelegates(); + + expect(walletManager.allByAddress()).toEqual([wallet1, wallet2]); + }); + + it("should not be purged if wallet.username is set", async () => { + const wallet1 = new Wallet(walletData1.address); + wallet1.publicKey = "dummy-1-publicKey"; + wallet1.username = "dummy-1-username"; + walletManager.reindex(wallet1); + + const wallet2 = new Wallet(walletData2.address); + wallet2.username = "username"; + + walletManager.reindex(wallet2); + + walletManager.purgeEmptyNonDelegates(); + + expect(walletManager.allByAddress()).toEqual([wallet1, wallet2]); + }); + }); + + describe("buildVoteBalances", () => { + it("should update vote balance of delegates", async () => { + for (let i = 0; i < 5; i++) { + const delegateKey = i.toString().repeat(66); + const delegate = new Wallet(crypto.getAddress(delegateKey)); + delegate.publicKey = delegateKey; + delegate.username = `delegate${i}`; + delegate.voteBalance = Bignum.ZERO; + + const voter = new Wallet(crypto.getAddress((i + 5).toString().repeat(66))); + voter.balance = new Bignum((i + 1) * 1000 * ARKTOSHI); + voter.publicKey = `v${delegateKey}`; + voter.vote = delegateKey; + + walletManager.index([delegate, voter]); + } + + walletManager.buildVoteBalances(); + + const delegates = walletManager.allByUsername(); + for (let i = 0; i < 5; i++) { + const delegate = delegates[4 - i]; + expect(delegate.voteBalance).toEqual(new Bignum((5 - i) * 1000 * ARKTOSHI)); + } + }); + }); +}); diff --git a/packages/core-database/jest.config.js b/packages/core-database/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-database/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-database/lib/defaults.js b/packages/core-database/lib/defaults.js deleted file mode 100644 index 09837607a6..0000000000 --- a/packages/core-database/lib/defaults.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - snapshots: `${process.env.ARK_PATH_DATA}/snapshots/${ - process.env.ARK_NETWORK_NAME - }`, -} diff --git a/packages/core-database/lib/index.js b/packages/core-database/lib/index.js deleted file mode 100644 index e2bb02bb2c..0000000000 --- a/packages/core-database/lib/index.js +++ /dev/null @@ -1,28 +0,0 @@ -const databaseManager = require('./manager') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'databaseManager', - async register(container, options) { - container.resolvePlugin('logger').info('Starting Database Manager') - - return databaseManager - }, -} - -/** - * The interface used by concrete implementations. - * @type {ConnectionInterface} - */ -exports.ConnectionInterface = require('./interface') - -/** - * The Wallet Manager. - * @type {WalletManager} - */ -exports.WalletManager = require('./wallet-manager') diff --git a/packages/core-database/lib/interface.js b/packages/core-database/lib/interface.js deleted file mode 100644 index b4980fe3d6..0000000000 --- a/packages/core-database/lib/interface.js +++ /dev/null @@ -1,596 +0,0 @@ -const { crypto, slots } = require('@arkecosystem/crypto') -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const logger = app.resolvePlugin('logger') -const emitter = app.resolvePlugin('event-emitter') -const { Block } = require('@arkecosystem/crypto').models -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants -const { roundCalculator } = require('@arkecosystem/core-utils') -const cloneDeep = require('lodash/cloneDeep') -const assert = require('assert') -const WalletManager = require('./wallet-manager') - -module.exports = class ConnectionInterface { - /** - * @constructor - * @param {Object} options - */ - constructor(options) { - this.config = options - this.connection = null - this.blocksInCurrentRound = null - this.stateStarted = false - - this.__registerListeners() - } - - /** - * Get the current connection. - * @return {ConnectionInterface} - */ - getConnection() { - return this.connection - } - - /** - * Connect to a database. - * @return {void} - * @throws Error - */ - async connect() { - throw new Error('Method [connect] not implemented!') - } - - /** - * Disconnect from a database. - * @return {void} - * @throws Error - */ - async disconnect() { - throw new Error('Method [disconnect] not implemented!') - } - - /** - * Verify the blockchain stored on db is not corrupted making simple assertions: - * - Last block is available - * - Last block height equals the number of stored blocks - * - Number of stored transactions equals the sum of block.numberOfTransactions in the database - * - Sum of all tx fees equals the sum of block.totalFee - * - Sum of all tx amount equals the sum of block.totalAmount - * @return {Object} An object { valid, errors } with the result of the verification and the errors - */ - async verifyBlockchain() { - throw new Error('Method [verifyBlockchain] not implemented!') - } - - /** - * Get the top 51 delegates. - * @param {Number} height - * @param {Array} delegates - * @return {void} - * @throws Error - */ - async getActiveDelegates(height, delegates) { - throw new Error('Method [getActiveDelegates] not implemented!') - } - - /** - * Load a list of wallets into memory. - * @param {Number} height - * @return {Boolean} success - * @throws Error - */ - async buildWallets(height) { - throw new Error('Method [buildWallets] not implemented!') - } - - /** - * Commit wallets from the memory. - * @param {Boolean} force - * @return {void} - * @throws Error - */ - async saveWallets(force) { - throw new Error('Method [saveWallets] not implemented!') - } - - /** - * Commit the given block. - * NOTE: to be used when node is in sync and committing newly received blocks - * @param {Block} block - * @return {void} - * @throws Error - */ - async saveBlock(block) { - throw new Error('Method [saveBlock] not implemented!') - } - - /** - * Queue a query to save the given block. - * NOTE: Must call commitQueuedQueries() to save to database. - * NOTE: to use when rebuilding to decrease the number of database transactions, - * and commit blocks (save only every 1000s for instance) by calling commit - * @param {Block} block - * @return {void} - * @throws Error - */ - enqueueSaveBlock(block) { - throw new Error('Method [enqueueSaveBlock] not implemented!') - } - - /** - * Queue a query to delete the given block. - * See also enqueueSaveBlock - * @param {Block} block - * @return {void} - * @throws Error - */ - enqueueDeleteBlock(block) { - throw new Error('Method [enqueueDeleteBlock] not implemented!') - } - - /** - * Queue a query to delete the round at given height. - * See also enqueueSaveBlock and enqueueDeleteBlock - * @param {Number} height - * @return {void} - * @throws Error - */ - enqueueDeleteRound(height) { - throw new Error('Method [enqueueDeleteRound] not implemented!') - } - - /** - * Commit all queued queries to the database. - * NOTE: to be used in combination with other enqueue-functions. - * @return {void} - * @throws Error - */ - async commitQueuedQueries() { - throw new Error('Method [commitQueuedQueries] not implemented!') - } - - /** - * Delete the given block. - * @param {Block} block - * @return {void} - * @throws Error - */ - async deleteBlock(block) { - throw new Error('Method [deleteBlock] not implemented!') - } - - /** - * Get a block. - * @param {Block} id - * @return {void} - * @throws Error - */ - async getBlock(id) { - throw new Error('Method [getBlock] not implemented!') - } - - /** - * Get last block. - * @return {void} - * @throws Error - */ - async getLastBlock() { - throw new Error('Method [getLastBlock] not implemented!') - } - - /** - * Get blocks for the given offset and limit. - * @param {Number} offset - * @param {Number} limit - * @return {void} - * @throws Error - */ - async getBlocks(offset, limit) { - throw new Error('Method [getBlocks] not implemented!') - } - - /** - * Get top count blocks ordered by height DESC. - * NOTE: Only used when trying to restore database integrity. - * The returned blocks may be unchained. - * @param {Number} count - * @return {void} - * @throws Error - */ - async getTopBlocks(count) { - throw new Error('Method [getTopBlocks] not implemented!') - } - - /** - * Get recent block ids. - * @return {[]String} - */ - async getRecentBlockIds() { - throw new Error('Method [getRecentBlockIds] not implemented!') - } - - /** - * Store the given round. - * @param {Array} activeDelegates - * @return {void} - * @throws Error - */ - async saveRound(activeDelegates) { - throw new Error('Method [saveRound] not implemented!') - } - - /** - * Delete the given round. - * @param {Number} round - * @return {void} - * @throws Error - */ - async deleteRound(round) { - throw new Error('Method [deleteRound] not implemented!') - } - - /** - * Update delegate statistics in memory. - * NOTE: must be called before saving new round of delegates - * @param {Block} block - * @param {Array} delegates - * @return {void} - */ - updateDelegateStats(height, delegates) { - if (!delegates || !this.blocksInCurrentRound) { - return - } - - logger.debug('Updating delegate statistics') - - try { - delegates.forEach(delegate => { - const producedBlocks = this.blocksInCurrentRound.filter( - blockGenerator => - blockGenerator.data.generatorPublicKey === delegate.publicKey, - ) - const wallet = this.walletManager.findByPublicKey(delegate.publicKey) - - if (producedBlocks.length === 0) { - wallet.missedBlocks++ - logger.debug( - `Delegate ${wallet.username} (${ - wallet.publicKey - }) just missed a block. Total: ${wallet.missedBlocks}`, - ) - wallet.dirty = true - emitter.emit('forger.missing', { - delegate: wallet, - }) - } - }) - } catch (error) { - logger.error(error.stack) - } - } - - /** - * Apply the round. - * Note that the round is applied and the end of the round (so checking height + 1) - * so the next block to apply starting the new round will be ready to be validated - * @param {Number} height - * @return {void} - */ - async applyRound(height) { - const nextHeight = height === 1 ? 1 : height + 1 - const maxDelegates = config.getConstants(nextHeight).activeDelegates - - if (nextHeight % maxDelegates === 1) { - const round = Math.floor((nextHeight - 1) / maxDelegates) + 1 - - if ( - !this.forgingDelegates || - this.forgingDelegates.length === 0 || - (this.forgingDelegates.length && - this.forgingDelegates[0].round !== round) - ) { - logger.info(`Starting Round ${round.toLocaleString()} :dove_of_peace:`) - - try { - this.updateDelegateStats(height, this.forgingDelegates) - this.saveWallets(false) // save only modified wallets during the last round - const delegates = this.walletManager.loadActiveDelegateList( - maxDelegates, - nextHeight, - ) // get active delegate list from in-memory wallet manager - this.saveRound(delegates) // save next round delegate list non-blocking - this.forgingDelegates = await this.getActiveDelegates( - nextHeight, - delegates, - ) // generate the new active delegates list - this.blocksInCurrentRound.length = 0 - } catch (error) { - // trying to leave database state has it was - await this.deleteRound(round) - throw error - } - } else { - logger.warn( - `Round ${round.toLocaleString()} has already been applied. This should happen only if you are a forger. :warning:`, - ) - } - } - } - - /** - * Remove the round. - * @param {Number} height - * @return {void} - */ - async revertRound(height) { - const { round, nextRound, maxDelegates } = roundCalculator.calculateRound( - height, - ) - - if (nextRound === round + 1 && height >= maxDelegates) { - logger.info(`Back to previous round: ${round.toLocaleString()} :back:`) - - const delegates = await this.__calcPreviousActiveDelegates(round) - this.forgingDelegates = await this.getActiveDelegates(height, delegates) - - await this.deleteRound(nextRound) - } - } - - /** - * Calculate the active delegates of the previous round. In order to do - * so we need to go back to the start of that round. Therefore we create - * a temporary wallet manager with all delegates and revert all blocks - * and transactions of that round to get the initial vote balances - * which are then used to restore the original order. - * @param {Number} round - */ - async __calcPreviousActiveDelegates(round) { - // TODO: cache the blocks of the last X rounds - this.blocksInCurrentRound = await this.__getBlocksForRound(round) - - // Create temp wallet manager from all delegates - const tempWalletManager = new WalletManager() - tempWalletManager.index(cloneDeep(this.walletManager.allByUsername())) - - // Revert all blocks in reverse order - let height = 0 - for (let i = this.blocksInCurrentRound.length - 1; i >= 0; i--) { - tempWalletManager.revertBlock(this.blocksInCurrentRound[i]) - height = this.blocksInCurrentRound[i].data.height - } - - // The first round has no active delegates - if (height === 1) { - return [] - } - - // Assert that the height is the beginning of a round. - const { maxDelegates } = roundCalculator.calculateRound(height) - assert(height > 1 && height % maxDelegates === 1) - - // Now retrieve the active delegate list from the temporary wallet manager. - return tempWalletManager.loadActiveDelegateList(maxDelegates, height) - } - - /** - * Validate a delegate. - * @param {Block} block - * @return {void} - */ - async validateDelegate(block) { - if (this.__isException(block.data)) { - return - } - - const delegates = await this.getActiveDelegates(block.data.height) - const slot = slots.getSlotNumber(block.data.timestamp) - const forgingDelegate = delegates[slot % delegates.length] - - const generatorUsername = this.walletManager.findByPublicKey( - block.data.generatorPublicKey, - ).username - - if (!forgingDelegate) { - logger.debug( - `Could not decide if delegate ${generatorUsername} (${ - block.data.generatorPublicKey - }) is allowed to forge block ${block.data.height.toLocaleString()} :grey_question:`, - ) - } else if (forgingDelegate.publicKey !== block.data.generatorPublicKey) { - const forgingUsername = this.walletManager.findByPublicKey( - forgingDelegate.publicKey, - ).username - - if (delegates.some(delegate => delegate.publicKey === block.data.generatorPublicKey)) { - throw new Error( - `Delegate ${generatorUsername} (${ - block.data.generatorPublicKey - }) not allowed to forge, should be ${forgingUsername} (${ - forgingDelegate.publicKey - }) :-1:`, - ) - } else { - throw new Error("inactive generator"); - } - } else { - logger.debug( - `Delegate ${generatorUsername} (${ - block.data.generatorPublicKey - }) allowed to forge block ${block.data.height.toLocaleString()} :+1:`, - ) - } - } - - /** - * Validate a forked block. - * @param {Block} block - * @return {Boolean} - */ - async validateForkedBlock(block) { - try { - await this.validateDelegate(block) - } catch (error) { - logger.debug(error.stack) - return false - } - - return true - } - - /** - * Apply the given block. - * @param {Block} block - * @return {void} - */ - async applyBlock(block) { - await this.validateDelegate(block) - this.walletManager.applyBlock(block) - - if (this.blocksInCurrentRound) { - this.blocksInCurrentRound.push(block) - } - - await this.applyRound(block.data.height) - block.transactions.forEach(tx => this.__emitTransactionEvents(tx)) - emitter.emit('block.applied', block.data) - } - - /** - * Emit events for the specified transaction. - * @param {Object} transaction - * @return {void} - */ - __emitTransactionEvents(transaction) { - emitter.emit('transaction.applied', transaction.data) - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - emitter.emit('delegate.registered', transaction.data) - } - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_RESIGNATION) { - emitter.emit('delegate.resigned', transaction.data) - } - - if (transaction.type === TRANSACTION_TYPES.VOTE) { - const vote = transaction.asset.votes[0] - - emitter.emit(vote.startsWith('+') ? 'wallet.vote' : 'wallet.unvote', { - delegate: vote, - transaction: transaction.data, - }) - } - } - - /** - * Remove the given block. - * @param {Block} block - * @return {void} - */ - async revertBlock(block) { - await this.revertRound(block.data.height) - await this.walletManager.revertBlock(block) - - assert(this.blocksInCurrentRound.pop().data.id === block.data.id) - - emitter.emit('block.reverted', block.data) - } - - /** - * Verify a transaction. - * @param {Transaction} transaction - * @return {Boolean} - */ - async verifyTransaction(transaction) { - const senderId = crypto.getAddress( - transaction.data.senderPublicKey, - config.network.pubKeyHash, - ) - - const sender = this.walletManager.findByAddress(senderId) // should exist - - if (!sender.publicKey) { - sender.publicKey = transaction.data.senderPublicKey - this.walletManager.reindex(sender) - } - - const dbTransaction = await this.getTransaction(transaction.data.id) - - return sender.canApply(transaction.data, []) && !dbTransaction - } - - /** - * Get blocks for round. - * @param {number} round - * @return {[]Block} - */ - async __getBlocksForRound(round) { - let lastBlock - if (app.has('state')) { - lastBlock = app.resolve('state').getLastBlock() - } else { - lastBlock = await this.getLastBlock() - } - - if (!lastBlock) { - return [] - } - - let height = +lastBlock.data.height - if (!round) { - round = roundCalculator.calculateRound(height).round - } - - const maxDelegates = config.getConstants(height).activeDelegates - height = round * maxDelegates + 1 - - const blocks = await this.getBlocks(height - maxDelegates, maxDelegates - 1) - return blocks.map(b => new Block(b)) - } - - /** - * Register event listeners. - * @return {void} - */ - __registerListeners() { - emitter.on('state:started', () => { - this.stateStarted = true - }) - } - - /** - * Register the wallet app. - * @return {void} - */ - async _registerWalletManager() { - this.walletManager = new WalletManager() - } - - /** - * Register the wallet and delegate repositories. - * @return {void} - */ - async _registerRepositories() { - this.wallets = new (require('./repositories/wallets'))(this) - this.delegates = new (require('./repositories/delegates'))(this) - } - - /** - * Determine if the given block is an exception. - * @param {Object} block - * @return {Boolean} - */ - __isException(block) { - if (!config) { - return false - } - - if (!Array.isArray(config.network.exceptions.blocks)) { - return false - } - - return config.network.exceptions.blocks.includes(block.id) - } -} diff --git a/packages/core-database/lib/manager.js b/packages/core-database/lib/manager.js deleted file mode 100644 index 00b00833ab..0000000000 --- a/packages/core-database/lib/manager.js +++ /dev/null @@ -1,30 +0,0 @@ -class DatabaseManager { - /** - * Create a new database manager instance. - * @constructor - */ - constructor() { - this.connections = {} - } - - /** - * Get a database connection instance. - * @param {String} name - * @return {ConnectionInterface} - */ - connection(name = 'default') { - return this.connections[name] - } - - /** - * Make the database connection instance. - * @param {ConnectionInterface} connection - * @param {String} name - * @return {void} - */ - async makeConnection(connection, name = 'default') { - this.connections[name] = await connection.make() - } -} - -module.exports = new DatabaseManager() diff --git a/packages/core-database/lib/repositories/delegates.js b/packages/core-database/lib/repositories/delegates.js deleted file mode 100644 index f11b258c63..0000000000 --- a/packages/core-database/lib/repositories/delegates.js +++ /dev/null @@ -1,113 +0,0 @@ -const { delegateCalculator } = require('@arkecosystem/core-utils') -const orderBy = require('lodash/orderBy') -const limitRows = require('./utils/limit-rows') - -module.exports = class DelegatesRepository { - /** - * Create a new delegate repository instance. - * @param {ConnectionInterface} connection - */ - constructor(connection) { - this.connection = connection - } - - /** - * Get all local delegates. - * @return {Array} - */ - getLocalDelegates() { - return this.connection.walletManager - .all() - .filter(wallet => !!wallet.username) - } - - /** - * Find all delegates. - * @param {Object} params - * @return {Object} - */ - findAll(params = {}) { - const rows = this.getLocalDelegates() - - const order = params.orderBy ? params.orderBy.split(':') : ['rate', 'asc'] - - return { - rows: limitRows(orderBy(rows, order), params), - count: rows.length, - } - } - - /** - * Paginate all delegates. - * @param {Object} params - * @return {Object} - */ - paginate(params) { - return this.findAll(params) - } - - /** - * Search all delegates. - * TODO Currently it searches by username only - * @param {Object} [params] - * @param {String} [params.username] - Search by username - * @return {Object} - */ - search(params) { - let delegates = this.getLocalDelegates().filter( - delegate => delegate.username.indexOf(params.username) > -1, - ) - - if (params.orderBy) { - const orderByField = params.orderBy.split(':')[0] - const orderByDirection = params.orderBy.split(':')[1] || 'desc' - - delegates = delegates.sort((a, b) => { - if (orderByDirection === 'desc' && a[orderByField] < b[orderByField]) { - return -1 - } - - if (orderByDirection === 'asc' && a[orderByField] > b[orderByField]) { - return 1 - } - - return 0 - }) - } - - return { - rows: limitRows(delegates, params), - count: delegates.length, - } - } - - /** - * Find a delegate. - * @param {String} id - * @return {Object} - */ - findById(id) { - return this.getLocalDelegates().find( - a => a.address === id || a.publicKey === id || a.username === id, - ) - } - - /** - * Find all active delegates at height. - * @param {Number} height - * @return {Array} - */ - getActiveAtHeight(height) { - const delegates = this.connection.getActiveDelegates(height) - - return delegates.map(delegate => { - const wallet = this.connection.wallets.findById(delegate.publicKey) - - return { - username: wallet.username, - approval: delegateCalculator.calculateApproval(delegate, height), - productivity: delegateCalculator.calculateProductivity(wallet), - } - }) - } -} diff --git a/packages/core-database/lib/repositories/utils/filter-rows.js b/packages/core-database/lib/repositories/utils/filter-rows.js deleted file mode 100644 index 197ab448c5..0000000000 --- a/packages/core-database/lib/repositories/utils/filter-rows.js +++ /dev/null @@ -1,71 +0,0 @@ -/* eslint no-prototype-builtins: "off" */ - -/** - * Filter an Array of Objects based on the given parameters. - * @param {Array} rows - * @param {Object} params - * @param {Object} filters - * @return {Array} - */ -module.exports = (rows, params, filters) => - rows.filter(item => { - if (filters.hasOwnProperty('exact')) { - for (const elem of filters.exact) { - if (params[elem] && item[elem] !== params[elem]) { - return false - } - } - } - - if (filters.hasOwnProperty('between')) { - for (const elem of filters.between) { - if (!params[elem]) { - continue - } - - if ( - !params[elem].hasOwnProperty('from') && - !params[elem].hasOwnProperty('to') && - item[elem] !== params[elem] - ) { - return false - } - - if ( - params[elem].hasOwnProperty('from') || - params[elem].hasOwnProperty('to') - ) { - let isMoreThan = true - let isLessThan = true - - if (params[elem].hasOwnProperty('from')) { - isMoreThan = item[elem] >= params[elem].from - } - - if (params[elem].hasOwnProperty('to')) { - isLessThan = item[elem] <= params[elem].to - } - - return isMoreThan && isLessThan - } - } - } - - // NOTE: it was used to filter by `votes`, but that field was rejected and - // replaced by `vote`. This filter is kept here just in case - if (filters.hasOwnProperty('any')) { - for (const elem of filters.any) { - if (params[elem] && item[elem]) { - if (Array.isArray(params[elem])) { - if (item[elem].every(a => params[elem].indexOf(a) === -1)) { - return false - } - } else { - throw new Error('Fitering by "any" requires an Array') - } - } - } - } - - return true - }) diff --git a/packages/core-database/lib/repositories/utils/limit-rows.js b/packages/core-database/lib/repositories/utils/limit-rows.js deleted file mode 100644 index 28a7d5ddd9..0000000000 --- a/packages/core-database/lib/repositories/utils/limit-rows.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Return some rows by an offset and a limit. - * @param {Array} rows - * @param {Object} params - * @return {Array} - */ -module.exports = (rows, params) => { - if (params.offset || params.limit) { - const offset = params.offset || 0 - const limit = params.limit ? offset + params.limit : rows.length - - return rows.slice(offset, limit) - } - - return rows -} diff --git a/packages/core-database/lib/repositories/wallets.js b/packages/core-database/lib/repositories/wallets.js deleted file mode 100644 index 1c6e53b522..0000000000 --- a/packages/core-database/lib/repositories/wallets.js +++ /dev/null @@ -1,122 +0,0 @@ -const orderBy = require('lodash/orderBy') -const filterRows = require('./utils/filter-rows') -const limitRows = require('./utils/limit-rows') - -module.exports = class WalletsRepository { - /** - * Create a new wallet repository instance. - * @param {ConnectionInterface} connection - */ - constructor(connection) { - this.connection = connection - } - - /** - * Get all local wallets. - * @return {Array} - */ - all() { - return this.connection.walletManager.all() - } - - /** - * Find all wallets. - * @param {Object} params - * @return {Object} - */ - findAll(params = {}) { - const wallets = this.all() - - const [iteratee, order] = params.orderBy - ? params.orderBy.split(':') - : ['rate', 'asc'] - - return { - rows: limitRows(orderBy(wallets, iteratee, order), params), - count: wallets.length, - } - } - - /** - * Find all wallets for the given vote. - * @param {String} publicKey - * @param {Object} params - * @return {Object} - */ - findAllByVote(publicKey, params = {}) { - const wallets = this.all().filter(wallet => wallet.vote === publicKey) - - return { - rows: limitRows(wallets, params), - count: wallets.length, - } - } - - /** - * Find a wallet by address, public key or username. - * @param {Number} id - * @return {Object} - */ - findById(id) { - return this.all().find( - wallet => wallet.address === id - || wallet.publicKey === id - || wallet.username === id, - ) - } - - /** - * Count all wallets. - * @return {Number} - */ - count() { - return this.all().length - } - - /** - * Find all wallets sorted by balance. - * @param {Object} params - * @return {Object} - */ - top(params = {}) { - const wallets = Object.values(this.all()).sort( - (a, b) => +b.balance.minus(a.balance).toFixed(), - ) - - return { - rows: limitRows(wallets, params), - count: wallets.length, - } - } - - /** - * Search all wallets. - * @param {Object} [params] - * @param {Number} [params.limit] - Limit the number of results - * @param {Number} [params.offset] - Skip some results - * @param {Array} [params.orderBy] - Order of the results - * @param {String} [params.address] - Search by address - * @param {String} [params.publicKey] - Search by publicKey - * @param {String} [params.secondPublicKey] - Search by secondPublicKey - * @param {String} [params.username] - Search by username - * @param {String} [params.vote] - Search by vote - * @param {Object} [params.balance] - Search by balance - * @param {Number} [params.balance.from] - Search by balance (minimum) - * @param {Number} [params.balance.to] - Search by balance (maximum) - * @param {Object} [params.voteBalance] - Search by voteBalance - * @param {Number} [params.voteBalance.from] - Search by voteBalance (minimum) - * @param {Number} [params.voteBalance.to] - Search by voteBalance (maximum) - * @return {Object} - */ - search(params) { - const wallets = filterRows(this.all(), params, { - exact: ['address', 'publicKey', 'secondPublicKey', 'username', 'vote'], - between: ['balance', 'voteBalance'], - }) - - return { - rows: limitRows(wallets, params), - count: wallets.length, - } - } -} diff --git a/packages/core-database/lib/wallet-manager.js b/packages/core-database/lib/wallet-manager.js deleted file mode 100644 index bceb8f037d..0000000000 --- a/packages/core-database/lib/wallet-manager.js +++ /dev/null @@ -1,609 +0,0 @@ -const { crypto, formatArktoshi } = require('@arkecosystem/crypto') -const { Wallet } = require('@arkecosystem/crypto').models -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants -const { roundCalculator } = require('@arkecosystem/core-utils') -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const logger = app.resolvePlugin('logger') - -const pluralize = require('pluralize') - -module.exports = class WalletManager { - /** - * Create a new wallet manager instance. - * @constructor - */ - constructor() { - this.networkId = config ? config.network.pubKeyHash : 0x17 - this.reset() - } - - /** - * Reset the wallets index. - * @return {void} - */ - reset() { - this.byAddress = {} - this.byPublicKey = {} - this.byUsername = {} - } - - /** - * Get all wallets by address. - * @return {Array} - */ - all() { - return Object.values(this.byAddress) - } - - /** - * Get all wallets by publicKey. - * @return {Array} - */ - allByPublicKey() { - return Object.values(this.byPublicKey) - } - - /** - * Get all wallets by username. - * @return {Array} - */ - allByUsername() { - return Object.values(this.byUsername) - } - - /** - * Find a wallet by the given address. - * @param {String} address - * @return {Wallet} - */ - findByAddress(address) { - if (!this.byAddress[address]) { - this.byAddress[address] = new Wallet(address) - } - - return this.byAddress[address] - } - - /** - * Find a wallet by the given public key. - * @param {String} publicKey - * @return {Wallet} - */ - findByPublicKey(publicKey) { - if (!this.byPublicKey[publicKey]) { - const address = crypto.getAddress(publicKey, config.network.pubKeyHash) - - const wallet = this.findByAddress(address) - wallet.publicKey = publicKey - this.byPublicKey[publicKey] = wallet - } - - return this.byPublicKey[publicKey] - } - - /** - * Find a wallet by the given username. - * @param {String} username - * @return {Wallet} - */ - findByUsername(username) { - return this.byUsername[username] - } - - /** - * Set wallet by address. - * @param {String} address - * @param {Wallet} wallet - * @param {void} - */ - setByAddress(address, wallet) { - this.byAddress[address] = wallet - } - - /** - * Set wallet by publicKey. - * @param {String} publicKey - * @param {Wallet} wallet - * @param {void} - */ - setByPublicKey(publicKey, wallet) { - this.byPublicKey[publicKey] = wallet - } - - /** - * Set wallet by username. - * @param {String} username - * @param {Wallet} wallet - * @param {void} - */ - setByUsername(username, wallet) { - this.byUsername[username] = wallet - } - - /** - * Remove wallet by address. - * @param {String} address - * @param {void} - */ - forgetByAddress(address) { - delete this.byAddress[address] - } - - /** - * Remove wallet by publicKey. - * @param {String} publicKey - * @param {void} - */ - forgetByPublicKey(publicKey) { - delete this.byPublicKey[publicKey] - } - - /** - * Remove wallet by username. - * @param {String} username - * @param {void} - */ - forgetByUsername(username) { - delete this.byUsername[username] - } - - /** - * Index the given wallets. - * @param {Array} wallets - * @return {void} - */ - index(wallets) { - for (const wallet of wallets) { - this.reindex(wallet) - } - } - - /** - * Reindex the given wallet. - * @param {Wallet} wallet - * @return {void} - */ - reindex(wallet) { - if (wallet.address) { - this.byAddress[wallet.address] = wallet - } - - if (wallet.publicKey) { - this.byPublicKey[wallet.publicKey] = wallet - } - - if (wallet.username) { - this.byUsername[wallet.username] = wallet - } - } - - clear() { - Object.values(this.byAddress).forEach(wallet => { - wallet.dirty = false - }) - } - - /** - * Load a list of all active delegates. - * @param {Number} maxDelegates - * @return {Array} - */ - loadActiveDelegateList(maxDelegates, height) { - if (height > 1 && height % maxDelegates !== 1) { - throw new Error('Trying to build delegates outside of round change') - } - - const { round } = roundCalculator.calculateRound(height, maxDelegates) - let delegates = this.allByUsername() - - if (delegates.length < maxDelegates) { - throw new Error( - `Expected to find ${maxDelegates} delegates but only found ${ - delegates.length - }. This indicates an issue with the genesis block & delegates.`, - ) - } - - const equalVotesMap = new Map() - - delegates = delegates - .sort((a, b) => { - const diff = b.voteBalance.comparedTo(a.voteBalance) - - if (diff === 0) { - if (!equalVotesMap.has(a.voteBalance.toFixed())) { - equalVotesMap.set(a.voteBalance.toFixed(), new Set()) - } - - const set = equalVotesMap.get(a.voteBalance.toFixed()) - set.add(a) - set.add(b) - - if (a.publicKey === b.publicKey) { - throw new Error( - `The balance and public key of both delegates are identical! Delegate "${ - a.username - }" appears twice in the list.`, - ) - } - - return a.publicKey.localeCompare(b.publicKey, 'en') - } - - return diff - }) - .map((delegate, i) => { - const rate = i + 1 - this.byUsername[delegate.username].rate = rate - return { ...{ round }, ...delegate, rate } - }) - .slice(0, maxDelegates) - - for (const [voteBalance, set] of equalVotesMap.entries()) { - const values = Array.from(set.values()) - if (delegates.includes(values[0])) { - const mapped = values.map(v => `${v.username} (${v.publicKey})`) - logger.warn( - `Delegates ${JSON.stringify( - mapped, - null, - 4, - )} have a matching vote balance of ${formatArktoshi(voteBalance)}`, - ) - } - } - - logger.debug( - `Loaded ${delegates.length} active ${pluralize( - 'delegate', - delegates.length, - )}`, - ) - - return delegates - } - - /** - * Build vote balances of all delegates. - * NOTE: Only called during SPV. - * @return {void} - */ - buildVoteBalances() { - Object.values(this.byPublicKey).forEach(voter => { - if (voter.vote) { - const delegate = this.byPublicKey[voter.vote] - delegate.voteBalance = delegate.voteBalance.plus(voter.balance) - } - }) - } - - /** - * Remove non-delegate wallets that have zero (0) balance from memory. - * @return {void} - */ - purgeEmptyNonDelegates() { - Object.values(this.byPublicKey).forEach(wallet => { - if (this.__canBePurged(wallet)) { - delete this.byPublicKey[wallet.publicKey] - delete this.byAddress[wallet.address] - } - }) - } - - /** - * Apply the given block to a delegate. - * @param {Block} block - * @return {void} - */ - applyBlock(block) { - const generatorPublicKey = block.data.generatorPublicKey - - let delegate = this.byPublicKey[block.data.generatorPublicKey] - - if (!delegate) { - const generator = crypto.getAddress(generatorPublicKey, this.networkId) - - if (block.data.height === 1) { - delegate = new Wallet(generator) - delegate.publicKey = generatorPublicKey - - this.reindex(delegate) - } else { - logger.debug(`Delegate by address: ${this.byAddress[generator]}`) - - if (this.byAddress[generator]) { - logger.info('This look like a bug, please report :bug:') - } - - throw new Error( - `Could not find delegate with publicKey ${generatorPublicKey}`, - ) - } - } - - const appliedTransactions = [] - - try { - block.transactions.forEach(transaction => { - this.applyTransaction(transaction) - appliedTransactions.push(transaction) - }) - - const applied = delegate.applyBlock(block.data) - - // If the block has been applied to the delegate, the balance is increased - // by reward + totalFee. In which case the vote balance of the - // delegate's delegate has to be updated. - if (applied && delegate.vote) { - const increase = block.data.reward.plus(block.data.totalFee) - const votedDelegate = this.byPublicKey[delegate.vote] - votedDelegate.voteBalance = votedDelegate.voteBalance.plus(increase) - } - } catch (error) { - logger.error( - 'Failed to apply all transactions in block - reverting previous transactions', - ) - // Revert the applied transactions from last to first - for (let i = appliedTransactions.length - 1; i >= 0; i--) { - this.revertTransaction(appliedTransactions[i]) - } - - // TODO: should revert the delegate applyBlock ? - // TBC: whatever situation `delegate.applyBlock(block.data)` is never applied - - throw error - } - } - - /** - * Remove the given block from a delegate. - * @param {Block} block - * @return {void} - */ - async revertBlock(block) { - const delegate = this.byPublicKey[block.data.generatorPublicKey] - - if (!delegate) { - app.forceExit( - `Failed to lookup generator '${ - block.data.generatorPublicKey - }' of block '${block.data.id}'. :skull:`, - ) - } - - const revertedTransactions = [] - - try { - // Revert the transactions from last to first - for (let i = block.transactions.length - 1; i >= 0; i--) { - const transaction = block.transactions[i] - this.revertTransaction(transaction) - revertedTransactions.push(transaction) - } - - const reverted = delegate.revertBlock(block.data) - - // If the block has been reverted, the balance is decreased - // by reward + totalFee. In which case the vote balance of the - // delegate's delegate has to be updated. - if (reverted && delegate.vote) { - const decrease = block.data.reward.plus(block.data.totalFee) - const votedDelegate = this.byPublicKey[delegate.vote] - votedDelegate.voteBalance = votedDelegate.voteBalance.minus(decrease) - } - } catch (error) { - logger.error(error.stack) - - revertedTransactions - .reverse() - .forEach(transaction => this.applyTransaction(transaction)) - - throw error - } - } - - /** - * Apply the given transaction to a delegate. - * @param {Transaction} transaction - * @return {Transaction} - */ - applyTransaction(transaction) { - /* eslint padded-blocks: "off" */ - const { data } = transaction - const { type, asset, recipientId, senderPublicKey } = data - - const sender = this.findByPublicKey(senderPublicKey) - const recipient = this.findByAddress(recipientId) - const errors = [] - - // specific verifications / adjustments depending on transaction type - if ( - type === TRANSACTION_TYPES.DELEGATE_REGISTRATION && - this.byUsername[asset.delegate.username.toLowerCase()] - ) { - logger.error( - `Can't apply transaction ${ - data.id - }: delegate name '${asset.delegate.username.toLowerCase()}' already taken.`, - ) - throw new Error( - `Can't apply transaction ${data.id}: delegate name already taken.`, - ) - - // NOTE: We use the vote public key, because vote transactions - // have the same sender and recipient - } else if ( - type === TRANSACTION_TYPES.VOTE && - !this.__isDelegate(asset.votes[0].slice(1)) - ) { - logger.error( - `Can't apply vote transaction ${data.id}: delegate ${ - asset.votes[0] - } does not exist.`, - ) - throw new Error( - `Can't apply transaction ${data.id}: delegate ${ - asset.votes[0] - } does not exist.`, - ) - } else if (type === TRANSACTION_TYPES.SECOND_SIGNATURE) { - data.recipientId = '' - } - - // handle exceptions / verify that we can apply the transaction to the sender - if (this.__isException(data)) { - logger.warn( - `Transaction ${ - data.id - } forcibly applied because it has been added as an exception.`, - ) - } else if (!sender.canApply(data, errors)) { - logger.error( - `Can't apply transaction id:${data.id} from sender:${ - sender.address - } due to ${JSON.stringify(errors)}`, - ) - logger.debug(`Audit: ${JSON.stringify(sender.auditApply(data), null, 2)}`) - throw new Error(`Can't apply transaction ${data.id}`) - } - - sender.applyTransactionToSender(data) - - if (type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - this.reindex(sender) - } - - if (recipient && type === TRANSACTION_TYPES.TRANSFER) { - recipient.applyTransactionToRecipient(data) - } - - this._updateVoteBalances(sender, recipient, data) - - return transaction - } - - /** - * Updates the vote balances of the respective delegates of sender and recipient. - * If the transaction is not a vote... - * 1. fee + amount is removed from the sender's delegate vote balance - * 2. amount is added to the recipient's delegate vote balance - * - * in case of a vote... - * 1. the full sender balance is added to the sender's delegate vote balance - * - * If revert is set to true, the operations are reversed (plus -> minus, minus -> plus). - * @param {Wallet} sender - * @param {Wallet} recipient - * @param {Transaction} transaction - * @param {Boolean} revert - * @return {Transaction} - */ - _updateVoteBalances(sender, recipient, transaction, revert = false) { - // TODO: multipayment? - if (transaction.type !== TRANSACTION_TYPES.VOTE) { - // Update vote balance of the sender's delegate - if (sender.vote) { - const delegate = this.findByPublicKey(sender.vote) - const total = transaction.amount.plus(transaction.fee) - delegate.voteBalance = revert - ? delegate.voteBalance.plus(total) - : delegate.voteBalance.minus(total) - } - - // Update vote balance of recipient's delegate - if (recipient && recipient.vote) { - const delegate = this.findByPublicKey(recipient.vote) - delegate.voteBalance = revert - ? delegate.voteBalance.minus(transaction.amount) - : delegate.voteBalance.plus(transaction.amount) - } - } else { - const vote = transaction.asset.votes[0] - const delegate = this.findByPublicKey(vote.substr(1)) - - if (vote.startsWith('+')) { - delegate.voteBalance = revert - ? delegate.voteBalance.minus(sender.balance) - : delegate.voteBalance.plus(sender.balance) - } else { - delegate.voteBalance = revert - ? delegate.voteBalance.plus(sender.balance.plus(transaction.fee)) - : delegate.voteBalance.minus(sender.balance.plus(transaction.fee)) - } - } - } - - /** - * Remove the given transaction from a delegate. - * @param {Transaction} transaction - * @return {Transaction} - */ - revertTransaction(transaction) { - const { type, data } = transaction - const sender = this.findByPublicKey(data.senderPublicKey) // Should exist - const recipient = this.byAddress[data.recipientId] - - sender.revertTransactionForSender(data) - - // removing the wallet from the delegates index - if (type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - delete this.byUsername[data.asset.delegate.username] - } - - if (recipient && type === TRANSACTION_TYPES.TRANSFER) { - recipient.revertTransactionForRecipient(data) - } - - // Revert vote balance updates - this._updateVoteBalances(sender, recipient, data, true) - - return data - } - - /** - * Checks if a given publicKey is a registered delegate - * @param {String} publicKey - */ - __isDelegate(publicKey) { - const delegateWallet = this.byPublicKey[publicKey] - - if (delegateWallet && delegateWallet.username) { - return !!this.byUsername[delegateWallet.username] - } - - return false - } - - /** - * Determine if the wallet can be removed from memory. - * @param {Object} wallet - * @return {Boolean} - */ - __canBePurged(wallet) { - return ( - wallet.balance.isZero() && - !wallet.secondPublicKey && - !wallet.multisignature && - !wallet.username - ) - } - - /** - * Determine if the given transaction is an exception. - * @param {Object} transaction - * @return {Boolean} - */ - __isException(transaction) { - if (!config) { - return false - } - - if (!Array.isArray(config.network.exceptions.transactions)) { - return false - } - - return config.network.exceptions.transactions.includes(transaction.id) - } -} diff --git a/packages/core-database/package.json b/packages/core-database/package.json index 1559f49017..4f8acdcc61 100644 --- a/packages/core-database/package.json +++ b/packages/core-database/package.json @@ -1,38 +1,58 @@ { - "name": "@arkecosystem/core-database", - "description": "Database Interface for Ark Core", - "version": "0.2.0", - "contributors": [ - "François-Xavier Thoorens ", - "Kristjan Košič ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "lodash.clonedeep": "^4.5.0", - "lodash.compact": "^3.0.1", - "lodash.uniq": "^4.5.0", - "pluralize": "^7.0.0" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-database", + "description": "Database Interface for Ark Core", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Kristjan Košič ", + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/lodash.clonedeep": "^4.5.4", + "@types/lodash.compact": "^3.0.4", + "@types/lodash.uniq": "^4.5.4", + "lodash.clonedeep": "^4.5.0", + "lodash.compact": "^3.0.1", + "lodash.uniq": "^4.5.0", + "pluralize": "^7.0.0" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0", + "@types/pluralize": "^0.0.29" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-database/src/database-service-factory.ts b/packages/core-database/src/database-service-factory.ts new file mode 100644 index 0000000000..daeceb6d20 --- /dev/null +++ b/packages/core-database/src/database-service-factory.ts @@ -0,0 +1,13 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { DatabaseService } from "./database-service"; +import { DelegatesRepository } from "./repositories/delegates"; +import { WalletsRepository } from "./repositories/wallets"; + +// Allow extenders of core-database to provide, optionally, a IWalletManager concrete in addition to a IDatabaseConnection, but keep the business repos common +export const databaseServiceFactory = async (opts: any, walletManager: Database.IWalletManager, connection: Database.IDatabaseConnection): Promise => { + let databaseService: DatabaseService; + databaseService = new DatabaseService(opts, connection, walletManager, new WalletsRepository(() => databaseService), new DelegatesRepository(() => databaseService)); + await databaseService.init(); + return databaseService; +}; + diff --git a/packages/core-database/src/database-service.ts b/packages/core-database/src/database-service.ts new file mode 100644 index 0000000000..cf95461a88 --- /dev/null +++ b/packages/core-database/src/database-service.ts @@ -0,0 +1,556 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import { roundCalculator } from "@arkecosystem/core-utils"; +import { Bignum, constants, crypto as arkCrypto, models } from "@arkecosystem/crypto"; +import assert from "assert"; +import crypto from "crypto"; +import cloneDeep from "lodash/cloneDeep"; +import pluralize from "pluralize"; +import { WalletManager } from "./wallet-manager"; + +const { Block, Transaction } = models; +const { TransactionTypes } = constants; + + +export class DatabaseService implements Database.IDatabaseService { + + public connection: Database.IDatabaseConnection; + public walletManager: Database.IWalletManager; + public logger = app.resolvePlugin("logger"); + public emitter = app.resolvePlugin("event-emitter"); + public config = app.getConfig(); + public options: any; + public wallets: Database.IWalletsBusinessRepository; + public delegates: Database.IDelegatesBusinessRepository; + public blocksInCurrentRound: any[] = null; + public stateStarted: boolean = false; + public restoredDatabaseIntegrity: boolean = false; + public forgingDelegates: any[] = null; + public cache: Map = new Map(); + private spvFinished: boolean; + + constructor(options: any, + connection: Database.IDatabaseConnection, + walletManager: Database.IWalletManager, + walletsBusinessRepository: Database.IWalletsBusinessRepository, + delegatesBusinessRepository: Database.IDelegatesBusinessRepository + ) { + this.connection = connection; + this.walletManager = walletManager; + this.options = options; + this.wallets = walletsBusinessRepository; + this.delegates = delegatesBusinessRepository; + + this.registerListeners(); + } + + public async init() { + await this.loadBlocksFromCurrentRound(); + } + + public async applyBlock(block: models.Block) { + this.walletManager.applyBlock(block); + + if (this.blocksInCurrentRound) { + this.blocksInCurrentRound.push(block); + } + + await this.applyRound(block.data.height); + block.transactions.forEach(tx => this.emitTransactionEvents(tx)); + this.emitter.emit("block.applied", block.data); + return true; + } + + public async applyRound(height: number) { + const nextHeight = height === 1 ? 1 : height + 1; + const maxDelegates = this.config.getMilestone(nextHeight).activeDelegates; + + if (nextHeight % maxDelegates === 1) { + const round = Math.floor((nextHeight - 1) / maxDelegates) + 1; + + if ( + !this.forgingDelegates || + this.forgingDelegates.length === 0 || + (this.forgingDelegates.length && this.forgingDelegates[0].round !== round) + ) { + this.logger.info(`Starting Round ${round.toLocaleString()} :dove_of_peace:`); + + try { + this.updateDelegateStats(this.forgingDelegates); + await this.saveWallets(false); // save only modified wallets during the last round + const delegates = this.walletManager.loadActiveDelegateList(maxDelegates, nextHeight); // get active delegate list from in-memory wallet manager + await this.saveRound(delegates); // save next round delegate list non-blocking + this.forgingDelegates = await this.getActiveDelegates(nextHeight, delegates); // generate the new active delegates list + this.blocksInCurrentRound.length = 0; + } catch (error) { + // trying to leave database state has it was + await this.deleteRound(round); + throw error; + } + } else { + this.logger.warn( + // tslint:disable-next-line:max-line-length + `Round ${round.toLocaleString()} has already been applied. This should happen only if you are a forger. :warning:`, + ); + } + } + } + + public async buildWallets(height: number): Promise { + this.walletManager.reset(); + + try { + const success = await this.connection.buildWallets(height); + this.spvFinished = true; + return success; + } catch (e) { + this.logger.error(e.stack); + } + return false; + } + + public async commitQueuedQueries() { + await this.connection.commitQueuedQueries(); + } + + public async deleteBlock(block: models.Block) { + await this.connection.deleteBlock(block); + } + + public async deleteRound(round: number) { + await this.connection.roundsRepository.delete(round); + } + + public enqueueDeleteBlock(block: models.Block) { + this.connection.enqueueDeleteBlock(block); + } + + public enqueueDeleteRound(height: number) { + this.connection.enqueueDeleteRound(height); + } + + public enqueueSaveBlock(block: models.Block) { + this.connection.enqueueSaveBlock(block); + } + + public async getActiveDelegates(height: number, delegates?: any[]) { + const maxDelegates = this.config.getMilestone(height).activeDelegates; + const round = Math.floor((height - 1) / maxDelegates) + 1; + + if (this.forgingDelegates && this.forgingDelegates.length && this.forgingDelegates[0].round === round) { + return this.forgingDelegates; + } + + // When called during applyRound we already know the delegates, so we don't have to query the database. + if (!delegates || delegates.length === 0) { + delegates = await this.connection.roundsRepository.findById(round); + } + + const seedSource = round.toString(); + let currentSeed = crypto + .createHash("sha256") + .update(seedSource, "utf8") + .digest(); + + for (let i = 0, delCount = delegates.length; i < delCount; i++) { + for (let x = 0; x < 4 && i < delCount; i++, x++) { + const newIndex = currentSeed[x] % delCount; + const b = delegates[newIndex]; + delegates[newIndex] = delegates[i]; + delegates[i] = b; + } + currentSeed = crypto + .createHash("sha256") + .update(currentSeed) + .digest(); + } + + this.forgingDelegates = delegates.map(delegate => { + delegate.round = +delegate.round; + return delegate; + }); + + return this.forgingDelegates; + } + + public async getBlock(id: string) { + // TODO: caching the last 1000 blocks, in combination with `saveBlock` could help to optimise + const block = await this.connection.blocksRepository.findById(id); + + if (!block) { + return null; + } + + const transactions = await this.connection.transactionsRepository.findByBlockId(block.id); + + block.transactions = transactions.map(({ serialized }) => Transaction.deserialize(serialized.toString("hex"))); + + return new Block(block); + } + + public async getBlocks(offset: number, limit: number) { + let blocks = []; + + // The functions below return matches in the range [start, end], including both ends. + const start = offset; + const end = offset + limit - 1; + + if (app.has("state")) { + blocks = app.resolve("state").getLastBlocksByHeight(start, end); + } + + if (blocks.length !== limit) { + blocks = await this.connection.blocksRepository.heightRange(start, end); + + await this.loadTransactionsForBlocks(blocks); + } + + return blocks; + } + + public async getBlocksForRound(round?: number) { + let lastBlock; + if (app.has("state")) { + lastBlock = app.resolve("state").getLastBlock(); + } else { + lastBlock = await this.getLastBlock(); + } + + if (!lastBlock) { + return []; + } + + let height = +lastBlock.data.height; + if (!round) { + round = roundCalculator.calculateRound(height).round; + } + + const maxDelegates = this.config.getMilestone(height).activeDelegates; + height = round * maxDelegates + 1; + + const blocks = await this.getBlocks(height - maxDelegates, maxDelegates); + return blocks.map(b => new Block(b)); + } + + public async getForgedTransactionsIds(ids: string[]) { + if (!ids.length) { + return []; + } + + const txs = await this.connection.transactionsRepository.forged(ids); + return txs.map(tx => tx.id); + } + + public async getLastBlock() { + const block = await this.connection.blocksRepository.latest(); + + if (!block) { + return null; + } + + const transactions = await this.connection.transactionsRepository.latestByBlock(block.id); + + block.transactions = transactions.map(({ serialized }) => Transaction.deserialize(serialized.toString("hex"))); + + return new Block(block); + } + + public async getCommonBlocks(ids: string[]) { + const state = app.resolve("state"); + let commonBlocks = state.getCommonBlocks(ids); + if (commonBlocks.length < ids.length) { + commonBlocks = await this.connection.blocksRepository.common(ids); + } + + return commonBlocks; + } + + public async getRecentBlockIds() { + const state = app.resolve("state"); + let blocks = state + .getLastBlockIds() + .reverse() + .slice(0, 10); + + if (blocks.length < 10) { + blocks = await this.connection.blocksRepository.recent(10); + blocks = blocks.map(block => block.id); + } + + return blocks; + } + + public async getTopBlocks(count: any) { + const blocks = await this.connection.blocksRepository.top(count); + + await this.loadTransactionsForBlocks(blocks); + + return blocks; + } + + public async getTransaction(id: string) { + return this.connection.transactionsRepository.findById(id); + } + + public async loadBlocksFromCurrentRound() { + this.blocksInCurrentRound = await this.getBlocksForRound(); + } + + public async loadTransactionsForBlocks(blocks) { + if (!blocks.length) { + return; + } + + const ids = blocks.map(block => block.id); + + let transactions = await this.connection.transactionsRepository.latestByBlocks(ids); + transactions = transactions.map(tx => { + const data = Transaction.deserialize(tx.serialized.toString("hex")); + data.blockId = tx.blockId; + return data; + }); + + for (const block of blocks) { + if (block.numberOfTransactions > 0) { + block.transactions = transactions.filter(transaction => transaction.blockId === block.id); + } + } + } + + public async revertBlock(block: models.Block) { + await this.revertRound(block.data.height); + await this.walletManager.revertBlock(block); + + assert(this.blocksInCurrentRound.pop().data.id === block.data.id); + + this.emitter.emit("block.reverted", block.data); + } + + public async revertRound(height: number) { + const { round, nextRound, maxDelegates } = roundCalculator.calculateRound(height); + + if (nextRound === round + 1 && height >= maxDelegates) { + this.logger.info(`Back to previous round: ${round.toLocaleString()} :back:`); + + const delegates = await this.calcPreviousActiveDelegates(round); + this.forgingDelegates = await this.getActiveDelegates(height, delegates); + + await this.deleteRound(nextRound); + } + } + + public async saveBlock(block: models.Block) { + await this.connection.saveBlock(block); + } + + public async saveRound(activeDelegates: any[]) { + this.logger.info(`Saving round ${activeDelegates[0].round.toLocaleString()}`); + + await this.connection.roundsRepository.insert(activeDelegates); + + this.emitter.emit("round.created", activeDelegates); + } + + public async saveWallets(force: boolean) { + const wallets = this.walletManager + .allByPublicKey() + .filter(wallet => wallet.publicKey && (force || wallet.dirty)); + + // Remove dirty flags first to not save all dirty wallets in the exit handler + // when called during a force insert right after SPV. + this.walletManager.clear(); + + await this.connection.saveWallets(wallets, force); + + this.logger.info(`${wallets.length} modified ${pluralize("wallet", wallets.length)} committed to database`); + + this.emitter.emit("wallet.saved", wallets.length); + + // NOTE: commented out as more use cases to be taken care of + // this.walletManager.purgeEmptyNonDelegates() + } + + public updateDelegateStats(delegates: any[]): void { + if (!delegates || !this.blocksInCurrentRound) { + return; + } + + this.logger.debug("Updating delegate statistics"); + + try { + delegates.forEach(delegate => { + const producedBlocks = this.blocksInCurrentRound.filter( + blockGenerator => blockGenerator.data.generatorPublicKey === delegate.publicKey, + ); + const wallet = this.walletManager.findByPublicKey(delegate.publicKey); + + if (producedBlocks.length === 0) { + wallet.missedBlocks++; + this.logger.debug( + `Delegate ${wallet.username} (${wallet.publicKey}) just missed a block. Total: ${ + wallet.missedBlocks + }`, + ); + wallet.dirty = true; + this.emitter.emit("forger.missing", { + delegate: wallet, + }); + } + }); + } catch (error) { + this.logger.error(error.stack); + } + } + + public async verifyBlockchain(): Promise<{ valid: boolean; errors: any[] }> { + const errors = []; + + const lastBlock = await this.getLastBlock(); + + // Last block is available + if (!lastBlock) { + errors.push("Last block is not available"); + } else { + const numberOfBlocks = await this.connection.blocksRepository.count(); + + // Last block height equals the number of stored blocks + if (lastBlock.data.height !== +numberOfBlocks) { + errors.push( + `Last block height: ${lastBlock.data.height.toLocaleString()}, number of stored blocks: ${numberOfBlocks}`, + ); + } + } + + const blockStats = await this.connection.blocksRepository.statistics(); + const transactionStats = await this.connection.transactionsRepository.statistics(); + + // Number of stored transactions equals the sum of block.numberOfTransactions in the database + if (blockStats.numberOfTransactions !== transactionStats.count) { + errors.push( + `Number of transactions: ${transactionStats.count}, number of transactions included in blocks: ${ + blockStats.numberOfTransactions + }`, + ); + } + + // Sum of all tx fees equals the sum of block.totalFee + if (blockStats.totalFee !== transactionStats.totalFee) { + errors.push( + `Total transaction fees: ${transactionStats.totalFee}, total of block.totalFee : ${ + blockStats.totalFee + }`, + ); + } + + // Sum of all tx amount equals the sum of block.totalAmount + if (blockStats.totalAmount !== transactionStats.totalAmount) { + errors.push( + `Total transaction amounts: ${transactionStats.totalAmount}, total of block.totalAmount : ${ + blockStats.totalAmount + }`, + ); + } + + return { + valid: !errors.length, + errors, + }; + } + + public async verifyTransaction(transaction: models.Transaction) { + const senderId = arkCrypto.getAddress(transaction.data.senderPublicKey, this.config.get("network.pubKeyHash")); + + const sender = this.walletManager.findByAddress(senderId); // should exist + + if (!sender.publicKey) { + sender.publicKey = transaction.data.senderPublicKey; + this.walletManager.reindex(sender); + } + + const dbTransaction = await this.getTransaction(transaction.data.id); + + return sender.canApply(transaction.data, []) && !dbTransaction; + } + + private async calcPreviousActiveDelegates(round: number) { + // TODO: cache the blocks of the last X rounds + this.blocksInCurrentRound = await this.getBlocksForRound(round); + + // Create temp wallet manager from all delegates + const tempWalletManager = new WalletManager(); + tempWalletManager.index(cloneDeep(this.walletManager.allByUsername())); + + // Revert all blocks in reverse order + let height = 0; + for (let i = this.blocksInCurrentRound.length - 1; i >= 0; i--) { + tempWalletManager.revertBlock(this.blocksInCurrentRound[i]); + height = this.blocksInCurrentRound[i].data.height; + } + + // The first round has no active delegates + if (height === 1) { + return []; + } + + // Assert that the height is the beginning of a round. + const { maxDelegates } = roundCalculator.calculateRound(height); + assert(height > 1 && height % maxDelegates === 1); + + // Now retrieve the active delegate list from the temporary wallet manager. + return tempWalletManager.loadActiveDelegateList(maxDelegates, height); + } + + private emitTransactionEvents(transaction) { + this.emitter.emit("transaction.applied", transaction.data); + + if (transaction.type === TransactionTypes.DelegateRegistration) { + this.emitter.emit("delegate.registered", transaction.data); + } + + if (transaction.type === TransactionTypes.DelegateResignation) { + this.emitter.emit("delegate.resigned", transaction.data); + } + + if (transaction.type === TransactionTypes.Vote) { + const vote = transaction.asset.votes[0]; + + this.emitter.emit(vote.startsWith("+") ? "wallet.vote" : "wallet.unvote", { + delegate: vote, + transaction: transaction.data, + }); + } + } + + private registerListeners() { + + this.emitter.on("state:started", () => { + this.stateStarted = true; + }); + + this.emitter.on("wallet.created.cold", async coldWallet => { + try { + const wallet = await this.connection.walletsRepository.findByAddress(coldWallet.address); + + if (wallet) { + Object.keys(wallet).forEach(key => { + if (["balance"].indexOf(key) !== -1) { + return; + } + + coldWallet[key] = key !== "voteBalance" ? wallet[key] : new Bignum(wallet[key]); + }); + } + } catch (err) { + this.logger.error(err); + } + }); + + this.emitter.once("shutdown", async () => { + if (!this.spvFinished) { + // Prevent dirty wallets to be saved when SPV didn't finish + this.walletManager.clear(); + } + }); + } + +} diff --git a/packages/core-database/src/index.ts b/packages/core-database/src/index.ts new file mode 100644 index 0000000000..51340494aa --- /dev/null +++ b/packages/core-database/src/index.ts @@ -0,0 +1,6 @@ +export * from "./manager"; +export * from "./database-service-factory"; +export * from "./wallet-manager"; +export * from "./repositories/delegates"; +export * from "./repositories/wallets"; +export * from "./plugin"; diff --git a/packages/core-database/src/manager.ts b/packages/core-database/src/manager.ts new file mode 100644 index 0000000000..41721bcd62 --- /dev/null +++ b/packages/core-database/src/manager.ts @@ -0,0 +1,33 @@ +import { Database } from "@arkecosystem/core-interfaces"; + +export class DatabaseManager { + public connections: { [key: string]: Database.IDatabaseConnection }; + + /** + * Create a new database manager instance. + * @constructor + */ + constructor() { + this.connections = {}; + } + + /** + * Get a database connection instance. + * @param {String} name + * @return {DatabaseConnection} + */ + public connection(name = "default"): Database.IDatabaseConnection { + return this.connections[name]; + } + + /** + * Make the database connection instance. + * @param {DatabaseConnection} connection + * @param {String} name + * @return {void} + */ + public async makeConnection(connection: Database.IDatabaseConnection, name = "default"): Promise { + this.connections[name] = await connection.make(); + return this.connection(name); + } +} diff --git a/packages/core-database/src/plugin.ts b/packages/core-database/src/plugin.ts new file mode 100644 index 0000000000..b4083c984e --- /dev/null +++ b/packages/core-database/src/plugin.ts @@ -0,0 +1,12 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { DatabaseManager } from "./manager"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + alias: "databaseManager", + async register(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("Starting Database Manager"); + + return new DatabaseManager(); + }, +}; diff --git a/packages/core-database/src/repositories/delegates.ts b/packages/core-database/src/repositories/delegates.ts new file mode 100644 index 0000000000..f273070024 --- /dev/null +++ b/packages/core-database/src/repositories/delegates.ts @@ -0,0 +1,126 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { delegateCalculator } from "@arkecosystem/core-utils"; +import orderBy from "lodash/orderBy"; +import limitRows from "./utils/limit-rows"; + +export class DelegatesRepository implements Database.IDelegatesBusinessRepository { + + /** + * Create a new delegate repository instance. + * @param databaseServiceProvider + */ + public constructor(private databaseServiceProvider : () => Database.IDatabaseService) {} + + /** + * Get all local delegates. + */ + public getLocalDelegates() { + // TODO: What's the diff between this and just calling 'allByUsername' + return this.databaseServiceProvider().walletManager.allByAddress().filter(wallet => !!wallet.username); + } + + /** + * Find all delegates. + * @param {Object} params + * @return {Object} + */ + public findAll(params: Database.IParameters = {}) { + const delegates = this.getLocalDelegates(); + + const [iteratee, order] = this.__orderBy(params); + + return { + rows: limitRows(orderBy(delegates, iteratee, order as "desc" | "asc"), params), + count: delegates.length, + }; + } + + /** + * Search all delegates. + * TODO Currently it searches by username only + * @param {Object} [params] + * @param {String} [params.username] - Search by username + */ + public search(params : Database.IParameters) { + let delegates = this.getLocalDelegates(); + if (params.hasOwnProperty("username")) { + delegates = delegates.filter(delegate => delegate.username.indexOf(params.username as string) > -1); + } + + if (params.orderBy) { + const orderByField = params.orderBy.split(":")[0]; + const orderByDirection = params.orderBy.split(":")[1] || "desc"; + + delegates = delegates.sort((a, b) => { + if (orderByDirection === "desc" && a[orderByField] < b[orderByField]) { + return -1; + } + + if (orderByDirection === "asc" && a[orderByField] > b[orderByField]) { + return 1; + } + + return 0; + }); + } + + return { + rows: limitRows(delegates, params), + count: delegates.length, + }; + } + + /** + * Find a delegate. + * @param {String} id + * @return {Object} + */ + public findById(id) { + return this.getLocalDelegates().find(a => a.address === id || a.publicKey === id || a.username === id); + } + + /** + * Find all active delegates at height. + * @param {Number} height + * @return {Array} + */ + public async getActiveAtHeight(height: number) { + const delegates = await this.databaseServiceProvider().getActiveDelegates(height); + + return delegates.map(delegate => { + const wallet = this.databaseServiceProvider().wallets.findById(delegate.publicKey); + + return { + username: wallet.username, + approval: delegateCalculator.calculateApproval(delegate, height), + productivity: delegateCalculator.calculateProductivity(wallet), + }; + }); + } + + public __orderBy(params): string[] { + if (!params.orderBy) { + return ["rate", "asc"]; + } + + const orderByMapped = params.orderBy.split(":").map(p => p.toLowerCase()); + if (orderByMapped.length !== 2 || ["desc", "asc"].includes(orderByMapped[1]) !== true) { + return ["rate", "asc"]; + } + + return [this.__manipulateIteratee(orderByMapped[0]), orderByMapped[1]]; + } + + public __manipulateIteratee(iteratee): any { + switch (iteratee) { + case "rank": + return "rate"; + case "productivity": + return delegateCalculator.calculateProductivity; + case "approval": + return delegateCalculator.calculateApproval; + default: + return iteratee; + } + } +} diff --git a/packages/core-database/src/repositories/utils/filter-rows.ts b/packages/core-database/src/repositories/utils/filter-rows.ts new file mode 100644 index 0000000000..0cf7962b95 --- /dev/null +++ b/packages/core-database/src/repositories/utils/filter-rows.ts @@ -0,0 +1,74 @@ +/** + * Filter an Array of Objects based on the given parameters. + * @param {Array} rows + * @param {Object} params + * @param {Object} filters + * @return {Array} + */ +export = (rows: T[], params, filters) => + rows.filter(item => { + if (filters.hasOwnProperty("exact")) { + for (const elem of filters.exact) { + if (params[elem] && item[elem] !== params[elem]) { + return false; + } + } + } + + if (filters.hasOwnProperty("between")) { + for (const elem of filters.between) { + if (!params[elem]) { + continue; + } + + if ( + !params[elem].hasOwnProperty("from") && + !params[elem].hasOwnProperty("to") && + item[elem] !== params[elem] + ) { + return false; + } + + if (params[elem].hasOwnProperty("from") || params[elem].hasOwnProperty("to")) { + let isMoreThan = true; + let isLessThan = true; + + if (params[elem].hasOwnProperty("from")) { + isMoreThan = item[elem] >= params[elem].from; + } + + if (params[elem].hasOwnProperty("to")) { + isLessThan = item[elem] <= params[elem].to; + } + + return isMoreThan && isLessThan; + } + } + } + + if (filters.hasOwnProperty("in")) { + for (const elem of filters.in) { + if (params[elem] && Array.isArray(params[elem])) { + return params[elem].indexOf(item[elem]) > -1; + } + } + } + + // NOTE: it was used to filter by `votes`, but that field was rejected and + // replaced by `vote`. This filter is kept here just in case + if (filters.hasOwnProperty("any")) { + for (const elem of filters.any) { + if (params[elem] && item[elem]) { + if (Array.isArray(params[elem])) { + if (item[elem].every(a => params[elem].indexOf(a) === -1)) { + return false; + } + } else { + throw new Error('Fitering by "any" requires an Array'); + } + } + } + } + + return true; + }); diff --git a/packages/core-database/src/repositories/utils/limit-rows.ts b/packages/core-database/src/repositories/utils/limit-rows.ts new file mode 100644 index 0000000000..169521762c --- /dev/null +++ b/packages/core-database/src/repositories/utils/limit-rows.ts @@ -0,0 +1,14 @@ +import { Database } from "@arkecosystem/core-interfaces"; +/** + * Return some rows by an offset and a limit. + */ +export = (rows: T[], params: Database.IParameters) => { + if (params.offset || params.limit) { + const offset = params.offset || 0; + const limit = params.limit ? offset + params.limit : rows.length; + + return rows.slice(offset, limit); + } + + return rows; +}; diff --git a/packages/core-database/src/repositories/wallets.ts b/packages/core-database/src/repositories/wallets.ts new file mode 100644 index 0000000000..1df7856c1c --- /dev/null +++ b/packages/core-database/src/repositories/wallets.ts @@ -0,0 +1,121 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import orderBy from "lodash/orderBy"; +import filterRows from "./utils/filter-rows"; +import limitRows from "./utils/limit-rows"; + +export class WalletsRepository implements Database.IWalletsBusinessRepository { + /** + * Create a new wallet repository instance. + * @param {DatabaseConnection} databaseService + */ + public constructor(private databaseServiceProvider : () => Database.IDatabaseService) {} + + /** + * Get all local wallets. + * @return {Array} + */ + public all() { + return this.databaseServiceProvider().walletManager.allByAddress(); + } + + /** + * Find all wallets. + * @param {{ orderBy?: string }} params + * @return {Object} + */ + public findAll(params: Database.IParameters = {}) { + const wallets = this.all(); + + const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : ["rate", "asc"]; + + return { + rows: limitRows(orderBy(wallets, iteratee, order as "desc" | "asc"), params), + count: wallets.length, + }; + } + + /** + * Find all wallets for the given vote. + * @param {String} publicKey + * @param {Object} params + * @return {Object} + */ + public findAllByVote(publicKey: string, params: Database.IParameters = {}) { + const wallets = this.all().filter(wallet => wallet.vote === publicKey); + + return { + rows: limitRows(wallets, params), + count: wallets.length, + }; + } + + /** + * Find a wallet by address, public key or username. + */ + public findById(id: string) { + return this.all().find(wallet => wallet.address === id || wallet.publicKey === id || wallet.username === id); + } + + /** + * Count all wallets. + */ + public count() { + return this.all().length; + } + + /** + * Find all wallets sorted by balance. + */ + public top(params: Database.IParameters = {}) { + const wallets = Object.values(this.all()).sort((a: any, b: any) => +b.balance.minus(a.balance).toFixed()); + + return { + rows: limitRows(wallets, params), + count: wallets.length, + }; + } + + /** + * Search all wallets. + * @param {Object} [params] + * @param {Number} [params.limit] - Limit the number of results + * @param {Number} [params.offset] - Skip some results + * @param {Array} [params.orderBy] - Order of the results + * @param {String} [params.address] - Search by address + * @param {Array} [params.addresses] - Search by several addresses + * @param {String} [params.publicKey] - Search by publicKey + * @param {String} [params.secondPublicKey] - Search by secondPublicKey + * @param {String} [params.username] - Search by username + * @param {String} [params.vote] - Search by vote + * @param {Object} [params.balance] - Search by balance + * @param {Number} [params.balance.from] - Search by balance (minimum) + * @param {Number} [params.balance.to] - Search by balance (maximum) + * @param {Object} [params.voteBalance] - Search by voteBalance + * @param {Number} [params.voteBalance.from] - Search by voteBalance (minimum) + * @param {Number} [params.voteBalance.to] - Search by voteBalance (maximum) + * @return {Object} + */ + public search(params: T) { + const query: any = { + exact: ["address", "publicKey", "secondPublicKey", "username", "vote"], + between: ["balance", "voteBalance"], + }; + + if (params.addresses) { + // Use the `in` filter instead of `exact` for the `address` field + if (!params.address) { + params.address = params.addresses; + query.exact.shift(); + query.in = ["address"]; + } + delete params.addresses; + } + + const wallets = filterRows(this.all(), params, query); + + return { + rows: limitRows(wallets, params), + count: wallets.length, + }; + } +} diff --git a/packages/core-database/src/wallet-manager.ts b/packages/core-database/src/wallet-manager.ts new file mode 100644 index 0000000000..886d5f1a4d --- /dev/null +++ b/packages/core-database/src/wallet-manager.ts @@ -0,0 +1,553 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, Logger } from "@arkecosystem/core-interfaces"; +import { roundCalculator } from "@arkecosystem/core-utils"; +import { Bignum, constants, crypto, formatArktoshi, isException, models } from "@arkecosystem/crypto"; +import pluralize from "pluralize"; + +const { Wallet } = models; +const { TransactionTypes } = constants; + +export class WalletManager implements Database.IWalletManager { + public logger = app.resolvePlugin("logger"); + public config = app.getConfig(); + + public networkId: number; + public byAddress: { [key: string]: any }; + public byPublicKey: { [key: string]: any }; + public byUsername: { [key: string]: any }; + + /** + * Create a new wallet manager instance. + * @constructor + */ + constructor() { + this.networkId = this.config ? this.config.get("network.pubKeyHash") : 0x17; + this.reset(); + } + + public allByAddress(): models.Wallet[] { + return Object.values(this.byAddress); + } + + /** + * Get all wallets by publicKey. + */ + public allByPublicKey(): models.Wallet[] { + return Object.values(this.byPublicKey); + } + + /** + * Get all wallets by username. + * @return {Array} + */ + public allByUsername(): models.Wallet[] { + return Object.values(this.byUsername); + } + + /** + * Find a wallet by the given address. + */ + public findByAddress(address: string): models.Wallet { + if (!this.byAddress[address]) { + this.byAddress[address] = new Wallet(address); + } + + return this.byAddress[address]; + } + + /** + * Checks if wallet exits in wallet manager + * @param {String} key can be publicKey or address of wallet + */ + public exists(key: string) { + if (this.byPublicKey[key]) { + return true; + } + + return !!this.byAddress[key]; + } + + /** + * Find a wallet by the given public key. + * @param {String} publicKey + * @return {Wallet} + */ + public findByPublicKey(publicKey: string): models.Wallet { + if (!this.byPublicKey[publicKey]) { + const address = crypto.getAddress(publicKey, this.networkId); + + const wallet = this.findByAddress(address); + wallet.publicKey = publicKey; + this.byPublicKey[publicKey] = wallet; + } + + return this.byPublicKey[publicKey]; + } + + /** + * Find a wallet by the given username. + * @param {String} username + * @return {Wallet} + */ + public findByUsername(username: string): models.Wallet { + return this.byUsername[username]; + } + + /** + * Set wallet by address. + * @param {String} address + * @param {Wallet} wallet + */ + public setByAddress(address, wallet) { + this.byAddress[address] = wallet; + } + + /** + * Set wallet by publicKey. + * @param {String} publicKey + * @param {Wallet} wallet + */ + public setByPublicKey(publicKey, wallet) { + this.byPublicKey[publicKey] = wallet; + } + + /** + * Set wallet by username. + * @param {String} username + * @param {Wallet} wallet + */ + public setByUsername(username, wallet) { + this.byUsername[username] = wallet; + } + + /** + * Remove wallet by address. + * @param {String} address + */ + public forgetByAddress(address) { + delete this.byAddress[address]; + } + + /** + * Remove wallet by publicKey. + * @param {String} publicKey + */ + public forgetByPublicKey(publicKey) { + delete this.byPublicKey[publicKey]; + } + + /** + * Remove wallet by username. + * @param {String} username + */ + public forgetByUsername(username) { + delete this.byUsername[username]; + } + + /** + * Index the given wallets. + * @param {Array} wallets + * @return {void} + */ + public index(wallets) { + for (const wallet of wallets) { + this.reindex(wallet); + } + } + + /** + * Reindex the given wallet. + * @param {Wallet} wallet + * @return {void} + */ + public reindex(wallet: models.Wallet) { + if (wallet.address) { + this.byAddress[wallet.address] = wallet; + } + + if (wallet.publicKey) { + this.byPublicKey[wallet.publicKey] = wallet; + } + + if (wallet.username) { + this.byUsername[wallet.username] = wallet; + } + } + + public clear() { + Object.values(this.byAddress).forEach(wallet => { + wallet.dirty = false; + }); + } + + /** + * Load a list of all active delegates. + * @param {Number} maxDelegates + * @param height + * @return {Array} + */ + public loadActiveDelegateList(maxDelegates: number, height?: number): any[] { + if (height > 1 && height % maxDelegates !== 1) { + throw new Error("Trying to build delegates outside of round change"); + } + + const { round } = roundCalculator.calculateRound(height, maxDelegates); + const delegatesWallets = this.allByUsername(); + + if (delegatesWallets.length < maxDelegates) { + throw new Error( + `Expected to find ${maxDelegates} delegates but only found ${ + delegatesWallets.length + }. This indicates an issue with the genesis block & delegates.`, + ); + } + + const equalVotesMap = new Map(); + + const delegates = delegatesWallets + .sort((a, b) => { + const diff = b.voteBalance.comparedTo(a.voteBalance); + + if (diff === 0) { + if (!equalVotesMap.has(a.voteBalance.toFixed())) { + equalVotesMap.set(a.voteBalance.toFixed(), new Set()); + } + + const set = equalVotesMap.get(a.voteBalance.toFixed()); + set.add(a); + set.add(b); + + if (a.publicKey === b.publicKey) { + throw new Error( + `The balance and public key of both delegates are identical! Delegate "${ + a.username + }" appears twice in the list.`, + ); + } + + return a.publicKey.localeCompare(b.publicKey, "en"); + } + + return diff; + }) + .map((delegate, i) => { + const rate = i + 1; + this.byUsername[delegate.username].rate = rate; + return { ...{ round }, ...delegate, rate }; + }) + .slice(0, maxDelegates); + + for (const [voteBalance, set] of equalVotesMap.entries()) { + const values: any[] = Array.from(set.values()); + if (delegates.includes(values[0])) { + const mapped = values.map(v => `${v.username} (${v.publicKey})`); + this.logger.warn( + `Delegates ${JSON.stringify(mapped, null, 4)} have a matching vote balance of ${formatArktoshi( + voteBalance, + )}`, + ); + } + } + + this.logger.debug(`Loaded ${delegates.length} active ${pluralize("delegate", delegates.length)}`); + + return delegates; + } + + /** + * Build vote balances of all delegates. + * NOTE: Only called during SPV. + * @return {void} + */ + public buildVoteBalances() { + Object.values(this.byPublicKey).forEach(voter => { + if (voter.vote) { + const delegate = this.byPublicKey[voter.vote]; + delegate.voteBalance = delegate.voteBalance.plus(voter.balance); + } + }); + } + + /** + * Remove non-delegate wallets that have zero (0) balance from memory. + * @return {void} + */ + public purgeEmptyNonDelegates() { + Object.values(this.byPublicKey).forEach(wallet => { + if (this.canBePurged(wallet)) { + delete this.byPublicKey[wallet.publicKey]; + delete this.byAddress[wallet.address]; + } + }); + } + + /** + * Apply the given block to a delegate. + * @param {Block} block + * @return {void} + */ + public applyBlock(block: models.Block) { + const generatorPublicKey = block.data.generatorPublicKey; + + let delegate = this.byPublicKey[block.data.generatorPublicKey]; + + if (!delegate) { + const generator = crypto.getAddress(generatorPublicKey, this.networkId); + + if (block.data.height === 1) { + delegate = new Wallet(generator); + delegate.publicKey = generatorPublicKey; + + this.reindex(delegate); + } else { + this.logger.debug(`Delegate by address: ${this.byAddress[generator]}`); + + if (this.byAddress[generator]) { + this.logger.info("This look like a bug, please report :bug:"); + } + + throw new Error(`Could not find delegate with publicKey ${generatorPublicKey}`); + } + } + + const appliedTransactions = []; + + try { + block.transactions.forEach(transaction => { + this.applyTransaction(transaction); + appliedTransactions.push(transaction); + }); + + const applied = delegate.applyBlock(block.data); + + // If the block has been applied to the delegate, the balance is increased + // by reward + totalFee. In which case the vote balance of the + // delegate's delegate has to be updated. + if (applied && delegate.vote) { + const increase = (block.data.reward as Bignum).plus(block.data.totalFee); + const votedDelegate = this.byPublicKey[delegate.vote]; + votedDelegate.voteBalance = votedDelegate.voteBalance.plus(increase); + } + } catch (error) { + this.logger.error("Failed to apply all transactions in block - reverting previous transactions"); + // Revert the applied transactions from last to first + for (let i = appliedTransactions.length - 1; i >= 0; i--) { + this.revertTransaction(appliedTransactions[i]); + } + + // TODO: should revert the delegate applyBlock ? + // TBC: whatever situation `delegate.applyBlock(block.data)` is never applied + + throw error; + } + } + + /** + * Remove the given block from a delegate. + * @param {Block} block + * @return {void} + */ + public revertBlock(block: models.Block) { + const delegate = this.byPublicKey[block.data.generatorPublicKey]; + + if (!delegate) { + app.forceExit( + `Failed to lookup generator '${block.data.generatorPublicKey}' of block '${block.data.id}'. :skull:`, + ); + } + + const revertedTransactions = []; + + try { + // Revert the transactions from last to first + for (let i = block.transactions.length - 1; i >= 0; i--) { + const transaction = block.transactions[i]; + this.revertTransaction(transaction); + revertedTransactions.push(transaction); + } + + const reverted = delegate.revertBlock(block.data); + + // If the block has been reverted, the balance is decreased + // by reward + totalFee. In which case the vote balance of the + // delegate's delegate has to be updated. + if (reverted && delegate.vote) { + const decrease = (block.data.reward as Bignum).plus(block.data.totalFee); + const votedDelegate = this.byPublicKey[delegate.vote]; + votedDelegate.voteBalance = votedDelegate.voteBalance.minus(decrease); + } + } catch (error) { + this.logger.error(error.stack); + + revertedTransactions.reverse().forEach(transaction => this.applyTransaction(transaction)); + + throw error; + } + } + + /** + * Apply the given transaction to a delegate. + * @param {Transaction} transaction + * @return {Transaction} + */ + public applyTransaction(transaction: models.Transaction) { + const { data } = transaction; + const { type, asset, recipientId, senderPublicKey } = data; + + const sender = this.findByPublicKey(senderPublicKey); + const recipient = this.findByAddress(recipientId); + const errors = []; + + // specific verifications / adjustments depending on transaction type + if (type === TransactionTypes.DelegateRegistration && this.byUsername[asset.delegate.username.toLowerCase()]) { + this.logger.error( + `Can't apply transaction ${ + data.id + }: delegate name '${asset.delegate.username.toLowerCase()}' already taken.`, + ); + throw new Error(`Can't apply transaction ${data.id}: delegate name already taken.`); + + // NOTE: We use the vote public key, because vote transactions + // have the same sender and recipient + } else if (type === TransactionTypes.Vote && !this.isDelegate(asset.votes[0].slice(1))) { + this.logger.error(`Can't apply vote transaction ${data.id}: delegate ${asset.votes[0]} does not exist.`); + throw new Error(`Can't apply transaction ${data.id}: delegate ${asset.votes[0]} does not exist.`); + } else if (type === TransactionTypes.SecondSignature) { + data.recipientId = ""; + } + + // handle exceptions / verify that we can apply the transaction to the sender + if (isException(data)) { + this.logger.warn(`Transaction ${data.id} forcibly applied because it has been added as an exception.`); + } else if (!sender.canApply(data, errors)) { + this.logger.error( + `Can't apply transaction id:${data.id} from sender:${sender.address} due to ${JSON.stringify(errors)}`, + ); + this.logger.debug(`Audit: ${JSON.stringify(sender.auditApply(data), null, 2)}`); + throw new Error(`Can't apply transaction ${data.id}`); + } + + sender.applyTransactionToSender(data); + + if (type === TransactionTypes.DelegateRegistration) { + this.reindex(sender); + } + + if (recipient && type === TransactionTypes.Transfer) { + recipient.applyTransactionToRecipient(data); + } + + this._updateVoteBalances(sender, recipient, data); + + return transaction; + } + + /** + * Updates the vote balances of the respective delegates of sender and recipient. + * If the transaction is not a vote... + * 1. fee + amount is removed from the sender's delegate vote balance + * 2. amount is added to the recipient's delegate vote balance + * + * in case of a vote... + * 1. the full sender balance is added to the sender's delegate vote balance + * + * If revert is set to true, the operations are reversed (plus -> minus, minus -> plus). + * @param {Wallet} sender + * @param {Wallet} recipient + * @param {Transaction} transaction + * @param {Boolean} revert + * @return {Transaction} + */ + public _updateVoteBalances(sender, recipient, transaction, revert = false) { + // TODO: multipayment? + if (transaction.type !== TransactionTypes.Vote) { + // Update vote balance of the sender's delegate + if (sender.vote) { + const delegate = this.findByPublicKey(sender.vote); + const total = transaction.amount.plus(transaction.fee); + delegate.voteBalance = revert ? delegate.voteBalance.plus(total) : delegate.voteBalance.minus(total); + } + + // Update vote balance of recipient's delegate + if (recipient && recipient.vote) { + const delegate = this.findByPublicKey(recipient.vote); + delegate.voteBalance = revert + ? delegate.voteBalance.minus(transaction.amount) + : delegate.voteBalance.plus(transaction.amount); + } + } else { + const vote = transaction.asset.votes[0]; + const delegate = this.findByPublicKey(vote.substr(1)); + + if (vote.startsWith("+")) { + delegate.voteBalance = revert + ? delegate.voteBalance.minus(sender.balance) + : delegate.voteBalance.plus(sender.balance); + } else { + delegate.voteBalance = revert + ? delegate.voteBalance.plus(sender.balance.plus(transaction.fee)) + : delegate.voteBalance.minus(sender.balance.plus(transaction.fee)); + } + } + } + + /** + * Remove the given transaction from a delegate. + * @param {Transaction} transaction + * @return {Transaction} + */ + public revertTransaction(transaction: models.Transaction) { + const { type, data } = transaction; + const sender = this.findByPublicKey(data.senderPublicKey); // Should exist + const recipient = this.byAddress[data.recipientId]; + + sender.revertTransactionForSender(data); + + // removing the wallet from the delegates index + if (type === TransactionTypes.DelegateRegistration) { + delete this.byUsername[data.asset.delegate.username]; + } + + if (recipient && type === TransactionTypes.Transfer) { + recipient.revertTransactionForRecipient(data); + } + + // Revert vote balance updates + this._updateVoteBalances(sender, recipient, data, true); + + return data; + } + + /** + * Checks if a given publicKey is a registered delegate + * @param {String} publicKey + */ + public isDelegate(publicKey: string) { + const delegateWallet = this.byPublicKey[publicKey]; + + if (delegateWallet && delegateWallet.username) { + return !!this.byUsername[delegateWallet.username]; + } + + return false; + } + + /** + * Determine if the wallet can be removed from memory. + * @param {Object} wallet + * @return {Boolean} + */ + public canBePurged(wallet) { + return wallet.balance.isZero() && !wallet.secondPublicKey && !wallet.multisignature && !wallet.username; + } + + /** + * Reset the wallets index. + * @return {void} + */ + public reset() { + this.byAddress = {}; + this.byPublicKey = {}; + this.byUsername = {}; + } +} diff --git a/packages/core-database/tsconfig.json b/packages/core-database/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-database/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-debugger-cli/CHANGELOG.md b/packages/core-debugger-cli/CHANGELOG.md deleted file mode 100644 index 95a15b0628..0000000000 --- a/packages/core-debugger-cli/CHANGELOG.md +++ /dev/null @@ -1,26 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Retrieve identities -- Verify second signature - -### Changed - -- Change `transaction.serialized` from `Buffer` to hex -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.0 - 2018-10-02 - -### Added - -- initial release diff --git a/packages/core-debugger-cli/LICENSE b/packages/core-debugger-cli/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-debugger-cli/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-debugger-cli/README.md b/packages/core-debugger-cli/README.md index 5560b6d780..bc6530e60b 100644 --- a/packages/core-debugger-cli/README.md +++ b/packages/core-debugger-cli/README.md @@ -14,8 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-debugger-cli/__tests__/__fixtures__/block.json b/packages/core-debugger-cli/__tests__/__fixtures__/block.json index 85acc88b6f..d1228aa285 100644 --- a/packages/core-debugger-cli/__tests__/__fixtures__/block.json +++ b/packages/core-debugger-cli/__tests__/__fixtures__/block.json @@ -1,140 +1,140 @@ { - "data": { - "id": "7176646138626297930", - "version": 0, - "height": 2243161, - "timestamp": 24760440, - "previousBlock": "3112633353705641986", - "numberOfTransactions": 7, - "totalAmount": "3890300", - "totalFee": "70000000", - "reward": "200000000", - "payloadLength": 224, - "payloadHash": "3784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282", - "generatorPublicKey": "020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a325", - "blockSignature": "3045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", - "transactions": [ - { - "type": 0, - "amount": 555760, - "fee": 10000000, - "recipientId": "DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY", - "timestamp": 24760418, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb978", - "signSignature": "30450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717", - "id": "170543154a3b79459cbaa529f9f62b6f1342682799eb549dbf09fcca2d1f9c11", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - }, - { - "type": 0, - "amount": 555750, - "fee": 10000000, - "recipientId": "DGExsNogZR7JFa2656ZFP9TMWJYJh5djzQ", - "timestamp": 24760416, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727", - "signSignature": "304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9", - "id": "1da153f37eceda233ff1b407ac18e47b3cae47c14cdcd5297d929618a916c4a7", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - }, - { - "type": 0, - "amount": 555770, - "fee": 10000000, - "recipientId": "DHGK5np6LuMMErfRfC5CmjpGu3ME85c25n", - "timestamp": 24760420, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a", - "signSignature": "3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099f", - "id": "1e255f07dc25ce22d900ea81663c8f00d05a7b7c061e6fc3c731b05d642fa0b9", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - }, - { - "type": 0, - "amount": 555750, - "fee": 10000000, - "recipientId": "D7pcLJNGe197ibmWEmT8mM9KKU1htrcDyW", - "timestamp": 24760417, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11", - "signSignature": "304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79", - "id": "66336c61d6ec623f8a1d2fd156a0fac16a4fe93bb3fba337859355c2119923a8", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - }, - { - "type": 0, - "amount": 555760, - "fee": 10000000, - "recipientId": "DD4yhwzryQdNGqKtezmycToQv63g27Tqqq", - "timestamp": 24760418, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "30450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e8", - "signSignature": "30440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64", - "id": "78db36f7d79f51c67d7210ee3819dfb8d0d47b16a7484ebf55c5a055b17209a3", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - }, - { - "type": 0, - "amount": 555760, - "fee": 10000000, - "recipientId": "D5LiYGXL5keycWuTF6AFFwSRc6Mt4uEHMu", - "timestamp": 24760419, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e", - "signSignature": "3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739", - "id": "83c80bb58777bb43f5037544b44ef69f191d3548fd1b2a00bed368f9f0d694c5", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - }, - { - "type": 0, - "amount": 555750, - "fee": 10000000, - "recipientId": "DPopNLwMvv4zSjdZnqUk8HFH13Mcb7NbEK", - "timestamp": 24760416, - "asset": {}, - "vendorField": "Goose Voter - True Block Weight", - "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", - "signature": "3045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4", - "signSignature": "304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb", - "id": "d2faf992fdd5da96d6d15038b6ddb65230338fa2096e45e44da51daad5e2f3ca", - "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", - "hop": 2, - "broadcast": false, - "blockId": "7176646138626297930" - } - ] - }, - "serialized": "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", - "serializedFull": "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29ff000000fe00000000010000ff000000ff000000ff000000ff000000ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e40fad23d21da7a4fd4decb5c49726ea22f5e6bf6304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb97830450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e79c579fb08f448879c22fe965906b4e3b88d02ed304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9ff011e0064d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874fa7a080000000000000000001e84fee45dde2b11525afe192a2e991d014ff93a36304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099fff011e0061d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e1d69583ede5ee82d220e74bffb36bae2ce762dfb3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e56f9a37a859f4f84e93ce7593e809b15a524db2930450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e830440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64ff011e0063d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e0232a083c16aba4362dddec1b3050ffdd6d43f2e3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001eccc4fce0dc95f9951ee40c09a7ae807746cf51403045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb" + "data": { + "id": "7176646138626297930", + "version": 0, + "height": 2243161, + "timestamp": 24760440, + "previousBlock": "3112633353705641986", + "numberOfTransactions": 7, + "totalAmount": "3890300", + "totalFee": "70000000", + "reward": "200000000", + "payloadLength": 224, + "payloadHash": "3784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282", + "generatorPublicKey": "020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a325", + "blockSignature": "3045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", + "transactions": [ + { + "type": 0, + "amount": 555760, + "fee": 10000000, + "recipientId": "DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY", + "timestamp": 24760418, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb978", + "signSignature": "30450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717", + "id": "170543154a3b79459cbaa529f9f62b6f1342682799eb549dbf09fcca2d1f9c11", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + }, + { + "type": 0, + "amount": 555750, + "fee": 10000000, + "recipientId": "DGExsNogZR7JFa2656ZFP9TMWJYJh5djzQ", + "timestamp": 24760416, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727", + "signSignature": "304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9", + "id": "1da153f37eceda233ff1b407ac18e47b3cae47c14cdcd5297d929618a916c4a7", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + }, + { + "type": 0, + "amount": 555770, + "fee": 10000000, + "recipientId": "DHGK5np6LuMMErfRfC5CmjpGu3ME85c25n", + "timestamp": 24760420, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a", + "signSignature": "3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099f", + "id": "1e255f07dc25ce22d900ea81663c8f00d05a7b7c061e6fc3c731b05d642fa0b9", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + }, + { + "type": 0, + "amount": 555750, + "fee": 10000000, + "recipientId": "D7pcLJNGe197ibmWEmT8mM9KKU1htrcDyW", + "timestamp": 24760417, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11", + "signSignature": "304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79", + "id": "66336c61d6ec623f8a1d2fd156a0fac16a4fe93bb3fba337859355c2119923a8", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + }, + { + "type": 0, + "amount": 555760, + "fee": 10000000, + "recipientId": "DD4yhwzryQdNGqKtezmycToQv63g27Tqqq", + "timestamp": 24760418, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "30450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e8", + "signSignature": "30440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64", + "id": "78db36f7d79f51c67d7210ee3819dfb8d0d47b16a7484ebf55c5a055b17209a3", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + }, + { + "type": 0, + "amount": 555760, + "fee": 10000000, + "recipientId": "D5LiYGXL5keycWuTF6AFFwSRc6Mt4uEHMu", + "timestamp": 24760419, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e", + "signSignature": "3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739", + "id": "83c80bb58777bb43f5037544b44ef69f191d3548fd1b2a00bed368f9f0d694c5", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + }, + { + "type": 0, + "amount": 555750, + "fee": 10000000, + "recipientId": "DPopNLwMvv4zSjdZnqUk8HFH13Mcb7NbEK", + "timestamp": 24760416, + "asset": {}, + "vendorField": "Goose Voter - True Block Weight", + "senderPublicKey": "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + "signature": "3045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4", + "signSignature": "304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb", + "id": "d2faf992fdd5da96d6d15038b6ddb65230338fa2096e45e44da51daad5e2f3ca", + "senderId": "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + "hop": 2, + "broadcast": false, + "blockId": "7176646138626297930" + } + ] + }, + "serialized": "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", + "serializedFull": "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29ff000000fe00000000010000ff000000ff000000ff000000ff000000ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e40fad23d21da7a4fd4decb5c49726ea22f5e6bf6304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb97830450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e79c579fb08f448879c22fe965906b4e3b88d02ed304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9ff011e0064d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874fa7a080000000000000000001e84fee45dde2b11525afe192a2e991d014ff93a36304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099fff011e0061d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e1d69583ede5ee82d220e74bffb36bae2ce762dfb3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e56f9a37a859f4f84e93ce7593e809b15a524db2930450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e830440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64ff011e0063d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e0232a083c16aba4362dddec1b3050ffdd6d43f2e3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001eccc4fce0dc95f9951ee40c09a7ae807746cf51403045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb" } diff --git a/packages/core-debugger-cli/__tests__/__fixtures__/identities.json b/packages/core-debugger-cli/__tests__/__fixtures__/identities.json index 2ae9e9e891..71063bafbc 100644 --- a/packages/core-debugger-cli/__tests__/__fixtures__/identities.json +++ b/packages/core-debugger-cli/__tests__/__fixtures__/identities.json @@ -1,6 +1,6 @@ { - "passphrase": "this is a top secret passphrase", - "publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", - "privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", - "address": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib" + "passphrase": "this is a top secret passphrase", + "publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + "privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", + "address": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib" } diff --git a/packages/core-debugger-cli/__tests__/__fixtures__/transaction-second.json b/packages/core-debugger-cli/__tests__/__fixtures__/transaction-second.json index 7f6a589dca..8a0ebe0907 100644 --- a/packages/core-debugger-cli/__tests__/__fixtures__/transaction-second.json +++ b/packages/core-debugger-cli/__tests__/__fixtures__/transaction-second.json @@ -1,15 +1,15 @@ { - "data": { - "type": 0, - "amount": 200000000, - "fee": 10000000, - "recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", - "timestamp": 41268430, - "asset": {}, - "senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", - "signature": "304402206da703bfcc11ec2ccb3f363fa0e23fc64050fdf68e1f1852b7d4a5bb07824166022031ed1d86b586a79f9c1e5010dbc4f4cb36641c62a196536f90b1dfd6be1c9868", - "signSignature": "304402200759b6f9de5257aa3fcf54b9cd7a426a00af9368b7ea3d5ea2b13a91b97fb277022076e4d2d7deb9bdd8245b2533cab1eeeef72981e18576ef8455a61ee3e6f3fb57", - "id": "bb8054b6298d659d4b5d655e82de17b3504ba27655ec3d6e35d311f3104b1c43" - }, - "serialized": "ff011e00ceb47502034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed19280969800000000000000c2eb0b00000000000000001e0995750207ecaf0ccf251c1265b92ad84f553662304402206da703bfcc11ec2ccb3f363fa0e23fc64050fdf68e1f1852b7d4a5bb07824166022031ed1d86b586a79f9c1e5010dbc4f4cb36641c62a196536f90b1dfd6be1c9868304402200759b6f9de5257aa3fcf54b9cd7a426a00af9368b7ea3d5ea2b13a91b97fb277022076e4d2d7deb9bdd8245b2533cab1eeeef72981e18576ef8455a61ee3e6f3fb57" + "data": { + "type": 0, + "amount": 200000000, + "fee": 10000000, + "recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", + "timestamp": 41268430, + "asset": {}, + "senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + "signature": "304402206da703bfcc11ec2ccb3f363fa0e23fc64050fdf68e1f1852b7d4a5bb07824166022031ed1d86b586a79f9c1e5010dbc4f4cb36641c62a196536f90b1dfd6be1c9868", + "signSignature": "304402200759b6f9de5257aa3fcf54b9cd7a426a00af9368b7ea3d5ea2b13a91b97fb277022076e4d2d7deb9bdd8245b2533cab1eeeef72981e18576ef8455a61ee3e6f3fb57", + "id": "bb8054b6298d659d4b5d655e82de17b3504ba27655ec3d6e35d311f3104b1c43" + }, + "serialized": "ff011e00ceb47502034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed19280969800000000000000c2eb0b00000000000000001e0995750207ecaf0ccf251c1265b92ad84f553662304402206da703bfcc11ec2ccb3f363fa0e23fc64050fdf68e1f1852b7d4a5bb07824166022031ed1d86b586a79f9c1e5010dbc4f4cb36641c62a196536f90b1dfd6be1c9868304402200759b6f9de5257aa3fcf54b9cd7a426a00af9368b7ea3d5ea2b13a91b97fb277022076e4d2d7deb9bdd8245b2533cab1eeeef72981e18576ef8455a61ee3e6f3fb57" } diff --git a/packages/core-debugger-cli/__tests__/__fixtures__/transaction.json b/packages/core-debugger-cli/__tests__/__fixtures__/transaction.json index 74f4904255..70490d28c9 100644 --- a/packages/core-debugger-cli/__tests__/__fixtures__/transaction.json +++ b/packages/core-debugger-cli/__tests__/__fixtures__/transaction.json @@ -1,14 +1,14 @@ { - "data": { - "type": 0, - "amount": 200000000, - "fee": 10000000, - "recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", - "timestamp": 41268326, - "asset": {}, - "senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", - "signature": "3044022002994b30e08b58825c8c16ebf2cc693cfe706fb26571674784ead098accc89d702205b79dedc752a84504ecfe4b9e1292997f22260ee4daa102d2d9a61432d93b286", - "id": "da61c6cba363cc39baa0ca3f9ba2c5db81b9805045bd0b9fc58af07ad4206856" - }, - "serialized": "ff011e0066b47502034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed19280969800000000000000c2eb0b00000000000000001e0995750207ecaf0ccf251c1265b92ad84f5536623044022002994b30e08b58825c8c16ebf2cc693cfe706fb26571674784ead098accc89d702205b79dedc752a84504ecfe4b9e1292997f22260ee4daa102d2d9a61432d93b286" + "data": { + "type": 0, + "amount": 200000000, + "fee": 10000000, + "recipientId": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", + "timestamp": 41268326, + "asset": {}, + "senderPublicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + "signature": "3044022002994b30e08b58825c8c16ebf2cc693cfe706fb26571674784ead098accc89d702205b79dedc752a84504ecfe4b9e1292997f22260ee4daa102d2d9a61432d93b286", + "id": "da61c6cba363cc39baa0ca3f9ba2c5db81b9805045bd0b9fc58af07ad4206856" + }, + "serialized": "ff011e0066b47502034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed19280969800000000000000c2eb0b00000000000000001e0995750207ecaf0ccf251c1265b92ad84f5536623044022002994b30e08b58825c8c16ebf2cc693cfe706fb26571674784ead098accc89d702205b79dedc752a84504ecfe4b9e1292997f22260ee4daa102d2d9a61432d93b286" } diff --git a/packages/core-debugger-cli/__tests__/commands/deserialize.test.js b/packages/core-debugger-cli/__tests__/commands/deserialize.test.js deleted file mode 100644 index d46609efe5..0000000000 --- a/packages/core-debugger-cli/__tests__/commands/deserialize.test.js +++ /dev/null @@ -1,80 +0,0 @@ -const testSubject = require('../../lib/commands/deserialize') -const fixtureBlock = require('../__fixtures__/block.json') -const fixtureTransaction = require('../__fixtures__/transaction.json') - -describe('Commands - Deserialize', () => { - it('should be a function', () => { - expect(testSubject).toBeFunction() - }) - - it('should deserialize a block (not-full)', () => { - const actual = JSON.parse( - testSubject({ - data: fixtureBlock.serialized, - type: 'block', - }), - ) - - expect(actual.data.version).toBe(fixtureBlock.data.version) - expect(actual.data.timestamp).toBe(fixtureBlock.data.timestamp) - expect(actual.data.height).toBe(fixtureBlock.data.height) - expect(actual.data.previousBlock).toBe(fixtureBlock.data.previousBlock) - expect(actual.data.numberOfTransactions).toBe( - fixtureBlock.data.numberOfTransactions, - ) - expect(actual.data.totalAmount).toBe(fixtureBlock.data.totalAmount) - expect(actual.data.totalFee).toBe(fixtureBlock.data.totalFee) - expect(actual.data.reward).toBe(fixtureBlock.data.reward) - expect(actual.data.payloadLength).toBe(fixtureBlock.data.payloadLength) - expect(actual.data.payloadHash).toBe(fixtureBlock.data.payloadHash) - expect(actual.data.generatorPublicKey).toBe( - fixtureBlock.data.generatorPublicKey, - ) - expect(actual.data.blockSignature).toBe(fixtureBlock.data.blockSignature) - }) - - it('should deserialize a block (full)', () => { - const actual = JSON.parse( - testSubject({ - data: fixtureBlock.serializedFull, - type: 'block', - }), - ) - - expect(actual.data.version).toBe(fixtureBlock.data.version) - expect(actual.data.timestamp).toBe(fixtureBlock.data.timestamp) - expect(actual.data.height).toBe(fixtureBlock.data.height) - expect(actual.data.previousBlock).toBe(fixtureBlock.data.previousBlock) - expect(actual.data.numberOfTransactions).toBe( - fixtureBlock.data.numberOfTransactions, - ) - expect(actual.data.totalAmount).toBe(fixtureBlock.data.totalAmount) - expect(actual.data.totalFee).toBe(fixtureBlock.data.totalFee) - expect(actual.data.reward).toBe(fixtureBlock.data.reward) - expect(actual.data.payloadLength).toBe(fixtureBlock.data.payloadLength) - expect(actual.data.payloadHash).toBe(fixtureBlock.data.payloadHash) - expect(actual.data.generatorPublicKey).toBe( - fixtureBlock.data.generatorPublicKey, - ) - expect(actual.data.blockSignature).toBe(fixtureBlock.data.blockSignature) - expect(actual.transactions).toHaveLength(7) - }) - - it('should deserialize a transaction', () => { - const actual = JSON.parse( - testSubject({ - data: fixtureTransaction.serialized, - type: 'transaction', - }), - ) - - expect(actual.type).toBe(fixtureTransaction.data.type) - expect(+actual.amount).toBe(fixtureTransaction.data.amount) - expect(+actual.fee).toBe(fixtureTransaction.data.fee) - expect(actual.recipientId).toBe(fixtureTransaction.data.recipientId) - expect(actual.timestamp).toBe(fixtureTransaction.data.timestamp) - expect(actual.senderPublicKey).toBe(fixtureTransaction.data.senderPublicKey) - expect(actual.signature).toBe(fixtureTransaction.data.signature) - expect(actual.id).toBe(fixtureTransaction.data.id) - }) -}) diff --git a/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts b/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts new file mode 100644 index 0000000000..a7b7fa572e --- /dev/null +++ b/packages/core-debugger-cli/__tests__/commands/deserialize.test.ts @@ -0,0 +1,60 @@ +import "jest-extended"; + +import { DeserializeCommand } from "../../src/commands/deserialize"; + +describe("Commands - Deserialize", () => { + const fixtureBlock = require("../__fixtures__/block.json"); + const fixtureTransaction = require("../__fixtures__/transaction.json"); + + it("should deserialize a block (not-full)", async () => { + const actual = JSON.parse(await DeserializeCommand.run(["--data", fixtureBlock.serialized, "--type", "block"])); + + expect(actual.data.version).toBe(fixtureBlock.data.version); + expect(actual.data.timestamp).toBe(fixtureBlock.data.timestamp); + expect(actual.data.height).toBe(fixtureBlock.data.height); + expect(actual.data.previousBlock).toBe(fixtureBlock.data.previousBlock); + expect(actual.data.numberOfTransactions).toBe(fixtureBlock.data.numberOfTransactions); + expect(actual.data.totalAmount).toBe(fixtureBlock.data.totalAmount); + expect(actual.data.totalFee).toBe(fixtureBlock.data.totalFee); + expect(actual.data.reward).toBe(fixtureBlock.data.reward); + expect(actual.data.payloadLength).toBe(fixtureBlock.data.payloadLength); + expect(actual.data.payloadHash).toBe(fixtureBlock.data.payloadHash); + expect(actual.data.generatorPublicKey).toBe(fixtureBlock.data.generatorPublicKey); + expect(actual.data.blockSignature).toBe(fixtureBlock.data.blockSignature); + }); + + it("should deserialize a block (full)", async () => { + const actual = JSON.parse( + await DeserializeCommand.run(["--data", fixtureBlock.serializedFull, "--type", "block"]), + ); + + expect(actual.data.version).toBe(fixtureBlock.data.version); + expect(actual.data.timestamp).toBe(fixtureBlock.data.timestamp); + expect(actual.data.height).toBe(fixtureBlock.data.height); + expect(actual.data.previousBlock).toBe(fixtureBlock.data.previousBlock); + expect(actual.data.numberOfTransactions).toBe(fixtureBlock.data.numberOfTransactions); + expect(actual.data.totalAmount).toBe(fixtureBlock.data.totalAmount); + expect(actual.data.totalFee).toBe(fixtureBlock.data.totalFee); + expect(actual.data.reward).toBe(fixtureBlock.data.reward); + expect(actual.data.payloadLength).toBe(fixtureBlock.data.payloadLength); + expect(actual.data.payloadHash).toBe(fixtureBlock.data.payloadHash); + expect(actual.data.generatorPublicKey).toBe(fixtureBlock.data.generatorPublicKey); + expect(actual.data.blockSignature).toBe(fixtureBlock.data.blockSignature); + expect(actual.transactions).toHaveLength(7); + }); + + it("should deserialize a transaction", async () => { + const actual = JSON.parse( + await DeserializeCommand.run(["--data", fixtureTransaction.serialized, "--type", "transaction"]), + ); + + expect(actual.type).toBe(fixtureTransaction.data.type); + expect(+actual.amount).toBe(fixtureTransaction.data.amount); + expect(+actual.fee).toBe(fixtureTransaction.data.fee); + expect(actual.recipientId).toBe(fixtureTransaction.data.recipientId); + expect(actual.timestamp).toBe(fixtureTransaction.data.timestamp); + expect(actual.senderPublicKey).toBe(fixtureTransaction.data.senderPublicKey); + expect(actual.signature).toBe(fixtureTransaction.data.signature); + expect(actual.id).toBe(fixtureTransaction.data.id); + }); +}); diff --git a/packages/core-debugger-cli/__tests__/commands/identity.test.js b/packages/core-debugger-cli/__tests__/commands/identity.test.js deleted file mode 100644 index 1852332ce6..0000000000 --- a/packages/core-debugger-cli/__tests__/commands/identity.test.js +++ /dev/null @@ -1,58 +0,0 @@ -const testSubject = require('../../lib/commands/identity') -const fixtureIdentities = require('../__fixtures__/identities.json') - -describe('Commands - Identity', () => { - it('should be a function', () => { - expect(testSubject).toBeFunction() - }) - - it('should return identities from passphrase', () => { - const expected = { - passphrase: 'this is a top secret passphrase', - publicKey: - '034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192', - privateKey: - 'd8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712', - address: 'D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib', - } - - expect( - testSubject({ - data: fixtureIdentities.passphrase, - type: 'passphrase', - }), - ).toEqual(expected) - }) - - it('should return identities from privateKey', () => { - const expected = { - publicKey: - '034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192', - privateKey: - 'd8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712', - address: 'D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib', - } - - expect( - testSubject({ - data: fixtureIdentities.privateKey, - type: 'privateKey', - }), - ).toEqual(expected) - }) - - it('should return identities from publicKey', () => { - const expected = { - publicKey: - '034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192', - address: 'D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib', - } - - expect( - testSubject({ - data: fixtureIdentities.publicKey, - type: 'publicKey', - }), - ).toEqual(expected) - }) -}) diff --git a/packages/core-debugger-cli/__tests__/commands/identity.test.ts b/packages/core-debugger-cli/__tests__/commands/identity.test.ts new file mode 100644 index 0000000000..2fff72ab91 --- /dev/null +++ b/packages/core-debugger-cli/__tests__/commands/identity.test.ts @@ -0,0 +1,43 @@ +import "jest-extended"; + +import { IdentityCommand } from "../../src/commands/identity"; + +describe("Commands - Identity", async () => { + const fixtureIdentities = require("../__fixtures__/identities.json"); + + it("should return identities from passphrase", async () => { + const expected = { + passphrase: "this is a top secret passphrase", + publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + privateKey: "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", + address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", + }; + + expect(await IdentityCommand.run(["--data", fixtureIdentities.passphrase, "--type", "passphrase"])).toEqual( + expected, + ); + }); + + it("should return identities from privateKey", async () => { + const expected = { + publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + privateKey: "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", + address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", + }; + + expect(await IdentityCommand.run(["--data", fixtureIdentities.privateKey, "--type", "privateKey"])).toEqual( + expected, + ); + }); + + it("should return identities from publicKey", async () => { + const expected = { + publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + address: "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", + }; + + expect(await IdentityCommand.run(["--data", fixtureIdentities.publicKey, "--type", "publicKey"])).toEqual( + expected, + ); + }); +}); diff --git a/packages/core-debugger-cli/__tests__/commands/serialize.test.js b/packages/core-debugger-cli/__tests__/commands/serialize.test.js deleted file mode 100644 index 74c12f82a1..0000000000 --- a/packages/core-debugger-cli/__tests__/commands/serialize.test.js +++ /dev/null @@ -1,38 +0,0 @@ -const testSubject = require('../../lib/commands/serialize') -const fixtureBlock = require('../__fixtures__/block.json') -const fixtureTransaction = require('../__fixtures__/transaction.json') - -describe('Commands - Serialize', () => { - it('should be a function', () => { - expect(testSubject).toBeFunction() - }) - - it('should serialize a block (not-full)', () => { - expect( - testSubject({ - data: JSON.stringify(fixtureBlock.data), - type: 'block', - full: false, - }), - ).toEqual(fixtureBlock.serialized) - }) - - it('should serialize a block (full)', () => { - expect( - testSubject({ - data: JSON.stringify(fixtureBlock.data), - type: 'block', - full: true, - }), - ).toEqual(fixtureBlock.serializedFull) - }) - - it('should serialize a transaction', () => { - expect( - testSubject({ - data: JSON.stringify(fixtureTransaction.data), - type: 'transaction', - }), - ).toEqual(fixtureTransaction.serialized) - }) -}) diff --git a/packages/core-debugger-cli/__tests__/commands/serialize.test.ts b/packages/core-debugger-cli/__tests__/commands/serialize.test.ts new file mode 100644 index 0000000000..a2ddb006bb --- /dev/null +++ b/packages/core-debugger-cli/__tests__/commands/serialize.test.ts @@ -0,0 +1,26 @@ +import "jest-extended"; + +import { SerializeCommand } from "../../src/commands/serialize"; + +describe("Commands - Serialize", () => { + const fixtureBlock = require("../__fixtures__/block.json"); + const fixtureTransaction = require("../__fixtures__/transaction.json"); + + it("should serialize a block (not-full)", async () => { + expect(await SerializeCommand.run(["--data", JSON.stringify(fixtureBlock.data), "--type", "block"])).toEqual( + fixtureBlock.serialized, + ); + }); + + it("should serialize a block (full)", async () => { + expect( + await SerializeCommand.run(["--data", JSON.stringify(fixtureBlock.data), "--type", "block", "--full"]), + ).toEqual(fixtureBlock.serializedFull); + }); + + it("should serialize a transaction", async () => { + expect( + await SerializeCommand.run(["--data", JSON.stringify(fixtureTransaction.data), "--type", "transaction"]), + ).toEqual(fixtureTransaction.serialized); + }); +}); diff --git a/packages/core-debugger-cli/__tests__/commands/verify-second.test.js b/packages/core-debugger-cli/__tests__/commands/verify-second.test.js deleted file mode 100644 index 57ed595876..0000000000 --- a/packages/core-debugger-cli/__tests__/commands/verify-second.test.js +++ /dev/null @@ -1,18 +0,0 @@ -const testSubject = require('../../lib/commands/verify-second') -const fixtureTransaction = require('../__fixtures__/transaction-second.json') - -describe('Commands - Verify Second', () => { - it('should be a function', () => { - expect(testSubject).toBeFunction() - }) - - it('should verify a second signature', () => { - expect( - testSubject({ - data: fixtureTransaction.serialized, - publicKey: - '03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609', - }), - ).toBeTrue() - }) -}) diff --git a/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts b/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts new file mode 100644 index 0000000000..5cb6fb2b1a --- /dev/null +++ b/packages/core-debugger-cli/__tests__/commands/verify-second.test.ts @@ -0,0 +1,18 @@ +import "jest-extended"; + +import { VerifySecondSignatureCommand } from "../../src/commands/verify-second"; + +describe("Commands - Verify Second", () => { + const fixtureTransaction = require("../__fixtures__/transaction-second.json"); + + it("should verify a second signature", async () => { + expect( + await VerifySecondSignatureCommand.run([ + "--data", + fixtureTransaction.serialized, + "--publicKey", + "03699e966b2525f9088a6941d8d94f7869964a000efe65783d78ac82e1199fe609", + ]), + ).toBeTrue(); + }); +}); diff --git a/packages/core-debugger-cli/__tests__/commands/verify.test.js b/packages/core-debugger-cli/__tests__/commands/verify.test.js deleted file mode 100644 index 458933ebbc..0000000000 --- a/packages/core-debugger-cli/__tests__/commands/verify.test.js +++ /dev/null @@ -1,27 +0,0 @@ -const testSubject = require('../../lib/commands/verify') -const fixtureBlock = require('../__fixtures__/block.json') -const fixtureTransaction = require('../__fixtures__/transaction.json') - -describe('Commands - Verify', () => { - it('should be a function', () => { - expect(testSubject).toBeFunction() - }) - - it('should verify a block', () => { - expect( - testSubject({ - data: fixtureBlock.serializedFull, - type: 'block', - }), - ).toBeTrue() - }) - - it('should verify a transaction', () => { - expect( - testSubject({ - data: fixtureTransaction.serialized, - type: 'transaction', - }), - ).toBeTrue() - }) -}) diff --git a/packages/core-debugger-cli/__tests__/commands/verify.test.ts b/packages/core-debugger-cli/__tests__/commands/verify.test.ts new file mode 100644 index 0000000000..8827d94bc0 --- /dev/null +++ b/packages/core-debugger-cli/__tests__/commands/verify.test.ts @@ -0,0 +1,16 @@ +import "jest-extended"; + +import { VerifyCommand } from "../../src/commands/verify"; + +describe("Commands - Verify", () => { + const fixtureBlock = require("../__fixtures__/block.json"); + const fixtureTransaction = require("../__fixtures__/transaction.json"); + + it("should verify a block", async () => { + expect(await VerifyCommand.run(["--data", fixtureBlock.serializedFull, "--type", "block"])).toBeTrue(); + }); + + it("should verify a transaction", async () => { + expect(await VerifyCommand.run(["--data", fixtureTransaction.serialized, "--type", "transaction"])).toBeTrue(); + }); +}); diff --git a/packages/core-debugger-cli/__tests__/utils.test.ts b/packages/core-debugger-cli/__tests__/utils.test.ts new file mode 100644 index 0000000000..b07503a359 --- /dev/null +++ b/packages/core-debugger-cli/__tests__/utils.test.ts @@ -0,0 +1,36 @@ +import { readSync } from "clipboardy"; +import "jest-extended"; + +import { copyToClipboard, handleOutput } from "../src/utils"; + +const dummyData = { hello: "world" }; + +describe("Utils", () => { + describe("copyToClipboard", () => { + it("should contain the copied data", () => { + copyToClipboard(dummyData); + + expect(JSON.parse(readSync())).toEqual(dummyData); + }); + }); + + describe("handleOutput", () => { + it("should copy the data", () => { + handleOutput({ copy: true }, dummyData); + + expect(JSON.parse(readSync())).toEqual(dummyData); + }); + + it("should log the data", () => { + const method = jest.spyOn(global.console, "log"); + + handleOutput({ log: true }, dummyData); + + expect(method).toHaveBeenCalledWith(dummyData); + }); + + it("should return the data", () => { + expect(handleOutput({}, dummyData)).toEqual(dummyData); + }); + }); +}); diff --git a/packages/core-debugger-cli/bin/debugger b/packages/core-debugger-cli/bin/debugger deleted file mode 100755 index b07d0c62d5..0000000000 --- a/packages/core-debugger-cli/bin/debugger +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env node - -const app = require('commander') - -app.version(require('../package.json').version) - -const registerCommand = (name, description) => { - return app - .command(name) - .description(description) - .option('-l, --log', 'log the data to the console') - .option('-c, --copy', 'copy the data to the clipboard') -} - -registerCommand('ser', 'serialize the given JSON') - .option('-d, --data ', 'JSON blob to serialize') - .option('-t, --type ', 'transaction or block', 'transaction') - .action(options => require('../lib/commands/serialize')(options)) - -registerCommand('des', 'deserialize the given HEX') - .option('-d, --data ', 'the HEX blob to deserialize') - .option('-t, --type ', 'transaction or block', 'transaction') - .action(options => require('../lib/commands/deserialize')(options)) - -registerCommand('verify', 'verify the given HEX') - .option('-d, --data ', 'the HEX blob to deserialize and verify') - .option('-t, --type ', 'transaction or block', 'transaction') - .action(options => require('../lib/commands/verify')(options)) - -registerCommand('verify-second', 'verify a second signature of a transaction') - .option('-d, --data ', 'the transaction HEX blob to deserialize and verify') - .option('-p, --publicKey ', 'the publicKey of the second signature in HEX') - .action(options => require('../lib/commands/verify-second')(options)) - -registerCommand('identity', 'get identities from the given input') - .option('-d, --data ', 'the data to get the identities from') - .option('-t, --type ', 'the input type is either of passphrase, privateKey or publicKey', 'passphrase') - .option('-n, --network ', 'the network version used for calculating the address.') - .action(options => require('../lib/commands/identity')(options)) - -app - .command('*') - .action(env => { - app.help() - }) - -app.parse(process.argv) - -if (app.args.length === 0) { - app.help() -} diff --git a/packages/core-debugger-cli/bin/run b/packages/core-debugger-cli/bin/run new file mode 100755 index 0000000000..30b14e1773 --- /dev/null +++ b/packages/core-debugger-cli/bin/run @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +require('@oclif/command').run() +.then(require('@oclif/command/flush')) +.catch(require('@oclif/errors/handle')) diff --git a/packages/core-debugger-cli/bin/run.cmd b/packages/core-debugger-cli/bin/run.cmd new file mode 100644 index 0000000000..968fc30758 --- /dev/null +++ b/packages/core-debugger-cli/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/core-debugger-cli/jest.config.js b/packages/core-debugger-cli/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-debugger-cli/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-debugger-cli/lib/commands/deserialize.js b/packages/core-debugger-cli/lib/commands/deserialize.js deleted file mode 100644 index 5df757b104..0000000000 --- a/packages/core-debugger-cli/lib/commands/deserialize.js +++ /dev/null @@ -1,16 +0,0 @@ -const { - models: { Block, Transaction }, -} = require('@arkecosystem/crypto') -const handleOutput = require('../utils/handle-output') - -module.exports = opts => { - let deserialized - - if (opts.type === 'transaction') { - deserialized = new Transaction(opts.data) - } else { - deserialized = new Block(opts.data) - } - - return handleOutput(opts, JSON.stringify(deserialized, null, 4)) -} diff --git a/packages/core-debugger-cli/lib/commands/identity.js b/packages/core-debugger-cli/lib/commands/identity.js deleted file mode 100644 index 594243da9d..0000000000 --- a/packages/core-debugger-cli/lib/commands/identity.js +++ /dev/null @@ -1,30 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') -const handleOutput = require('../utils/handle-output') - -module.exports = opts => { - let output - - if (opts.type === 'passphrase') { - const keys = crypto.getKeys(opts.data) - output = { - passphrase: opts.data, - publicKey: keys.publicKey, - privateKey: keys.privateKey, - address: crypto.getAddress(keys.publicKey, opts.network), - } - } else if (opts.type === 'privateKey') { - const keys = crypto.getKeysByPrivateKey(opts.data) - output = { - publicKey: keys.publicKey, - privateKey: keys.privateKey, - address: crypto.getAddress(keys.publicKey, opts.network), - } - } else if (opts.type === 'publicKey') { - output = { - publicKey: opts.data, - address: crypto.getAddress(opts.data, opts.network), - } - } - - return handleOutput(opts, output) -} diff --git a/packages/core-debugger-cli/lib/commands/serialize.js b/packages/core-debugger-cli/lib/commands/serialize.js deleted file mode 100644 index 561c32a658..0000000000 --- a/packages/core-debugger-cli/lib/commands/serialize.js +++ /dev/null @@ -1,12 +0,0 @@ -const { - models: { Block, Transaction }, -} = require('@arkecosystem/crypto') -const handleOutput = require('../utils/handle-output') - -module.exports = opts => { - const serialized = opts.type === 'transaction' - ? Transaction.serialize(JSON.parse(opts.data)) - : Block[opts.full ? 'serializeFull' : 'serialize'](JSON.parse(opts.data)) - - return handleOutput(opts, serialized.toString('hex')) -} diff --git a/packages/core-debugger-cli/lib/commands/verify-second.js b/packages/core-debugger-cli/lib/commands/verify-second.js deleted file mode 100644 index 8a312efd52..0000000000 --- a/packages/core-debugger-cli/lib/commands/verify-second.js +++ /dev/null @@ -1,13 +0,0 @@ -const { - crypto, - models: { Transaction }, -} = require('@arkecosystem/crypto') -const handleOutput = require('../utils/handle-output') - -module.exports = opts => { - const transaction = new Transaction(opts.data) - const publicKey = opts.publicKey - - const output = crypto.verifySecondSignature(transaction, publicKey) - return handleOutput(opts, output) -} diff --git a/packages/core-debugger-cli/lib/commands/verify.js b/packages/core-debugger-cli/lib/commands/verify.js deleted file mode 100644 index 33efadd177..0000000000 --- a/packages/core-debugger-cli/lib/commands/verify.js +++ /dev/null @@ -1,16 +0,0 @@ -const { - models: { Block, Transaction }, -} = require('@arkecosystem/crypto') -const handleOutput = require('../utils/handle-output') - -module.exports = opts => { - const deserialized = opts.type === 'transaction' - ? new Transaction(opts.data) - : new Block(Block.deserialize(opts.data)) - - const output = opts.type === 'transaction' - ? deserialized.verify() - : deserialized.verify().verified - - return handleOutput(opts, output) -} diff --git a/packages/core-debugger-cli/lib/utils/copy-to-clipboard.js b/packages/core-debugger-cli/lib/utils/copy-to-clipboard.js deleted file mode 100644 index 68720fb77f..0000000000 --- a/packages/core-debugger-cli/lib/utils/copy-to-clipboard.js +++ /dev/null @@ -1,3 +0,0 @@ -const clipboardy = require('clipboardy') - -module.exports = data => clipboardy.writeSync(JSON.stringify(data)) diff --git a/packages/core-debugger-cli/lib/utils/handle-output.js b/packages/core-debugger-cli/lib/utils/handle-output.js deleted file mode 100644 index 32387f9eab..0000000000 --- a/packages/core-debugger-cli/lib/utils/handle-output.js +++ /dev/null @@ -1,13 +0,0 @@ -const copyToClipboard = require('../utils/copy-to-clipboard') - -module.exports = (opts, data) => { - if (opts.copy) { - return copyToClipboard(data) - } - - if (opts.log) { - return console.info(data) - } - - return data -} diff --git a/packages/core-debugger-cli/package.json b/packages/core-debugger-cli/package.json index 204ea13233..b7b6ba0c29 100644 --- a/packages/core-debugger-cli/package.json +++ b/packages/core-debugger-cli/package.json @@ -1,33 +1,63 @@ { - "name": "@arkecosystem/core-debugger-cli", - "description": "Debugger CLI for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "bin": { - "ark:debugger": "./bin/debugger" - }, - "scripts": { - "start": "./bin/debugger", - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/crypto": "~0.2", - "clipboardy": "^1.2.3", - "commander": "^2.19.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-debugger-cli", + "description": "Debugger CLI for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "/bin", + "/dist", + "/oclif.manifest.json" + ], + "bin": { + "debugger": "./bin/run" + }, + "scripts": { + "debugger": "./bin/run", + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "prepack": "oclif-dev manifest && npm shrinkwrap", + "postpack": "rm -f oclif.manifest.json", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/crypto": "^2.1.0", + "@oclif/command": "^1.5.8", + "@oclif/config": "^1.12.4", + "@oclif/plugin-help": "^2.1.6", + "@oclif/plugin-not-found": "^1.2.2", + "@types/clipboardy": "^1.1.0", + "clipboardy": "^1.2.3" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + }, + "oclif": { + "commands": "./dist/commands", + "bin": "debugger", + "plugins": [ + "@oclif/plugin-help", + "@oclif/plugin-not-found" + ] + } } diff --git a/packages/core-debugger-cli/src/commands/command.ts b/packages/core-debugger-cli/src/commands/command.ts new file mode 100644 index 0000000000..4ab327459a --- /dev/null +++ b/packages/core-debugger-cli/src/commands/command.ts @@ -0,0 +1,12 @@ +import Command, { flags } from "@oclif/command"; + +export abstract class BaseCommand extends Command { + public static flags = { + log: flags.string({ + description: "log the data to the console", + }), + copy: flags.string({ + description: "copy the data to the clipboard", + }), + }; +} diff --git a/packages/core-debugger-cli/src/commands/deserialize.ts b/packages/core-debugger-cli/src/commands/deserialize.ts new file mode 100644 index 0000000000..553fe65abb --- /dev/null +++ b/packages/core-debugger-cli/src/commands/deserialize.ts @@ -0,0 +1,31 @@ +import { models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; + +export class DeserializeCommand extends BaseCommand { + public static description: string = "Deserialize the given HEX"; + + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to deserialize", + required: true, + default: "transaction", + }), + type: flags.string({ + description: "transaction or block", + required: true, + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(DeserializeCommand); + + const deserialized = + flags.type === "transaction" ? new models.Transaction(flags.data) : new models.Block(flags.data); + + return handleOutput(flags, JSON.stringify(deserialized, null, 4)); + } +} diff --git a/packages/core-debugger-cli/src/commands/identity.ts b/packages/core-debugger-cli/src/commands/identity.ts new file mode 100644 index 0000000000..0ed91c6ba3 --- /dev/null +++ b/packages/core-debugger-cli/src/commands/identity.ts @@ -0,0 +1,56 @@ +import { crypto } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; + +export class IdentityCommand extends BaseCommand { + public static description: string = "Get identities from the given input"; + + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the data to get the identities from", + required: true, + }), + network: flags.integer({ + description: "the network version used for calculating the address.", + required: true, + default: 30, + }), + type: flags.string({ + description: "the input type is either of passphrase, privateKey or publicKey", + required: true, + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(IdentityCommand); + + let output; + + if (flags.type === "passphrase") { + const keys = crypto.getKeys(flags.data); + output = { + passphrase: flags.data, + publicKey: keys.publicKey, + privateKey: keys.privateKey, + address: crypto.getAddress(keys.publicKey, flags.network), + }; + } else if (flags.type === "privateKey") { + const keys = crypto.getKeysByPrivateKey(flags.data); + output = { + publicKey: keys.publicKey, + privateKey: keys.privateKey, + address: crypto.getAddress(keys.publicKey, flags.network), + }; + } else if (flags.type === "publicKey") { + output = { + publicKey: flags.data, + address: crypto.getAddress(flags.data, flags.network), + }; + } + + return handleOutput(flags, output); + } +} diff --git a/packages/core-debugger-cli/src/commands/serialize.ts b/packages/core-debugger-cli/src/commands/serialize.ts new file mode 100644 index 0000000000..61c06590b6 --- /dev/null +++ b/packages/core-debugger-cli/src/commands/serialize.ts @@ -0,0 +1,36 @@ +import { models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; + +export class SerializeCommand extends BaseCommand { + public static description: string = "Serialize the given JSON"; + + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to serialize", + required: true, + }), + type: flags.string({ + description: "transaction or block", + required: true, + }), + full: flags.boolean({ + description: "serialize a full block with transactions", + required: false, + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(SerializeCommand); + + const serialized: any = + flags.type === "transaction" + ? models.Transaction.serialize(JSON.parse(flags.data)) + : models.Block[flags.full ? "serializeFull" : "serialize"](JSON.parse(flags.data)); + + return handleOutput(flags, serialized.toString("hex")); + } +} diff --git a/packages/core-debugger-cli/src/commands/verify-second.ts b/packages/core-debugger-cli/src/commands/verify-second.ts new file mode 100644 index 0000000000..aeed1cadb1 --- /dev/null +++ b/packages/core-debugger-cli/src/commands/verify-second.ts @@ -0,0 +1,29 @@ +import { crypto, models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; + +export class VerifySecondSignatureCommand extends BaseCommand { + public static description: string = "Verify a second signature of a transaction"; + + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to deserialize and verify", + required: true, + }), + publicKey: flags.string({ + description: "the publicKey of the second signature in HEX", + required: true, + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(VerifySecondSignatureCommand); + + const transaction = new models.Transaction(flags.data); + + return handleOutput(flags, crypto.verifySecondSignature(transaction, flags.publicKey)); + } +} diff --git a/packages/core-debugger-cli/src/commands/verify.ts b/packages/core-debugger-cli/src/commands/verify.ts new file mode 100644 index 0000000000..37deb12c65 --- /dev/null +++ b/packages/core-debugger-cli/src/commands/verify.ts @@ -0,0 +1,35 @@ +import { models } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import { handleOutput } from "../utils"; +import { BaseCommand } from "./command"; + +export class VerifyCommand extends BaseCommand { + public static description: string = "Verify the given HEX"; + + public static flags = { + ...BaseCommand.flags, + data: flags.string({ + description: "the HEX blob to deserialize and verify", + required: true, + }), + type: flags.string({ + description: "transaction or block", + required: true, + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(VerifyCommand); + + const deserialized = + flags.type === "transaction" + ? new models.Transaction(flags.data) + : new models.Block(models.Block.deserialize(flags.data)); + + const output = + deserialized instanceof models.Transaction ? deserialized.verify() : deserialized.verification.verified; + + return handleOutput(flags, output); + } +} diff --git a/packages/core-debugger-cli/src/index.ts b/packages/core-debugger-cli/src/index.ts new file mode 100644 index 0000000000..8bdb76f9a0 --- /dev/null +++ b/packages/core-debugger-cli/src/index.ts @@ -0,0 +1 @@ +export { run } from "@oclif/command"; diff --git a/packages/core-debugger-cli/src/utils.ts b/packages/core-debugger-cli/src/utils.ts new file mode 100644 index 0000000000..2fbd8ef2ac --- /dev/null +++ b/packages/core-debugger-cli/src/utils.ts @@ -0,0 +1,18 @@ +import clipboardy from "clipboardy"; + +export function copyToClipboard(data) { + clipboardy.writeSync(JSON.stringify(data)); +} + +export function handleOutput(opts, data) { + if (opts.copy) { + return copyToClipboard(data); + } + + if (opts.log) { + // tslint:disable-next-line:no-console + return console.log(data); + } + + return data; +} diff --git a/packages/core-debugger-cli/tsconfig.json b/packages/core-debugger-cli/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-debugger-cli/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-deployer/.gitattributes b/packages/core-deployer/.gitattributes deleted file mode 100644 index 60cc52db63..0000000000 --- a/packages/core-deployer/.gitattributes +++ /dev/null @@ -1,11 +0,0 @@ -# Path-based git attributes -# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html - -# Ignore all test and documentation with "export-ignore". -/.editorconfig export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/.travis.yml export-ignore -/__tests__ export-ignore -/docs export-ignore -/README.md export-ignore diff --git a/packages/core-deployer/CHANGELOG.md b/packages/core-deployer/CHANGELOG.md deleted file mode 100644 index 7d7b5529b2..0000000000 --- a/packages/core-deployer/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Changed - -- Handle numbers as `BigNumber` instances -- Updated `@arkecosystem/crypto` to `0.2.0` -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-07-26 - -### Added - -- initial release diff --git a/packages/core-deployer/LICENSE b/packages/core-deployer/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-deployer/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-deployer/README.md b/packages/core-deployer/README.md deleted file mode 100644 index afa712cd5c..0000000000 --- a/packages/core-deployer/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Ark Core - Deployer - -

- -

- -## Documentation - -You can find installation instructions and detailed instructions on how to use this package at the [dedicated documentation site](https://docs.ark.io/guidebook/core/plugins/core-deployer.html). - -## Security - -If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. - -## Credits - -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [All Contributors](../../../../contributors) - -## License - -[MIT](LICENSE) © [ArkEcosystem](https://ark.io) diff --git a/packages/core-deployer/__tests__/builder/genesis-block.test.js b/packages/core-deployer/__tests__/builder/genesis-block.test.js deleted file mode 100644 index 7357a779b7..0000000000 --- a/packages/core-deployer/__tests__/builder/genesis-block.test.js +++ /dev/null @@ -1,274 +0,0 @@ -const GenesisBlockBuilder = require('../../lib/builder/genesis-block') -const network = require('../../../crypto/lib/networks/ark/testnet') - -let builder -let genesis -let wallet -let delegateWallet -let delegateWallets - -beforeEach(() => { - builder = new GenesisBlockBuilder(network, { - totalPremine: 2100000000000000, - activeDelegates: 2, - }) - - delegateWallets = builder.__buildDelegates() -}) - -describe('Genesis Block Builder', () => { - it('should be an object', () => { - expect(builder).toBeInstanceOf(GenesisBlockBuilder) - }) - - describe('generate', () => { - it('should be a function', () => { - expect(builder.generate).toBeFunction() - }) - - it('should return a genesis object', () => { - genesis = builder.generate() - - expect(genesis).toContainAllKeys([ - 'genesisBlock', - 'genesisWallet', - 'delegatePassphrases', - ]) - }) - - it('should call the expected methods', () => { - builder.__createWallet = jest.fn(builder.__createWallet) - builder.__buildDelegates = jest.fn(builder.__buildDelegates) - builder.__buildDelegateTransactions = jest.fn( - builder.__buildDelegateTransactions, - ) - builder.__createTransferTransaction = jest.fn( - builder.__createTransferTransaction, - ) - builder.__createGenesisBlock = jest.fn(builder.__createGenesisBlock) - - builder.generate() - - expect(builder.__createWallet).toHaveBeenCalledTimes(4) - expect(builder.__buildDelegates).toHaveBeenCalledTimes(1) - expect(builder.__buildDelegateTransactions).toHaveBeenCalledTimes(1) - expect(builder.__createTransferTransaction).toHaveBeenCalledTimes(1) - expect(builder.__createGenesisBlock).toHaveBeenCalledTimes(1) - }) - }) - - describe('__createWallet', () => { - it('should be a function', () => { - expect(builder.__createWallet).toBeFunction() - }) - - it('should return an object', () => { - wallet = builder.__createWallet() - - expect(wallet).toBeObject() - }) - - it('should return a wallet object', () => { - expect(wallet).toContainAllKeys(['address', 'keys', 'passphrase']) - }) - - it('should have a valid address', () => { - expect(wallet.address).toEqual(expect.stringMatching(/^A/)) - }) - }) - - describe('__createDelegateWallet', () => { - it('should be a function', () => { - expect(builder.__createDelegateWallet).toBeFunction() - }) - - it('should return an object', () => { - delegateWallet = builder.__createDelegateWallet('testing') - - expect(delegateWallet).toBeObject() - }) - - it('should return a delegate wallet object', () => { - expect(delegateWallet).toContainAllKeys([ - 'address', - 'keys', - 'passphrase', - 'username', - ]) - }) - - it('should have a valid address', () => { - expect(delegateWallet.address).toEqual(expect.stringMatching(/^A/)) - }) - - it('should have a valid username', () => { - expect(delegateWallet.username).toEqual( - expect.stringMatching(/^[a-z0-9!@$&_.]+$/), - ) - }) - - it('should call the expected methods', () => { - builder.__createWallet = jest.fn(builder.__createWallet) - - builder.__createDelegateWallet('testing') - - expect(builder.__createWallet).toHaveBeenCalledTimes(1) - }) - }) - - describe('__buildDelegates', () => { - it('should be a function', () => { - expect(builder.__buildDelegates).toBeFunction() - }) - - it('should return an array of 2', () => { - expect(delegateWallets).toBeArrayOfSize(2) - }) - - it('should call the expected methods', () => { - builder.__createDelegateWallet = jest.fn(builder.__createDelegateWallet) - - builder.__buildDelegates('testing') - - expect(builder.__createDelegateWallet).toHaveBeenCalledTimes(2) - }) - }) - - describe('__buildDelegateTransactions', () => { - it('should be a function', () => { - expect(builder.__buildDelegateTransactions).toBeFunction() - }) - - it('should return an array of 2', () => { - const delegateTransactions = builder.__buildDelegateTransactions( - delegateWallets, - ) - - expect(delegateTransactions).toBeArrayOfSize(2) - }) - - it('should call the expected methods', () => { - builder.__createDelegateTransaction = jest.fn( - builder.__createDelegateTransaction, - ) - - builder.__buildDelegateTransactions(delegateWallets) - - expect(builder.__createDelegateTransaction).toHaveBeenCalledTimes(2) - }) - }) - - describe('__createTransferTransaction', () => { - it('should be a function', () => { - expect(builder.__createTransferTransaction).toBeFunction() - }) - - it('should return a transaction object', () => { - const transferTransaction = builder.__createTransferTransaction( - delegateWallet, - wallet, - 10, - ) - - expect(transferTransaction).toContainEntries([ - ['type', 0], - ['amount', 10], - ['fee', 0], - ['recipientId', wallet.address], - ]) - }) - - it('should call the expected methods', () => { - builder.__formatGenesisTransaction = jest.fn( - builder.__formatGenesisTransaction, - ) - - builder.__createTransferTransaction(delegateWallet, wallet, 10) - - expect(builder.__formatGenesisTransaction).toHaveBeenCalledTimes(1) - }) - }) - - describe('__createDelegateTransaction', () => { - it('should be a function', () => { - expect(builder.__createDelegateTransaction).toBeFunction() - }) - - it('should return a transaction object', () => { - const delegateTransaction = builder.__createDelegateTransaction( - delegateWallet, - ) - - expect(delegateTransaction).toContainEntries([ - ['type', 2], - ['amount', 0], - ['fee', 0], - ['senderId', delegateWallet.address], - ]) - - expect(delegateTransaction.asset.delegate).toHaveProperty( - 'username', - delegateWallet.username, - ) - expect(delegateTransaction.asset.delegate).toHaveProperty( - 'publicKey', - delegateWallet.keys.publicKey, - ) - }) - - it('should call the expected methods', () => { - builder.__formatGenesisTransaction = jest.fn( - builder.__formatGenesisTransaction, - ) - - builder.__createDelegateTransaction(delegateWallet) - - expect(builder.__formatGenesisTransaction).toHaveBeenCalledTimes(1) - }) - }) - - describe('__createGenesisBlock', () => { - it('should be a function', () => { - expect(builder.__createGenesisBlock).toBeFunction() - }) - - it('should match the expected struct', () => { - const genesisBlock = builder.__createGenesisBlock({ - keys: wallet.keys, - transactions: [], - timestamp: 0, - }) - - expect(genesisBlock).toContainAllKeys([ - 'id', - 'blockSignature', - 'version', - 'totalAmount', - 'totalFee', - 'reward', - 'payloadHash', - 'timestamp', - 'numberOfTransactions', - 'payloadLength', - 'previousBlock', - 'generatorPublicKey', - 'transactions', - 'height', - ]) - }) - - it('should call the expected methods', () => { - builder.__getBlockId = jest.fn() - builder.__signBlock = jest.fn() - - builder.__createGenesisBlock({ - keys: wallet.keys, - transactions: [], - timestamp: 0, - }) - - expect(builder.__getBlockId).toHaveBeenCalledTimes(1) - expect(builder.__signBlock).toHaveBeenCalledTimes(1) - }) - }) -}) diff --git a/packages/core-deployer/bin/deployer b/packages/core-deployer/bin/deployer deleted file mode 100755 index ccee9e2f02..0000000000 --- a/packages/core-deployer/bin/deployer +++ /dev/null @@ -1,312 +0,0 @@ -#!/usr/bin/env node - -'use strict' - -const commander = require('commander') -const Joi = require('joi') -const fs = require('fs-extra') -const os = require('os') -const path = require('path') -const { - getRandomNumber, - logger, - updateConfig, - writeEnv, -} = require('../lib/utils') -const GenesisBlockBuilder = require('../lib/builder/genesis-block') - -process.env.ARK_PATH_CONFIG = path.resolve(os.homedir(), '.ark') - -commander - .version(require('../package.json').version) - .option('--network ', 'Network to initially copy', 'mainnet') - .option('--name ', 'Name', 'Bridgechain') - .option('--nodeIp ', 'IP for node', '0.0.0.0') - .option('--p2pPort ', 'P2P API Port', 4102) - .option('--apiPort ', 'Public P2P Port', 4103) - .option('--dbHost ', 'Database host', 'localhost') - .option('--dbPort ', 'Database port', 5432) - .option('--dbUsername ', 'Database username', 'node') - .option('--dbPassword ', 'Database password', 'password') - .option( - '--dbDatabase ', - 'Database name', - `ark_${commander.name.toLowerCase()}`, - ) - .option( - '--explorerUrl ', - 'URL to link to explorer', - 'http://localhost:4200', - ) - .option( - '--activeDelegates ', - 'How many forgers for the network [51]', - 51, - ) - .option('--feeTransfer ', 'Fee for sending Transaction', 10000000) - .option('--feeVote ', 'Fee for Vote Transaction', 100000000) - .option( - '--feeSecondSignature ', - 'Fee for Second Passphrase Transaction', - 500000000, - ) - .option( - '--feeDelegateRegistration ', - 'Fee for Register Delegate Transaction', - 2500000000, - ) - .option( - '--feeMultiSignature ', - 'Fee for Multisignature Transaction', - 500000000, - ) - .option( - '--epoch ', - 'Set Epoch based on time the chain was created', - '2017-02-21T13:00:00.000Z', - ) - .option( - '--rewardHeight ', - 'Block Height when Forgers receive Rewards [1]', - 1, - ) - .option( - '--rewardPerBlock ', - 'How many Rewarded Tokens per Forged Block [200000000 (2)]', - 200000000, - ) - .option('--blocktime ', 'Time per block (seconds) [8]', 8) - .option('--token ', 'Token Name [CHAIN]', 'CHAIN') - .option('--symbol ', 'Symbol for Token [C]', 'C') - .option('--prefixHash ', 'Address Prefix Hash [28]', 28) - .option( - '--transactionsPerBlock ', - 'Max Transaction count per Block [50]', - 50, - ) - .option( - '--wifPrefix ', - 'Prefix for generating a WIF [rand(1, 255)]', - getRandomNumber(1, 255), - ) - .option( - '--totalPremine ', - 'How many tokens initially added to genesis account [2100000000000000 (21 million)]', - 2100000000000000, - ) - // .option('--max-tokens-per-account', 'Max amount of tokens per account [12500000000000000 (125 million)]') - .option( - '--overwriteConfig', - 'Overwrite current deployer config files [off]', - false, - ) - .option( - '--configPath ', - 'Deployer config path destination [~/.ark/deployer]', - `${process.env.ARK_PATH_CONFIG}/deployer`, - ) - .parse(process.argv) - -const { error, value } = Joi.validate(commander, require('../lib/schema.js'), { - allowUnknown: true, - convert: true, -}) -const options = value - -if (error) { - error.details.forEach(detail => logger.error(detail.message)) - process.exit(1) -} - -if (fs.existsSync(options.configPath)) { - if (options.overwriteConfig) { - fs.removeSync(options.configPath) - } else { - logger.error( - `Deployer config already exists in '${ - options.configPath - }' - to overwrite, use the '--overwriteConfig' flag`, - ) - process.exit(1) - } -} -fs.ensureDirSync(options.configPath) -fs.copySync( - path.resolve(__dirname, `../../core/lib/config/${options.network}`), - options.configPath, -) -const networkPath = path.resolve( - __dirname, - `../../crypto/lib/networks/ark/${options.network}.json`, -) -if (!fs.existsSync(networkPath)) { - logger.error(`Network '${options.network}' does not exist`) - process.exit(1) -} -fs.copySync(networkPath, path.resolve(options.configPath, 'network.json')) - -let networkConfig = { - name: options.name.toLowerCase(), - messagePrefix: `${options.token} message:\n`, - pubKeyHash: options.prefixHash, - wif: options.wifPrefix, - constants: [ - { - blocktime: options.blocktime, - block: { - version: 0, - maxTransactions: options.transactionsPerBlock, - maxPayload: 2097152, - }, - epoch: options.epoch, - activeDelegates: options.activeDelegates, - fees: { - dynamic: false, - dynamicFees: { - minFeePool: 1000, - minFeeBroadcast: 1000, - addonBytes: { - transfer: 100, - secondSignature: 250, - delegateRegistration: 500, - vote: 100, - multiSignature: 500, - ipfs: 250, - timelockTransfer: 500, - multiPayment: 500, - delegateResignation: 500, - }, - }, - staticFees: { - transfer: options.feeTransfer, - secondSignature: options.feeVote, - delegateRegistration: options.feeSecondSignature, - vote: options.feeDelegateRegistration, - multiSignature: options.feeMultiSignature, - ipfs: 0, - timelockTransfer: 0, - multiPayment: 0, - delegateResignation: 0, - }, - }, - }, - ], - client: { - token: options.token, - symbol: options.symbol, - explorer: options.explorerUrl, - }, - exceptions: {}, -} - -const network = updateConfig('network.json', networkConfig, options.configPath) - -const genesis = new GenesisBlockBuilder(network, options).generate() - -network.nethash = genesis.genesisBlock.payloadHash - -if (options.rewardHeight === 1) { - network.constants = [network.constants[0]] - network.constants[0].height = options.rewardHeight - network.constants[0].reward = options.rewardPerBlock -} else { - network.constants[1].height = options.rewardHeight - network.constants[1].reward = options.rewardPerBlock -} - -const requestNodeIp = - options.nodeIp === '0.0.0.0' ? '127.0.0.1' : options.nodeIp - -updateConfig('network.json', networkConfig, options.configPath) -updateConfig( - 'peers.json', - { - minimumVersion: '>=1.3.3', - minimumNetworkReach: 1, - list: [ - { - ip: requestNodeIp, - port: options.p2pPort, - }, - ], - sources: [], - }, - options.configPath, -) - -updateConfig( - 'genesisWallet.json', - { - address: genesis.genesisWallet.address, - passphrase: genesis.genesisWallet.passphrase, - }, - options.configPath, - true, -) -updateConfig( - 'genesisBlock.json', - genesis.genesisBlock, - options.configPath, - true, -) -updateConfig( - 'delegates.json', - { - secrets: genesis.delegatePassphrases, - }, - options.configPath, - true, -) - -updateConfig( - 'server.json', - { - port: options.p2pPort, - fastRebuild: false, - }, - options.configPath, -) - -updateConfig('api/public.json', { port: options.apiPort }, options.configPath) - -const pluginsOriginal = require(path.resolve(options.configPath, 'plugins')) -const plugins = {} -for (let plugin in pluginsOriginal) { - plugins[plugin] = {} -} -plugins['@arkecosystem/core-p2p'] = { - host: options.nodeIp, - port: options.p2pPort, - whitelist: ['127.0.0.1', '192.168.*'], -} -plugins['@arkecosystem/core-api'] = { - enabled: true, - host: options.nodeIp, - port: options.apiPort, - whitelist: ['*'], -} -plugins['@arkecosystem/core-database-postgres'] = { - host: options.dbHost, - port: options.dbPort, - username: options.dbUsername, - password: options.dbPassword, - database: options.dbDatabase, -} -plugins['@arkecosystem/core-blockchain'] = { - fastRebuild: false, -} -plugins['@arkecosystem/core-forger'] = { - hosts: [`http://${requestNodeIp}:${options.p2pPort}`], -} - -updateConfig('plugins.json', plugins, options.configPath, true) -fs.removeSync(path.resolve(options.configPath, 'plugins.js')) - -writeEnv( - { - P2P_PORT: options.p2pPort, - API_PORT: options.apiPort, - DB_PORT: options.dbPort, - }, - '~/.ark/.env', -) diff --git a/packages/core-deployer/jest.config.js b/packages/core-deployer/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-deployer/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-deployer/lib/builder/genesis-block.js b/packages/core-deployer/lib/builder/genesis-block.js deleted file mode 100644 index 1f6d8d0c5e..0000000000 --- a/packages/core-deployer/lib/builder/genesis-block.js +++ /dev/null @@ -1,303 +0,0 @@ -const { Bignum, client, crypto } = require('@arkecosystem/crypto') -const bip39 = require('bip39') -const ByteBuffer = require('bytebuffer') -const { createHash } = require('crypto') - -module.exports = class GenesisBlockBuilder { - /** - * Create a new Genesis Block builder instance. - * @param {Object} options - * @return {void} - */ - constructor(network, options) { - this.network = network - this.prefixHash = network.pubKeyHash - this.totalPremine = options.totalPremine - this.activeDelegates = options.activeDelegates - } - - /** - * Generate a Genesis Block. - * @return {Object} - */ - generate() { - const genesisWallet = this.__createWallet() - const premineWallet = this.__createWallet() - const delegates = this.__buildDelegates() - const transactions = [ - ...this.__buildDelegateTransactions(delegates), - this.__createTransferTransaction( - premineWallet, - genesisWallet, - this.totalPremine, - ), - ] - const genesisBlock = this.__createGenesisBlock({ - keys: genesisWallet.keys, - transactions, - timestamp: 0, - }) - - return { - genesisWallet, - genesisBlock, - delegatePassphrases: delegates.map(wallet => wallet.passphrase), - } - } - - /** - * Generate a new random wallet. - * @return {Object} - */ - __createWallet() { - const passphrase = bip39.generateMnemonic() - const keys = crypto.getKeys(passphrase) - - return { - address: crypto.getAddress(keys.publicKey, this.prefixHash), - passphrase, - keys, - } - } - - /** - * Generate a random wallet and assign it a delegate username. - * @param {String} username - * @return {Object} - */ - __createDelegateWallet(username) { - const wallet = this.__createWallet() - wallet.username = username - - return wallet - } - - /** - * Generate a collection of delegate wallets. - * @return {Object[]} - */ - __buildDelegates() { - const wallets = [] - for (let i = 0; i < this.activeDelegates; i++) { - wallets.push(this.__createDelegateWallet(`genesis_${i + 1}`)) - } - - return wallets - } - - /** - * Generate a collection of delegate registration transactions. - * @param {Object[]} wallets - * @return {Object[]} - */ - __buildDelegateTransactions(wallets) { - return wallets.map(wallet => this.__createDelegateTransaction(wallet)) - } - - /** - * Create transfer transaction. - * @param {Object} senderWallet - * @param {Object} receiverWallet - * @param {Number} amount - * @return {Object} - */ - __createTransferTransaction(senderWallet, receiverWallet, amount) { - const { data } = client - .getBuilder() - .transfer() - .recipientId(receiverWallet.address) - .amount(amount) - .network(this.prefixHash) - .sign(senderWallet.passphrase) - - return this.__formatGenesisTransaction(data, senderWallet) - } - - /** - * Create delegate registration transaction. - * @param {Object} wallet - * @return {Object} - */ - __createDelegateTransaction(wallet) { - const { data } = client - .getBuilder() - .delegateRegistration() - .usernameAsset(wallet.username) - .sign(wallet.passphrase) - - return this.__formatGenesisTransaction(data, wallet) - } - - /** - * Reset transaction to be applied in the genesis block. - * @param {Object} transaction - * @param {Object} wallet - * @return {Object} - */ - __formatGenesisTransaction(transaction, wallet) { - Object.assign(transaction, { - fee: 0, - timestamp: 0, - senderId: wallet.address, - }) - transaction.signature = crypto.sign(transaction, wallet.keys) - transaction.id = crypto.getId(transaction) - - return transaction - } - - /** - * Create block based on data. - * @param {Object} data - * @return {Object} - */ - __createGenesisBlock(data) { - const transactions = data.transactions.sort((a, b) => { - if (a.type === b.type) { - return a.amount - b.amount - } - - return a.type - b.type - }) - - let payloadLength = 0 - let totalFee = 0 - let totalAmount = 0 - const payloadHash = createHash('sha256') - - transactions.forEach(transaction => { - const bytes = crypto.getBytes(transaction) - payloadLength += bytes.length - totalFee += transaction.fee - totalAmount += transaction.amount - payloadHash.update(bytes) - }) - - const block = { - version: 0, - totalAmount, - totalFee, - reward: 0, - payloadHash: payloadHash.digest().toString('hex'), - timestamp: data.timestamp, - numberOfTransactions: transactions.length, - payloadLength, - previousBlock: null, - generatorPublicKey: data.keys.publicKey.toString('hex'), - transactions, - height: 1, - } - - block.id = this.__getBlockId(block) - - try { - block.blockSignature = this.__signBlock(block, data.keys) - } catch (e) { - throw e - } - - return block - } - - /** - * Work out block id for block. - * @param {Object} block - * @return {String} - */ - __getBlockId(block) { - const hash = this.__getHash(block) - const blockBuffer = Buffer.alloc(8) - for (let i = 0; i < 8; i++) { - blockBuffer[i] = hash[7 - i] - } - - return new Bignum(blockBuffer.toString('hex'), 16).toString() - } - - /** - * Sign block with keys. - * @param {Object} block - * @param {Object]} keys - * @return {String} - */ - __signBlock(block, keys) { - const hash = this.__getHash(block) - return crypto.signHash(hash, keys) - } - - /** - * Get hash of block. - * @param {Object} block - * @return {String} - */ - __getHash(block) { - return createHash('sha256') - .update(this.__getBytes(block)) - .digest() - } - - /** - * Get block bytes. - * @param {Object} block - * @return {(Buffer|undefined)} - */ - __getBytes(block) { - const size = 4 + 4 + 4 + 8 + 4 + 4 + 8 + 8 + 4 + 4 + 4 + 32 + 32 + 64 - - try { - const byteBuffer = new ByteBuffer(size, true) - byteBuffer.writeInt(block.version) - byteBuffer.writeInt(block.timestamp) - byteBuffer.writeInt(block.height) - - if (block.previousBlock) { - const previousBlock = Buffer.from( - new Bignum(block.previousBlock).toString(16), - 'hex', - ) - - for (let i = 0; i < 8; i++) { - byteBuffer.writeByte(previousBlock[i]) - } - } else { - for (let i = 0; i < 8; i++) { - byteBuffer.writeByte(0) - } - } - - byteBuffer.writeInt(block.numberOfTransactions) - byteBuffer.writeLong(block.totalAmount) - byteBuffer.writeLong(block.totalFee) - byteBuffer.writeLong(block.reward) - - byteBuffer.writeInt(block.payloadLength) - - const payloadHashBuffer = Buffer.from(block.payloadHash, 'hex') - for (let i = 0; i < payloadHashBuffer.length; i++) { - byteBuffer.writeByte(payloadHashBuffer[i]) - } - - const generatorPublicKeyBuffer = Buffer.from( - block.generatorPublicKey, - 'hex', - ) - for (let i = 0; i < generatorPublicKeyBuffer.length; i++) { - byteBuffer.writeByte(generatorPublicKeyBuffer[i]) - } - - if (block.blockSignature) { - const blockSignatureBuffer = Buffer.from(block.blockSignature, 'hex') - for (let i = 0; i < blockSignatureBuffer.length; i++) { - byteBuffer.writeByte(blockSignatureBuffer[i]) - } - } - - byteBuffer.flip() - const buffer = byteBuffer.toBuffer() - - return buffer - } catch (error) { - throw error - } - } -} diff --git a/packages/core-deployer/lib/logger.js b/packages/core-deployer/lib/logger.js deleted file mode 100644 index 1659945fe0..0000000000 --- a/packages/core-deployer/lib/logger.js +++ /dev/null @@ -1,7 +0,0 @@ -const pino = require('pino') - -module.exports = pino({ - name: 'ark-tester-cli', - safe: true, - prettyPrint: true, -}) diff --git a/packages/core-deployer/lib/schema.js b/packages/core-deployer/lib/schema.js deleted file mode 100644 index 621d12766a..0000000000 --- a/packages/core-deployer/lib/schema.js +++ /dev/null @@ -1,45 +0,0 @@ -const Joi = require('joi') - -module.exports = Joi.object().keys({ - network: Joi.string().required(), - name: Joi.string().required(), - nodeIp: Joi.string().required(), - p2pPort: Joi.number().required(), - apiPort: Joi.number().required(), - dbHost: Joi.string().required(), - dbPort: Joi.number().required(), - dbUsername: Joi.string().required(), - dbPassword: Joi.string().required(), - dbDatabase: Joi.string().required(), - explorerUrl: Joi.string() - .uri({ scheme: ['http', 'https'] }) - .required(), - activeDelegates: Joi.number().required(), - feeTransfer: Joi.number().required(), - feeVote: Joi.number().required(), - feeSecondSignature: Joi.number().required(), - feeDelegateRegistration: Joi.number().required(), - feeMultiSignature: Joi.number().required(), - epoch: Joi.string() - .regex( - /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/, - ) - .required(), - rewardHeight: Joi.number() - .integer() - .positive() - .required(), - rewardPerBlock: Joi.number().required(), - blocktime: Joi.number().required(), - token: Joi.string().required(), - symbol: Joi.string().required(), - prefixHash: Joi.number().required(), - transactionsPerBlock: Joi.number().required(), - wifPrefix: Joi.number() - .integer() - .min(1) - .max(255) - .required(), - totalPremine: Joi.number().required(), - configPath: Joi.string().required(), -}) diff --git a/packages/core-deployer/lib/utils.js b/packages/core-deployer/lib/utils.js deleted file mode 100644 index c63a67218c..0000000000 --- a/packages/core-deployer/lib/utils.js +++ /dev/null @@ -1,52 +0,0 @@ -const set = require('lodash/set') -const envfile = require('envfile') -const expandHomeDir = require('expand-home-dir') -const fs = require('fs-extra') -const path = require('path') - -/** - * Get a random number from range. - * @param {Number} min - * @param {Number} max - * @return {Number} - */ -exports.getRandomNumber = (min, max) => - Math.floor(Math.random() * (max - min) + min) - -exports.logger = require('./logger') - -/** - * Update the contents of the given file and return config. - * @param {String} file - * @param {Object} values - * @return {Object} - */ -exports.updateConfig = (file, values, configPath, forceOverwrite) => { - configPath = configPath || `${process.env.ARK_PATH_CONFIG}/deployer` - configPath = path.resolve(configPath, file) - let config - if (fs.existsSync(configPath) && !forceOverwrite) { - config = require(configPath) - } else { - config = {} - } - - Object.keys(values).forEach(key => set(config, key, values[key])) - - fs.ensureFileSync(configPath) - fs.writeFileSync(configPath, JSON.stringify(config, null, 2)) - - return config -} - -/** - * Write Environment variables to file. - * @param {Object} object - * @param {String} path - * @return {void} - */ -exports.writeEnv = (object, filePath) => { - filePath = expandHomeDir(filePath) - fs.ensureDirSync(path.dirname(filePath)) - fs.writeFileSync(filePath, envfile.stringifySync(object)) -} diff --git a/packages/core-deployer/package.json b/packages/core-deployer/package.json deleted file mode 100644 index 3f2465136c..0000000000 --- a/packages/core-deployer/package.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "name": "@arkecosystem/core-deployer", - "description": "Deployer for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust ", - "Alex Barnsley " - ], - "license": "MIT", - "main": "lib/index.js", - "bin": { - "ark:deployer": "./bin/deployer" - }, - "scripts": { - "start": "./bin/deployer", - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/crypto": "~0.2", - "bip39": "^2.5.0", - "bytebuffer": "^5.0.1", - "commander": "^2.19.0", - "envfile": "^2.3.0", - "expand-home-dir": "0.0.3", - "fs-extra": "^7.0.1", - "joi": "^14.3.0", - "lodash.set": "^4.3.2", - "pino": "^5.9.0", - "pino-pretty": "^2.3.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } -} diff --git a/packages/core-elasticsearch/CHANGELOG.md b/packages/core-elasticsearch/CHANGELOG.md deleted file mode 100644 index 6842caf9df..0000000000 --- a/packages/core-elasticsearch/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.1.0 - 2018-12-03 - -### Added - -- initial release diff --git a/packages/core-elasticsearch/LICENSE b/packages/core-elasticsearch/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-elasticsearch/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-elasticsearch/README.md b/packages/core-elasticsearch/README.md index 140161d7f3..7d91695924 100644 --- a/packages/core-elasticsearch/README.md +++ b/packages/core-elasticsearch/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-elasticsearch/jest.config.js b/packages/core-elasticsearch/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-elasticsearch/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-elasticsearch/lib/defaults.js b/packages/core-elasticsearch/lib/defaults.js deleted file mode 100644 index 229aa2c9af..0000000000 --- a/packages/core-elasticsearch/lib/defaults.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - server: { - host: '0.0.0.0', - port: 4007, - whitelist: ['*'], - }, - client: { - host: 'localhost:9200', - log: 'info', - }, - chunkSize: 50000, -} diff --git a/packages/core-elasticsearch/lib/index.js b/packages/core-elasticsearch/lib/index.js deleted file mode 100644 index 1546591aae..0000000000 --- a/packages/core-elasticsearch/lib/index.js +++ /dev/null @@ -1,39 +0,0 @@ -const blockIndex = require('./index/block') -const transactionIndex = require('./index/transaction') -const walletIndex = require('./index/wallet') -const roundIndex = require('./index/round') -const client = require('./services/client') -const storage = require('./services/storage') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'elasticsearch', - async register(container, options) { - const logger = container.resolvePlugin('logger') - - logger.info('[Elasticsearch] Initialising History :hourglass:') - storage.ensure('history') - - logger.info('[Elasticsearch] Initialising Client :joystick:') - await client.setUp(options.client) - - blockIndex.setUp(options.chunkSize) - transactionIndex.setUp(options.chunkSize) - walletIndex.setUp(options.chunkSize) - roundIndex.setUp(options.chunkSize) - - return require('./server')(options.server) - }, - async deregister(container, options) { - container - .resolvePlugin('logger') - .info('[Elasticsearch] Stopping API :warning:') - - return container.resolvePlugin('elasticsearch').stop() - }, -} diff --git a/packages/core-elasticsearch/lib/index/block.js b/packages/core-elasticsearch/lib/index/block.js deleted file mode 100644 index c72cf21e9b..0000000000 --- a/packages/core-elasticsearch/lib/index/block.js +++ /dev/null @@ -1,87 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const first = require('lodash/first') -const last = require('lodash/last') -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') -const logger = app.resolvePlugin('logger') -const Index = require('./index') -const client = require('../services/client') -const storage = require('../services/storage') - -class BlockIndex extends Index { - /** - * Index blocks using the specified chunk size. - * @return {void} - */ - async index() { - const { count } = await this.__count() - - const queries = Math.ceil(count / this.chunkSize) - - for (let i = 0; i < queries; i++) { - const modelQuery = this.__createQuery() - - const query = modelQuery - .select() - .from(modelQuery) - .where(modelQuery.timestamp.gte(storage.get('history', 'lastBlock'))) - .order(modelQuery.height.asc) - .limit(this.chunkSize) - .offset(this.chunkSize * i) - - const rows = await database.query.manyOrNone(query.toQuery()) - - if (!rows.length) { - continue - } - - const heights = rows.map(row => row.height) - logger.info( - `[Elasticsearch] Indexing blocks from height ${first( - heights, - )} to ${last(heights)} :card_index_dividers:`, - ) - - try { - await client.bulk(this._buildBulkUpsert(rows)) - - storage.update('history', { - lastBlock: last(heights), - }) - } catch (error) { - logger.error(`[Elasticsearch] ${error.message} :exclamation:`) - } - } - } - - /** - * Register listeners for "block.*" events. - * @return {void} - */ - listen() { - this._registerCreateListener('block.applied') - // this._registerCreateListener('block.forged') - - this._registerDeleteListener('block.reverted') - } - - /** - * Get the document index. - * @return {String} - */ - getIndex() { - return 'blocks' - } - - /** - * Get the document type. - * @return {String} - */ - getType() { - return 'block' - } -} - -module.exports = new BlockIndex() diff --git a/packages/core-elasticsearch/lib/index/index.js b/packages/core-elasticsearch/lib/index/index.js deleted file mode 100644 index 27c67a9817..0000000000 --- a/packages/core-elasticsearch/lib/index/index.js +++ /dev/null @@ -1,181 +0,0 @@ -/* eslint camelcase: "off" */ - -const app = require('@arkecosystem/core-container') - -const emitter = app.resolvePlugin('event-emitter') -const logger = app.resolvePlugin('logger') -const database = app.resolvePlugin('database') -const client = require('../services/client') -const storage = require('../services/storage') - -module.exports = class Index { - /** - * Create a new index instance. - * @param {Number} chunkSize - * @return {void} - */ - setUp(chunkSize) { - logger.info(`[Elasticsearch] Initialising ${this.getType()} index :scroll:`) - this.chunkSize = chunkSize - - logger.info( - `[Elasticsearch] Initialising ${this.getType()} listener :radio:`, - ) - this.listen() - - logger.info(`[Elasticsearch] Indexing ${this.getIndex()} :bookmark:`) - this.index() - } - - /** - * Register a new "CREATE" operation listener. - * @param {String} event - * @return {void} - */ - _registerCreateListener(event) { - emitter.on(event, async doc => { - try { - const exists = await this._exists(doc) - - if (!exists) { - await this._create(doc) - } - } catch (error) { - logger.error(`[Elasticsearch] ${error.message} :exclamation:`) - } - }) - } - - /** - * Register a new "DELETE" operation listener. - * @param {String} event - * @return {void} - */ - _registerDeleteListener(event) { - emitter.on(event, async doc => { - try { - const exists = await this._exists(doc) - - if (exists) { - await this._delete(doc) - } - } catch (error) { - logger.error(`[Elasticsearch] ${error.message} :exclamation:`) - } - }) - } - - /** - * Check if the specified document exists. - * @param {String} doc - * @return {Promise} - */ - _exists(doc) { - return client.exists(this._getReadQuery(doc)) - } - - /** - * Create a new document. - * @param {String} doc - * @return {Promise} - */ - _create(doc) { - logger.info(`[Elasticsearch] Creating ${this.getType()} with ID ${doc.id}`) - - if (this.getType() === 'block') { - storage.update('history', { lastBlock: doc.height }) - } else { - storage.update('history', { lastTransaction: doc.timestamp }) - } - - return client.create(this._getWriteQuery(doc)) - } - - /** - * Delete the specified document. - * @param {String} doc - * @return {Promise} - */ - _delete(doc) { - logger.info(`[Elasticsearch] Deleting ${this.getType()} with ID ${doc.id}`) - - return client.delete(this._getReadQuery(doc)) - } - - /** - * Get a query for a "WRITE" operation. - * @param {String} doc - * @return {Object} - */ - _getWriteQuery(doc) { - return { - index: this.getIndex(), - type: this.getType(), - id: doc.id, - body: doc, - } - } - - /** - * Get a query for a "READ" operation. - * @param {String} doc - * @return {Object} - */ - _getReadQuery(doc) { - return { - index: this.getIndex(), - type: this.getType(), - id: doc.id, - } - } - - /** - * Get a query for a "READ" operation. - * @param {String} doc - * @return {Object} - */ - _getUpsertQuery(doc) { - return { - action: { - update: { - _index: this.getIndex(), - _type: this.getType(), - _id: doc.id, - }, - }, - document: { - doc, - doc_as_upsert: true, - }, - } - } - - /** - * Get a query for a "READ" operation. - * @param {Array} items - * @return {Object} - */ - _buildBulkUpsert(items) { - const actions = [] - - items.forEach(item => { - const query = this._getUpsertQuery(item) - actions.push(query.action) - actions.push(query.document) - }) - - return actions - } - - __createQuery() { - return database.models[this.getType()].query() - } - - __count() { - const modelQuery = this.__createQuery() - - const query = modelQuery.select(modelQuery.count('count')).from(modelQuery) - - return database.query.one(query.toQuery()) - } -} diff --git a/packages/core-elasticsearch/lib/index/round.js b/packages/core-elasticsearch/lib/index/round.js deleted file mode 100644 index c0ee818141..0000000000 --- a/packages/core-elasticsearch/lib/index/round.js +++ /dev/null @@ -1,85 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const first = require('lodash/first') -const last = require('lodash/last') -const app = require('@arkecosystem/core-container') - -const emitter = app.resolvePlugin('event-emitter') -const database = app.resolvePlugin('database') -const logger = app.resolvePlugin('logger') -const Index = require('./index') -const client = require('../services/client') -const storage = require('../services/storage') - -class RoundIndex extends Index { - /** - * Index rounds using the specified chunk size. - * @return {void} - */ - async index() { - const { count } = await this.__count() - - const queries = Math.ceil(count / this.chunkSize) - - for (let i = 0; i < queries; i++) { - const modelQuery = this.__createQuery() - - const query = modelQuery - .select() - .from(modelQuery) - .where(modelQuery.round.gte(storage.get('history', 'lastRound'))) - .order(modelQuery.round.asc) - .limit(this.chunkSize) - .offset(this.chunkSize * i) - - const rows = await database.query.manyOrNone(query.toQuery()) - - if (!rows.length) { - continue - } - - const roundIds = rows.map(row => row.round) - logger.info( - `[Elasticsearch] Indexing rounds from ${first(roundIds)} to ${last( - roundIds, - )} :card_index_dividers:`, - ) - - try { - await client.bulk(this._buildBulkUpsert(rows)) - - storage.update('history', { - lastRound: last(roundIds), - }) - } catch (error) { - logger.error(`[Elasticsearch] ${error.message} :exclamation:`) - } - } - } - - /** - * Register listeners for "round.*" events. - * @return {void} - */ - listen() { - emitter.on('round.created', data => this.index()) - } - - /** - * Get the document index. - * @return {String} - */ - getIndex() { - return 'rounds' - } - - /** - * Get the document type. - * @return {String} - */ - getType() { - return 'round' - } -} - -module.exports = new RoundIndex() diff --git a/packages/core-elasticsearch/lib/index/transaction.js b/packages/core-elasticsearch/lib/index/transaction.js deleted file mode 100644 index 903013867d..0000000000 --- a/packages/core-elasticsearch/lib/index/transaction.js +++ /dev/null @@ -1,98 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const first = require('lodash/first') -const last = require('lodash/last') -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') -const logger = app.resolvePlugin('logger') -const { Transaction } = require('@arkecosystem/crypto').models -const Index = require('./index') -const client = require('../services/client') -const storage = require('../services/storage') - -class TransactionIndex extends Index { - /** - * Index transactions using the specified chunk size. - * @return {void} - */ - async index() { - const { count } = await this.__count() - - const queries = Math.ceil(count / this.chunkSize) - - for (let i = 0; i < queries; i++) { - const modelQuery = this.__createQuery() - - const query = modelQuery - .select(modelQuery.block_id, modelQuery.serialized) - .from(modelQuery) - .where( - modelQuery.timestamp.gte(storage.get('history', 'lastTransaction')), - ) - .order(modelQuery.timestamp.asc) - .limit(this.chunkSize) - .offset(this.chunkSize * i) - - let rows = await database.query.manyOrNone(query.toQuery()) - - if (!rows.length) { - continue - } - - rows = rows.map(row => { - const transaction = new Transaction(row.serialized.toString('hex')) - transaction.blockId = row.blockId - - return transaction - }) - - const blockIds = rows.map(row => row.blockId) - logger.info( - `[Elasticsearch] Indexing transactions from block ${first( - blockIds, - )} to ${last(blockIds)} :card_index_dividers:`, - ) - - try { - await client.bulk(this._buildBulkUpsert(rows)) - - storage.update('history', { - lastTransaction: last(rows.map(row => row.timestamp)), - }) - } catch (error) { - logger.error(`[Elasticsearch] ${error.message} :exclamation:`) - } - } - } - - /** - * Register listeners for "transaction.*" events. - * @return {void} - */ - listen() { - this._registerCreateListener('transaction.applied') - this._registerCreateListener('transaction.forged') - - this._registerDeleteListener('transaction.expired') - this._registerDeleteListener('transaction.reverted') - } - - /** - * Get the document index. - * @return {String} - */ - getIndex() { - return 'transactions' - } - - /** - * Get the document type. - * @return {String} - */ - getType() { - return 'transaction' - } -} - -module.exports = new TransactionIndex() diff --git a/packages/core-elasticsearch/lib/index/wallet.js b/packages/core-elasticsearch/lib/index/wallet.js deleted file mode 100644 index 57f305b91d..0000000000 --- a/packages/core-elasticsearch/lib/index/wallet.js +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const app = require('@arkecosystem/core-container') - -const emitter = app.resolvePlugin('event-emitter') -const database = app.resolvePlugin('database') -const logger = app.resolvePlugin('logger') -const Index = require('./index') -const client = require('../services/client') - -class WalletIndex extends Index { - /** - * Index wallets using the specified chunk size. - * @return {void} - */ - async index() { - const { count } = await this.__count() - - const queries = Math.ceil(count / this.chunkSize) - - for (let i = 0; i < queries; i++) { - const modelQuery = this.__createQuery() - - const query = modelQuery - .select() - .from(modelQuery) - .limit(this.chunkSize) - .offset(this.chunkSize * i) - - const rows = await database.query.manyOrNone(query.toQuery()) - - if (!rows.length) { - continue - } - - logger.info( - `[Elasticsearch] Indexing ${rows.length} wallets :card_index_dividers:`, - ) - - try { - rows.forEach(row => { - row.id = row.address - }) - - await client.bulk(this._buildBulkUpsert(rows)) - } catch (error) { - logger.error(`[Elasticsearch] ${error.message} :exclamation:`) - } - } - } - - /** - * Register listeners for "wallet.*" events. - * @return {void} - */ - listen() { - emitter.on('wallets:updated', data => this.index()) - } - - /** - * Get the document index. - * @return {String} - */ - getIndex() { - return 'wallets' - } - - /** - * Get the document type. - * @return {String} - */ - getType() { - return 'wallet' - } -} - -module.exports = new WalletIndex() diff --git a/packages/core-elasticsearch/lib/server/handler.js b/packages/core-elasticsearch/lib/server/handler.js deleted file mode 100644 index 4f762ef63e..0000000000 --- a/packages/core-elasticsearch/lib/server/handler.js +++ /dev/null @@ -1,68 +0,0 @@ -const Joi = require('joi') -const client = require('../services/client') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const query = await client.search(request.payload) - - return { - meta: { - count: query.hits.total, - }, - data: query.hits.hits.map(result => result._source), - } - }, - options: { - validate: { - payload: { - analyzer: Joi.string(), - analyzeWildcard: Joi.boolean(), - defaultOperator: Joi.string(), - df: Joi.string(), - explain: Joi.boolean(), - storedFields: Joi.any(), - docvalueFields: Joi.any(), - from: Joi.number(), - allowNoIndices: Joi.boolean(), - expandWildcards: Joi.string(), - lenient: Joi.boolean(), - preference: Joi.string(), - q: Joi.string(), - routing: Joi.any(), - scroll: Joi.string(), - searchType: Joi.string(), - size: Joi.number().default(10), - sort: Joi.any(), - _source: Joi.any(), - _sourceExclude: Joi.any(), - _sourceInclude: Joi.any(), - terminateAfter: Joi.number(), - stats: Joi.any(), - suggestField: Joi.string(), - suggestMode: Joi.string(), - suggestSize: Joi.number(), - suggestText: Joi.string(), - timeout: Joi.string(), - trackScores: Joi.boolean(), - trackTotalHits: Joi.boolean(), - typedKeys: Joi.boolean(), - version: Joi.boolean(), - requestCache: Joi.boolean(), - batchedReduceSize: Joi.number(), - maxConcurrentShardRequests: Joi.number(), - preFilterShardSize: Joi.number(), - index: Joi.any(), - type: Joi.any(), - body: Joi.object(), - }, - }, - }, -} diff --git a/packages/core-elasticsearch/lib/server/index.js b/packages/core-elasticsearch/lib/server/index.js deleted file mode 100644 index fbb0e30a85..0000000000 --- a/packages/core-elasticsearch/lib/server/index.js +++ /dev/null @@ -1,36 +0,0 @@ -const { - createServer, - mountServer, - plugins, -} = require('@arkecosystem/core-http-utils') - -/** - * Creates a new hapi.js server. - * @param {Object} config - * @return {Hapi.Server} - */ -module.exports = async config => { - const server = await createServer({ - host: config.host, - port: config.port, - routes: { - validate: { - async failAction(request, h, err) { - throw err - }, - }, - }, - }) - - await server.register({ - plugin: plugins.whitelist, - options: { - whitelist: config.whitelist, - name: 'Elasticsearch API', - }, - }) - - await server.register(require('./routes')) - - return mountServer('Elasticsearch API', server) -} diff --git a/packages/core-elasticsearch/lib/server/routes.js b/packages/core-elasticsearch/lib/server/routes.js deleted file mode 100644 index f44a24626f..0000000000 --- a/packages/core-elasticsearch/lib/server/routes.js +++ /dev/null @@ -1,27 +0,0 @@ -const handler = require('./handler') - -/** - * Register search routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.route([ - { - method: 'POST', - path: '/', - ...handler.index, - }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'routes', - version: '0.1.0', - register, -} diff --git a/packages/core-elasticsearch/lib/services/client.js b/packages/core-elasticsearch/lib/services/client.js deleted file mode 100644 index 5b392b8193..0000000000 --- a/packages/core-elasticsearch/lib/services/client.js +++ /dev/null @@ -1,84 +0,0 @@ -const elasticsearch = require('elasticsearch') - -class Client { - /** - * Create a new client instance. - * @param {Object} options - */ - async setUp(options) { - this.client = new elasticsearch.Client(options) - } - - /** - * Get the elasticsearch client. - * @return {elasticsearch.Client} - */ - async getClient() { - return this.client - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} body - * @return {Promise} - */ - async bulk(body) { - return this.client.bulk({ body }) - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} params - * @return {Promise} - */ - async count(params) { - return this.client.count(params) - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} params - * @return {Promise} - */ - async search(params) { - return this.client.search(params) - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} params - * @return {Promise} - */ - async create(params) { - return this.client.create(params) - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} params - * @return {Promise} - */ - async update(params) { - return this.client.update(params) - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} params - * @return {Promise} - */ - async delete(params) { - return this.client.delete(params) - } - - /** - * Perform an "UPDATE" operation. - * @param {Object} params - * @return {Promise} - */ - async exists(params) { - return this.client.exists(params) - } -} - -module.exports = new Client() diff --git a/packages/core-elasticsearch/lib/services/storage.js b/packages/core-elasticsearch/lib/services/storage.js deleted file mode 100644 index bf55f1b35f..0000000000 --- a/packages/core-elasticsearch/lib/services/storage.js +++ /dev/null @@ -1,96 +0,0 @@ -const fs = require('fs-extra') -const loget = require('lodash/get') - -class Storage { - /** - * Create a new storage instance. - * @return {void} - */ - constructor() { - this.base = `${process.env.ARK_PATH_DATA}/plugins/core-elasticsearch` - } - - /** - * Read & parse the specified file. - * @param {String} file - * @return {Object} - */ - read(file) { - return this.exists(file) - ? JSON.parse(fs.readFileSync(`${this.base}/${file}.json`)) - : {} - } - - /** - * Write the specified data to the specified file. - * @param {String} file - * @param {Object} data - * @return {void} - */ - write(file, data) { - fs.ensureFileSync(`${this.base}/${file}.json`) - - fs.writeFileSync(`${this.base}/${file}.json`, JSON.stringify(data, null, 2)) - } - - /** - * Update the specified data in the specified file. - * @param {String} file - * @param {Object} data - * @return {void} - */ - update(file, data) { - fs.ensureFileSync(`${this.base}/${file}.json`) - - data = Object.assign(this.read(file), data) - - fs.writeFileSync(`${this.base}/${file}.json`, JSON.stringify(data, null, 2)) - } - - /** - * Update the specified data in the specified file. - * @param {String} file - * @param {Object} data - * @return {void} - */ - ensure(file) { - if (!this.exists(file)) { - fs.ensureFileSync(`${this.base}/${file}.json`) - - fs.writeFileSync( - `${this.base}/${file}.json`, - JSON.stringify( - { - lastRound: 0, - lastBlock: 0, - lastTransaction: 0, - }, - null, - 2, - ), - ) - } - } - - /** - * Determine if the specified file exists. - * @param {String} file - * @return {Boolean} - */ - exists(file) { - return fs.existsSync(`${this.base}/${file}.json`) - } - - /** - * Get a value from the specified file for the specified key. - * @param {String} file - * @param {String} key - * @param {*} key - * @return {*} - */ - get(file, key, defaultValue = null) { - return loget(this.read(file), key, defaultValue) - } -} - -module.exports = new Storage() diff --git a/packages/core-elasticsearch/package.json b/packages/core-elasticsearch/package.json index 8481b00f7e..fc485a5fe2 100644 --- a/packages/core-elasticsearch/package.json +++ b/packages/core-elasticsearch/package.json @@ -1,35 +1,51 @@ { - "name": "@arkecosystem/core-elasticsearch", - "description": "A powerful Elasticsearch integration for Ark Core", - "version": "0.1.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "elasticsearch": "^15.2.0", - "fs-extra": "^7.0.1", - "joi": "^14.3.0", - "lodash.first": "^3.0.0", - "lodash.get": "^4.4.2", - "lodash.last": "^3.0.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-elasticsearch", + "description": "A powerful Elasticsearch integration for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/elasticsearch": "^5.0.30", + "@types/fs-extra": "^5.0.4", + "@types/joi": "^14.0.1", + "@types/lodash.first": "^3.0.4", + "@types/lodash.get": "^4.4.4", + "@types/lodash.last": "^3.0.4", + "elasticsearch": "^15.2.0", + "fs-extra": "^7.0.1", + "joi": "^14.3.0", + "lodash.first": "^3.0.0", + "lodash.get": "^4.4.2", + "lodash.last": "^3.0.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-elasticsearch/src/defaults.ts b/packages/core-elasticsearch/src/defaults.ts new file mode 100644 index 0000000000..5dc306b667 --- /dev/null +++ b/packages/core-elasticsearch/src/defaults.ts @@ -0,0 +1,12 @@ +export const defaults = { + server: { + host: "0.0.0.0", + port: 4007, + whitelist: ["*"], + }, + client: { + host: "localhost:9200", + log: "info", + }, + chunkSize: 50000, +}; diff --git a/packages/core-elasticsearch/src/index.ts b/packages/core-elasticsearch/src/index.ts new file mode 100644 index 0000000000..2ca124d76e --- /dev/null +++ b/packages/core-elasticsearch/src/index.ts @@ -0,0 +1,36 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { blockIndex } from "./index/block"; +import { roundIndex } from "./index/round"; +import { transactionIndex } from "./index/transaction"; +import { walletIndex } from "./index/wallet"; +import { startServer } from "./server"; +import { client } from "./services/client"; +import { storage } from "./services/storage"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "elasticsearch", + async register(container: Container.IContainer, options) { + const logger = container.resolvePlugin("logger"); + + logger.info("[Elasticsearch] Initialising History :hourglass:"); + storage.ensure("history"); + + logger.info("[Elasticsearch] Initialising Client :joystick:"); + await client.setUp(options.client); + + blockIndex.setUp(options.chunkSize); + transactionIndex.setUp(options.chunkSize); + walletIndex.setUp(options.chunkSize); + roundIndex.setUp(options.chunkSize); + + return startServer(options.server); + }, + async deregister(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("[Elasticsearch] Stopping API :warning:"); + + return container.resolvePlugin("elasticsearch").stop(); + }, +}; diff --git a/packages/core-elasticsearch/src/index/block.ts b/packages/core-elasticsearch/src/index/block.ts new file mode 100644 index 0000000000..c1516d02a1 --- /dev/null +++ b/packages/core-elasticsearch/src/index/block.ts @@ -0,0 +1,86 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, Logger } from "@arkecosystem/core-interfaces"; +import first from "lodash/first"; +import last from "lodash/last"; +import { client } from "../services/client"; +import { storage } from "../services/storage"; +import { Index } from "./index"; + +const logger = app.resolvePlugin("logger"); +const databaseService = app.resolvePlugin("database"); + +class BlockIndex extends Index { + /** + * Index blocks using the specified chunk size. + * @return {void} + */ + public async index() { + const { count } = await this.__count(); + + const queries = Math.ceil(count / this.chunkSize); + + for (let i = 0; i < queries; i++) { + const modelQuery = this.__createQuery(); + + const query = modelQuery + .select() + .from(modelQuery) + .where(modelQuery.timestamp.gte(storage.get("history", "lastBlock"))) + .order(modelQuery.height.asc) + .limit(this.chunkSize) + .offset(this.chunkSize * i); + + const rows = await (databaseService.connection as any).query.manyOrNone(query.toQuery()); + + if (!rows.length) { + continue; + } + + const heights = rows.map(row => row.height); + logger.info( + `[Elasticsearch] Indexing blocks from height ${first(heights)} to ${last( + heights, + )} :card_index_dividers:`, + ); + + try { + await client.bulk(this._buildBulkUpsert(rows)); + + storage.update("history", { + lastBlock: last(heights), + }); + } catch (error) { + logger.error(`[Elasticsearch] ${error.message} :exclamation:`); + } + } + } + + /** + * Register listeners for "block.*" events. + * @return {void} + */ + public listen() { + this._registerCreateListener("block.applied"); + // this._registerCreateListener('block.forged') + + this._registerDeleteListener("block.reverted"); + } + + /** + * Get the document index. + * @return {String} + */ + public getIndex() { + return "blocks"; + } + + /** + * Get the document type. + * @return {String} + */ + public getType() { + return "block"; + } +} + +export const blockIndex = new BlockIndex(); diff --git a/packages/core-elasticsearch/src/index/index.ts b/packages/core-elasticsearch/src/index/index.ts new file mode 100644 index 0000000000..d2b7fc9f04 --- /dev/null +++ b/packages/core-elasticsearch/src/index/index.ts @@ -0,0 +1,185 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import { client } from "../services/client"; +import { storage } from "../services/storage"; + +const emitter = app.resolvePlugin("event-emitter"); +const logger = app.resolvePlugin("logger"); +const databaseService = app.resolvePlugin("database"); + +export abstract class Index { + public chunkSize: any; + + public abstract getType(): any; + public abstract getIndex(): any; + public abstract index(): any; + public abstract listen(): any; + + /** + * Create a new index instance. + * @param {Number} chunkSize + * @return {void} + */ + public setUp(chunkSize) { + logger.info(`[Elasticsearch] Initialising ${this.getType()} index :scroll:`); + this.chunkSize = chunkSize; + + logger.info(`[Elasticsearch] Initialising ${this.getType()} listener :radio:`); + this.listen(); + + logger.info(`[Elasticsearch] Indexing ${this.getIndex()} :bookmark:`); + this.index(); + } + + /** + * Register a new "CREATE" operation listener. + * @param {String} event + * @return {void} + */ + public _registerCreateListener(event) { + emitter.on(event, async doc => { + try { + const exists = await this._exists(doc); + + if (!exists) { + await this._create(doc); + } + } catch (error) { + logger.error(`[Elasticsearch] ${error.message} :exclamation:`); + } + }); + } + + /** + * Register a new "DELETE" operation listener. + * @param {String} event + * @return {void} + */ + public _registerDeleteListener(event) { + emitter.on(event, async doc => { + try { + const exists = await this._exists(doc); + + if (exists) { + await this._delete(doc); + } + } catch (error) { + logger.error(`[Elasticsearch] ${error.message} :exclamation:`); + } + }); + } + + /** + * Check if the specified document exists. + * @param {String} doc + * @return {Promise} + */ + public _exists(doc) { + return client.exists(this._getReadQuery(doc)); + } + + /** + * Create a new document. + * @param {String} doc + * @return {Promise} + */ + public _create(doc) { + logger.info(`[Elasticsearch] Creating ${this.getType()} with ID ${doc.id}`); + + if (this.getType() === "block") { + storage.update("history", { lastBlock: doc.height }); + } else { + storage.update("history", { lastTransaction: doc.timestamp }); + } + + return client.create(this._getWriteQuery(doc)); + } + + /** + * Delete the specified document. + * @param {String} doc + * @return {Promise} + */ + public _delete(doc) { + logger.info(`[Elasticsearch] Deleting ${this.getType()} with ID ${doc.id}`); + + return client.delete(this._getReadQuery(doc)); + } + + /** + * Get a query for a "WRITE" operation. + * @param {String} doc + * @return {Object} + */ + public _getWriteQuery(doc) { + return { + index: this.getIndex(), + type: this.getType(), + id: doc.id, + body: doc, + }; + } + + /** + * Get a query for a "READ" operation. + * @param {String} doc + * @return {Object} + */ + public _getReadQuery(doc) { + return { + index: this.getIndex(), + type: this.getType(), + id: doc.id, + }; + } + + /** + * Get a query for a "READ" operation. + * @param {String} doc + * @return {Object} + */ + public _getUpsertQuery(doc) { + return { + action: { + update: { + _index: this.getIndex(), + _type: this.getType(), + _id: doc.id, + }, + }, + document: { + doc, + doc_as_upsert: true, + }, + }; + } + + /** + * Get a query for a "READ" operation. + * @param {Array} items + * @return {Object} + */ + public _buildBulkUpsert(items) { + const actions = []; + + items.forEach(item => { + const query = this._getUpsertQuery(item); + actions.push(query.action); + actions.push(query.document); + }); + + return actions; + } + + public __createQuery() { + return (databaseService.connection as any).models[this.getType()].query(); + } + + public __count() { + const modelQuery = this.__createQuery(); + + const query = modelQuery.select(modelQuery.count("count")).from(modelQuery); + + return (databaseService.connection as any).query.one(query.toQuery()); + } +} diff --git a/packages/core-elasticsearch/src/index/round.ts b/packages/core-elasticsearch/src/index/round.ts new file mode 100644 index 0000000000..cae825e467 --- /dev/null +++ b/packages/core-elasticsearch/src/index/round.ts @@ -0,0 +1,82 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import first from "lodash/first"; +import last from "lodash/last"; +import { client } from "../services/client"; +import { storage } from "../services/storage"; +import { Index } from "./index"; + +const emitter = app.resolvePlugin("event-emitter"); +const logger = app.resolvePlugin("logger"); +const databaseService = app.resolvePlugin("database"); + +class RoundIndex extends Index { + /** + * Index rounds using the specified chunk size. + * @return {void} + */ + public async index() { + const { count } = await this.__count(); + + const queries = Math.ceil(count / this.chunkSize); + + for (let i = 0; i < queries; i++) { + const modelQuery = this.__createQuery(); + + const query = modelQuery + .select() + .from(modelQuery) + .where(modelQuery.round.gte(storage.get("history", "lastRound"))) + .order(modelQuery.round.asc) + .limit(this.chunkSize) + .offset(this.chunkSize * i); + + const rows = await (databaseService.connection as any).query.manyOrNone(query.toQuery()); + + if (!rows.length) { + continue; + } + + const roundIds = rows.map(row => row.round); + logger.info( + `[Elasticsearch] Indexing rounds from ${first(roundIds)} to ${last(roundIds)} :card_index_dividers:`, + ); + + try { + await client.bulk(this._buildBulkUpsert(rows)); + + storage.update("history", { + lastRound: last(roundIds), + }); + } catch (error) { + logger.error(`[Elasticsearch] ${error.message} :exclamation:`); + } + } + } + + /** + * Register listeners for "round.*" events. + * @return {void} + */ + public listen() { + emitter.on("round.created", data => this.index()); + } + + /** + * Get the document index. + * @return {String} + */ + public getIndex() { + return "rounds"; + } + + /** + * Get the document type. + * @return {String} + */ + public getType() { + return "round"; + } +} + +export const roundIndex = new RoundIndex(); diff --git a/packages/core-elasticsearch/src/index/transaction.ts b/packages/core-elasticsearch/src/index/transaction.ts new file mode 100644 index 0000000000..04fb7e4677 --- /dev/null +++ b/packages/core-elasticsearch/src/index/transaction.ts @@ -0,0 +1,97 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import first from "lodash/first"; +import last from "lodash/last"; +import { client } from "../services/client"; +import { storage } from "../services/storage"; +import { Index } from "./index"; + +import { models } from "@arkecosystem/crypto"; +const { Transaction } = models; + +const logger = app.resolvePlugin("logger"); +const databaseService = app.resolvePlugin("database"); + +class TransactionIndex extends Index { + /** + * Index transactions using the specified chunk size. + * @return {void} + */ + public async index() { + const { count } = await this.__count(); + + const queries = Math.ceil(count / this.chunkSize); + + for (let i = 0; i < queries; i++) { + const modelQuery = this.__createQuery(); + + const query = modelQuery + .select(modelQuery.block_id, modelQuery.serialized) + .from(modelQuery) + .where(modelQuery.timestamp.gte(storage.get("history", "lastTransaction"))) + .order(modelQuery.timestamp.asc) + .limit(this.chunkSize) + .offset(this.chunkSize * i); + + let rows = await (databaseService.connection as any).query.manyOrNone(query.toQuery()); + + if (!rows.length) { + continue; + } + + rows = rows.map(row => { + const transaction: any = new Transaction(row.serialized.toString("hex")); + transaction.blockId = row.blockId; + + return transaction; + }); + + const blockIds = rows.map(row => row.blockId); + logger.info( + `[Elasticsearch] Indexing transactions from block ${first(blockIds)} to ${last( + blockIds, + )} :card_index_dividers:`, + ); + + try { + await client.bulk(this._buildBulkUpsert(rows)); + + storage.update("history", { + lastTransaction: last(rows.map(row => row.timestamp)), + }); + } catch (error) { + logger.error(`[Elasticsearch] ${error.message} :exclamation:`); + } + } + } + + /** + * Register listeners for "transaction.*" events. + * @return {void} + */ + public listen() { + this._registerCreateListener("transaction.applied"); + this._registerCreateListener("transaction.forged"); + + this._registerDeleteListener("transaction.expired"); + this._registerDeleteListener("transaction.reverted"); + } + + /** + * Get the document index. + * @return {String} + */ + public getIndex() { + return "transactions"; + } + + /** + * Get the document type. + * @return {String} + */ + public getType() { + return "transaction"; + } +} + +export const transactionIndex = new TransactionIndex(); diff --git a/packages/core-elasticsearch/src/index/wallet.ts b/packages/core-elasticsearch/src/index/wallet.ts new file mode 100644 index 0000000000..2707be2d2b --- /dev/null +++ b/packages/core-elasticsearch/src/index/wallet.ts @@ -0,0 +1,74 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import { client } from "../services/client"; +import { Index } from "./index"; + +const emitter = app.resolvePlugin("event-emitter"); +const logger = app.resolvePlugin("logger"); +const databaseService = app.resolvePlugin("database"); + +class WalletIndex extends Index { + /** + * Index wallets using the specified chunk size. + * @return {void} + */ + public async index() { + const { count } = await this.__count(); + + const queries = Math.ceil(count / this.chunkSize); + + for (let i = 0; i < queries; i++) { + const modelQuery = this.__createQuery(); + + const query = modelQuery + .select() + .from(modelQuery) + .limit(this.chunkSize) + .offset(this.chunkSize * i); + + const rows = await (databaseService.connection as any).query.manyOrNone(query.toQuery()); + + if (!rows.length) { + continue; + } + + logger.info(`[Elasticsearch] Indexing ${rows.length} wallets :card_index_dividers:`); + + try { + rows.forEach(row => { + row.id = row.address; + }); + + await client.bulk(this._buildBulkUpsert(rows)); + } catch (error) { + logger.error(`[Elasticsearch] ${error.message} :exclamation:`); + } + } + } + + /** + * Register listeners for "wallet.*" events. + * @return {void} + */ + public listen() { + emitter.on("wallets:updated", data => this.index()); + } + + /** + * Get the document index. + * @return {String} + */ + public getIndex() { + return "wallets"; + } + + /** + * Get the document type. + * @return {String} + */ + public getType() { + return "wallet"; + } +} + +export const walletIndex = new WalletIndex(); diff --git a/packages/core-elasticsearch/src/server/handler.ts b/packages/core-elasticsearch/src/server/handler.ts new file mode 100644 index 0000000000..7b99ea77ac --- /dev/null +++ b/packages/core-elasticsearch/src/server/handler.ts @@ -0,0 +1,60 @@ +import Joi from "joi"; +import { client } from "../services/client"; + +export const index = { + async handler(request, h) { + const query = await client.search(request.payload); + + return { + meta: { + count: query.hits.total, + }, + data: query.hits.hits.map(result => result._source), + }; + }, + options: { + validate: { + payload: { + analyzer: Joi.string(), + analyzeWildcard: Joi.boolean(), + defaultOperator: Joi.string(), + df: Joi.string(), + explain: Joi.boolean(), + storedFields: Joi.any(), + docvalueFields: Joi.any(), + from: Joi.number(), + allowNoIndices: Joi.boolean(), + expandWildcards: Joi.string(), + lenient: Joi.boolean(), + preference: Joi.string(), + q: Joi.string(), + routing: Joi.any(), + scroll: Joi.string(), + searchType: Joi.string(), + size: Joi.number().default(10), + sort: Joi.any(), + _source: Joi.any(), + _sourceExclude: Joi.any(), + _sourceInclude: Joi.any(), + terminateAfter: Joi.number(), + stats: Joi.any(), + suggestField: Joi.string(), + suggestMode: Joi.string(), + suggestSize: Joi.number(), + suggestText: Joi.string(), + timeout: Joi.string(), + trackScores: Joi.boolean(), + trackTotalHits: Joi.boolean(), + typedKeys: Joi.boolean(), + version: Joi.boolean(), + requestCache: Joi.boolean(), + batchedReduceSize: Joi.number(), + maxConcurrentShardRequests: Joi.number(), + preFilterShardSize: Joi.number(), + index: Joi.any(), + type: Joi.any(), + body: Joi.object(), + }, + }, + }, +}; diff --git a/packages/core-elasticsearch/src/server/index.ts b/packages/core-elasticsearch/src/server/index.ts new file mode 100644 index 0000000000..eddc4c4ff7 --- /dev/null +++ b/packages/core-elasticsearch/src/server/index.ts @@ -0,0 +1,28 @@ +import { createServer, mountServer, plugins } from "@arkecosystem/core-http-utils"; +import { routes } from "./routes"; + +export async function startServer(config) { + const server = await createServer({ + host: config.host, + port: config.port, + routes: { + validate: { + async failAction(request, h, err) { + throw err; + }, + }, + }, + }); + + await server.register({ + plugin: plugins.whitelist, + options: { + whitelist: config.whitelist, + name: "Elasticsearch API", + }, + }); + + await server.register(routes); + + return mountServer("Elasticsearch API", server); +} diff --git a/packages/core-elasticsearch/src/server/routes.ts b/packages/core-elasticsearch/src/server/routes.ts new file mode 100644 index 0000000000..24efd09ff3 --- /dev/null +++ b/packages/core-elasticsearch/src/server/routes.ts @@ -0,0 +1,15 @@ +import { index } from "./handler"; + +export const routes = { + name: "routes", + version: "0.1.0", + async register(server, options) { + server.route([ + { + method: "POST", + path: "/", + ...index, + }, + ]); + }, +}; diff --git a/packages/core-elasticsearch/src/services/client.ts b/packages/core-elasticsearch/src/services/client.ts new file mode 100644 index 0000000000..f622a30f12 --- /dev/null +++ b/packages/core-elasticsearch/src/services/client.ts @@ -0,0 +1,86 @@ +import elasticsearch from "elasticsearch"; + +class Client { + private client: elasticsearch.Client; + + /** + * Create a new client instance. + * @param {Object} options + */ + public async setUp(options) { + this.client = new elasticsearch.Client(options); + } + + /** + * Get the elasticsearch client. + * @return {elasticsearch.Client} + */ + public async getClient() { + return this.client; + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} body + * @return {Promise} + */ + public async bulk(body) { + return this.client.bulk({ body }); + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} params + * @return {Promise} + */ + public async count(params) { + return this.client.count(params); + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} params + * @return {Promise} + */ + public async search(params) { + return this.client.search(params); + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} params + * @return {Promise} + */ + public async create(params) { + return this.client.create(params); + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} params + * @return {Promise} + */ + public async update(params) { + return this.client.update(params); + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} params + * @return {Promise} + */ + public async delete(params) { + return this.client.delete(params); + } + + /** + * Perform an "UPDATE" operation. + * @param {Object} params + * @return {Promise} + */ + public async exists(params) { + return this.client.exists(params); + } +} + +export const client = new Client(); diff --git a/packages/core-elasticsearch/src/services/storage.ts b/packages/core-elasticsearch/src/services/storage.ts new file mode 100644 index 0000000000..41b7ce195e --- /dev/null +++ b/packages/core-elasticsearch/src/services/storage.ts @@ -0,0 +1,100 @@ +import { ensureFileSync, existsSync, readFileSync, writeFileSync } from "fs-extra"; +import get from "lodash/get"; + +class Storage { + private base: string; + + /** + * Create a new storage instance. + * @return {void} + */ + constructor() { + this.base = `${process.env.CORE_PATH_DATA}/plugins/core-elasticsearch`; + } + + /** + * Read & parse the specified file. + * @param {String} file + * @return {Object} + */ + public read(file) { + if (!this.exists(file)) { + return {}; + } + + return JSON.parse(readFileSync(`${this.base}/${file}.json`).toString()); + } + + /** + * Write the specified data to the specified file. + * @param {String} file + * @param {Object} data + * @return {void} + */ + public write(file, data) { + ensureFileSync(`${this.base}/${file}.json`); + + writeFileSync(`${this.base}/${file}.json`, JSON.stringify(data, null, 2)); + } + + /** + * Update the specified data in the specified file. + * @param {String} file + * @param {Object} data + * @return {void} + */ + public update(file, data) { + ensureFileSync(`${this.base}/${file}.json`); + + data = Object.assign(this.read(file), data); + + writeFileSync(`${this.base}/${file}.json`, JSON.stringify(data, null, 2)); + } + + /** + * Update the specified data in the specified file. + * @param {String} file + * @return {void} + */ + public ensure(file) { + if (!this.exists(file)) { + ensureFileSync(`${this.base}/${file}.json`); + + writeFileSync( + `${this.base}/${file}.json`, + JSON.stringify( + { + lastRound: 0, + lastBlock: 0, + lastTransaction: 0, + }, + null, + 2, + ), + ); + } + } + + /** + * Determine if the specified file exists. + * @param {String} file + * @return {Boolean} + */ + public exists(file) { + return existsSync(`${this.base}/${file}.json`); + } + + /** + * Get a value from the specified file for the specified key. + * @param {String} file + * @param {String} key + * @param defaultValue + * @param {*} key + * @return {*} + */ + public get(file, key, defaultValue = null) { + return get(this.read(file), key, defaultValue); + } +} + +export const storage = new Storage(); diff --git a/packages/core-elasticsearch/tsconfig.json b/packages/core-elasticsearch/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-elasticsearch/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-error-tracker-bugsnag/CHANGELOG.md b/packages/core-error-tracker-bugsnag/CHANGELOG.md deleted file mode 100644 index 6842caf9df..0000000000 --- a/packages/core-error-tracker-bugsnag/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.1.0 - 2018-12-03 - -### Added - -- initial release diff --git a/packages/core-error-tracker-bugsnag/LICENSE b/packages/core-error-tracker-bugsnag/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-error-tracker-bugsnag/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-error-tracker-bugsnag/README.md b/packages/core-error-tracker-bugsnag/README.md index 4bac94326b..fda421356b 100644 --- a/packages/core-error-tracker-bugsnag/README.md +++ b/packages/core-error-tracker-bugsnag/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-error-tracker-bugsnag/lib/defaults.js b/packages/core-error-tracker-bugsnag/lib/defaults.js deleted file mode 100644 index 712d000cc7..0000000000 --- a/packages/core-error-tracker-bugsnag/lib/defaults.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - apiKey: process.env.ARK_ERROR_TRACKER_BUGSNAG_API_KEY, - configuration: { - metaData: { - network: process.env.ARK_NETWORK_NAME, - }, - }, -} diff --git a/packages/core-error-tracker-bugsnag/lib/index.js b/packages/core-error-tracker-bugsnag/lib/index.js deleted file mode 100644 index 23c21c4035..0000000000 --- a/packages/core-error-tracker-bugsnag/lib/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const bugsnag = require('bugsnag') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'error-tracker', - async register(container, options) { - bugsnag.register(options.apiKey, options.configuration) - - return bugsnag - }, -} diff --git a/packages/core-error-tracker-bugsnag/package.json b/packages/core-error-tracker-bugsnag/package.json index da1468b57e..9e65072457 100644 --- a/packages/core-error-tracker-bugsnag/package.json +++ b/packages/core-error-tracker-bugsnag/package.json @@ -1,22 +1,37 @@ { - "name": "@arkecosystem/core-error-tracker-bugsnag", - "description": "Bugsnag error tracker integration for Ark Core.", - "version": "0.1.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "lint": "eslint ./ --fix" - }, - "dependencies": { - "bugsnag": "^2.4.3" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-error-tracker-bugsnag", + "description": "Bugsnag error tracker integration for Ark Core.", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@bugsnag/js": "^5.0.2", + "@arkecosystem/core-container": "^2.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-error-tracker-bugsnag/src/defaults.ts b/packages/core-error-tracker-bugsnag/src/defaults.ts new file mode 100644 index 0000000000..ff4721682b --- /dev/null +++ b/packages/core-error-tracker-bugsnag/src/defaults.ts @@ -0,0 +1,6 @@ +export const defaults = { + apiKey: process.env.CORE_ERROR_TRACKER_BUGSNAG_API_KEY, + metaData: { + network: process.env.CORE_NETWORK_NAME, + }, +}; diff --git a/packages/core-error-tracker-bugsnag/src/index.ts b/packages/core-error-tracker-bugsnag/src/index.ts new file mode 100644 index 0000000000..09c6304902 --- /dev/null +++ b/packages/core-error-tracker-bugsnag/src/index.ts @@ -0,0 +1,12 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import bugsnag from "@bugsnag/js"; +import { defaults } from "./defaults"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "error-tracker", + async register(container: Container.IContainer, options) { + return bugsnag(options); + }, +}; diff --git a/packages/core-error-tracker-bugsnag/tsconfig.json b/packages/core-error-tracker-bugsnag/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-error-tracker-bugsnag/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-error-tracker-sentry/CHANGELOG.md b/packages/core-error-tracker-sentry/CHANGELOG.md deleted file mode 100644 index 6842caf9df..0000000000 --- a/packages/core-error-tracker-sentry/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.1.0 - 2018-12-03 - -### Added - -- initial release diff --git a/packages/core-error-tracker-sentry/LICENSE b/packages/core-error-tracker-sentry/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-error-tracker-sentry/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-error-tracker-sentry/README.md b/packages/core-error-tracker-sentry/README.md index c8e8102d8f..ffb13a95e8 100644 --- a/packages/core-error-tracker-sentry/README.md +++ b/packages/core-error-tracker-sentry/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-error-tracker-sentry/lib/defaults.js b/packages/core-error-tracker-sentry/lib/defaults.js deleted file mode 100644 index f981c94507..0000000000 --- a/packages/core-error-tracker-sentry/lib/defaults.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - dsn: process.env.ARK_ERROR_TRACKER_SENTRY_DSN, - debug: true, - attachStacktrace: true, - environment: process.env.ARK_NETWORK_NAME, -} diff --git a/packages/core-error-tracker-sentry/lib/index.js b/packages/core-error-tracker-sentry/lib/index.js deleted file mode 100644 index 3ba27e5084..0000000000 --- a/packages/core-error-tracker-sentry/lib/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const Sentry = require('@sentry/node') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'error-tracker', - async register(container, options) { - Sentry.init(options) - - return Sentry - }, -} diff --git a/packages/core-error-tracker-sentry/package.json b/packages/core-error-tracker-sentry/package.json index 1d46bdae5d..1d53d1924e 100644 --- a/packages/core-error-tracker-sentry/package.json +++ b/packages/core-error-tracker-sentry/package.json @@ -1,22 +1,37 @@ { - "name": "@arkecosystem/core-error-tracker-sentry", - "description": "Sentry error tracker integration for Ark Core.", - "version": "0.1.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@sentry/node": "^4.4.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-error-tracker-sentry", + "description": "Sentry error tracker integration for Ark Core.", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@sentry/node": "^4.4.2", + "@arkecosystem/core-container": "^2.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-error-tracker-sentry/src/defaults.ts b/packages/core-error-tracker-sentry/src/defaults.ts new file mode 100644 index 0000000000..f669937fee --- /dev/null +++ b/packages/core-error-tracker-sentry/src/defaults.ts @@ -0,0 +1,6 @@ +export const defaults = { + dsn: process.env.CORE_ERROR_TRACKER_SENTRY_DSN, + debug: true, + attachStacktrace: true, + environment: process.env.CORE_NETWORK_NAME, +}; diff --git a/packages/core-error-tracker-sentry/src/index.ts b/packages/core-error-tracker-sentry/src/index.ts new file mode 100644 index 0000000000..fd9461fead --- /dev/null +++ b/packages/core-error-tracker-sentry/src/index.ts @@ -0,0 +1,14 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import Sentry from "@sentry/node"; +import { defaults } from "./defaults"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "error-tracker", + async register(container: Container.IContainer, options) { + Sentry.init(options); + + return Sentry; + }, +}; diff --git a/packages/core-error-tracker-sentry/tsconfig.json b/packages/core-error-tracker-sentry/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-error-tracker-sentry/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-event-emitter/CHANGELOG.md b/packages/core-event-emitter/CHANGELOG.md deleted file mode 100644 index fd0a4f14d3..0000000000 --- a/packages/core-event-emitter/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Changed - -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-event-emitter/LICENSE b/packages/core-event-emitter/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-event-emitter/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-event-emitter/README.md b/packages/core-event-emitter/README.md index 1016c52fb0..06f6190ead 100644 --- a/packages/core-event-emitter/README.md +++ b/packages/core-event-emitter/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-event-emitter/__tests__/emitter.test.js b/packages/core-event-emitter/__tests__/emitter.test.js deleted file mode 100755 index 3707cd75da..0000000000 --- a/packages/core-event-emitter/__tests__/emitter.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const EventEmitter = require('eventemitter3') -const emitter = require('../lib/emitter') - -let lastEmit -beforeAll(() => { - emitter.on('fake', data => { - lastEmit = data - }) -}) - -describe('Event Manager', () => { - it('should be an instance', () => { - expect(emitter).toBeInstanceOf(EventEmitter) - }) - - it('should emit the event', () => { - emitter.emit('fake', 'news') - - expect(lastEmit).toBe('news') - }) -}) diff --git a/packages/core-event-emitter/__tests__/emitter.test.ts b/packages/core-event-emitter/__tests__/emitter.test.ts new file mode 100755 index 0000000000..45d997fa12 --- /dev/null +++ b/packages/core-event-emitter/__tests__/emitter.test.ts @@ -0,0 +1,23 @@ +import EventEmitter from "eventemitter3"; +import { plugin } from "../src"; + +const emitter = plugin.register(); + +let lastEmit; +beforeAll(() => { + emitter.on("fake", data => { + lastEmit = data; + }); +}); + +describe("Event Manager", () => { + it("should be an instance", () => { + expect(emitter).toBeInstanceOf(EventEmitter); + }); + + it("should emit the event", () => { + emitter.emit("fake", "news"); + + expect(lastEmit).toBe("news"); + }); +}); diff --git a/packages/core-event-emitter/jest.config.js b/packages/core-event-emitter/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-event-emitter/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-event-emitter/lib/emitter.js b/packages/core-event-emitter/lib/emitter.js deleted file mode 100644 index c95b33e734..0000000000 --- a/packages/core-event-emitter/lib/emitter.js +++ /dev/null @@ -1,15 +0,0 @@ -const EventEmitter = require('eventemitter3') - -class Emitter { - /** - * Create a new event manager instance. - * @return {WebhookManager} - */ - constructor() { - this.emitter = new EventEmitter() - - return this.emitter - } -} - -module.exports = new Emitter() diff --git a/packages/core-event-emitter/lib/index.js b/packages/core-event-emitter/lib/index.js deleted file mode 100644 index f4e1ce95a1..0000000000 --- a/packages/core-event-emitter/lib/index.js +++ /dev/null @@ -1,13 +0,0 @@ -const emitter = require('./emitter') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - alias: 'event-emitter', - async register() { - return emitter - }, -} diff --git a/packages/core-event-emitter/package.json b/packages/core-event-emitter/package.json index 4c3d318027..0071f7f0b5 100644 --- a/packages/core-event-emitter/package.json +++ b/packages/core-event-emitter/package.json @@ -1,27 +1,41 @@ { - "name": "@arkecosystem/core-event-emitter", - "description": "Event Manager for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "eventemitter3": "^3.1.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-event-emitter", + "description": "Event Manager for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "eventemitter3": "^3.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-event-emitter/src/index.ts b/packages/core-event-emitter/src/index.ts new file mode 100644 index 0000000000..ec20d8dd61 --- /dev/null +++ b/packages/core-event-emitter/src/index.ts @@ -0,0 +1,9 @@ +import EventEmitter from "eventemitter3"; + +export const plugin = { + pkg: require("../package.json"), + alias: "event-emitter", + register() { + return new EventEmitter(); + }, +}; diff --git a/packages/core-event-emitter/tsconfig.json b/packages/core-event-emitter/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-event-emitter/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-forger/CHANGELOG.md b/packages/core-forger/CHANGELOG.md deleted file mode 100644 index 137e780335..0000000000 --- a/packages/core-forger/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Ping blockchain (relay node) to wake-up one slot before forging -- PBFT call to p2p layer to assess network state - -### Changed - -- Split monitor and client to seperate HTTP logic -- Read BIP38 from `.env` -- Read Passphrase from `.env` -- Dropped node.js 9 as minimum requirement in favour of node.js 10 -- Improved logging for transaction errors - -### Fixed - -- Properly exit if no delegates are configured -- Avoid duplicate secrets - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-forger/LICENSE b/packages/core-forger/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-forger/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-forger/README.md b/packages/core-forger/README.md index 6c3ac1f41c..e6031668bf 100644 --- a/packages/core-forger/README.md +++ b/packages/core-forger/README.md @@ -14,10 +14,12 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Erwann Gentric](https://github.com/air1one) +- [François-Xavier Thoorens](https://github.com/fix) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-forger/__tests__/__fixtures__/block.js b/packages/core-forger/__tests__/__fixtures__/block.js deleted file mode 100644 index eebf8e3554..0000000000 --- a/packages/core-forger/__tests__/__fixtures__/block.js +++ /dev/null @@ -1,23 +0,0 @@ -const { Block } = require('@arkecosystem/crypto').models - -module.exports = new Block({ - id: '4398082439836560423', - version: 0, - timestamp: 35751416, - height: 3342573, - previousBlock: '14909996519459393858', - numberOfTransactions: 0, - totalAmount: 0, - totalFee: 0, - reward: 200000000, - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03806036bc1bb470144184b10f815431c580ae2b806d5fd0ba2118dca823c5c4a6', - generatorId: 'DMrWy7PddjmDiJFm4bToMj4MhDBa9Wm9vN', - blockSignature: - '3045022100d0ad616575b1039b89ae22bb8efbce80dd14f52d193ef7a1d0a76fab0253aa4f02206a347bb5d4dc372e5a7ad3f16ae44409d9190fbd8138e9b4e99f83ca3236f91d', - confirmations: 1, - totalForged: '200000000', -}) diff --git a/packages/core-forger/__tests__/__fixtures__/block.ts b/packages/core-forger/__tests__/__fixtures__/block.ts new file mode 100644 index 0000000000..58780a3a8b --- /dev/null +++ b/packages/core-forger/__tests__/__fixtures__/block.ts @@ -0,0 +1,19 @@ +import { models } from "@arkecosystem/crypto"; + +export const sampleBlock = new models.Block({ + id: "4398082439836560423", + version: 0, + timestamp: 35751416, + height: 3342573, + previousBlock: "14909996519459393858", + numberOfTransactions: 0, + totalAmount: 0, + totalFee: 0, + reward: 200000000, + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03806036bc1bb470144184b10f815431c580ae2b806d5fd0ba2118dca823c5c4a6", + blockSignature: + // tslint:disable-next-line:max-line-length + "3045022100d0ad616575b1039b89ae22bb8efbce80dd14f52d193ef7a1d0a76fab0253aa4f02206a347bb5d4dc372e5a7ad3f16ae44409d9190fbd8138e9b4e99f83ca3236f91d", +}); diff --git a/packages/core-forger/__tests__/__fixtures__/delegate.js b/packages/core-forger/__tests__/__fixtures__/delegate.js deleted file mode 100644 index 1f4fe1d79a..0000000000 --- a/packages/core-forger/__tests__/__fixtures__/delegate.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - username: 'arkxdev', - publicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', -} diff --git a/packages/core-forger/__tests__/__fixtures__/delegate.ts b/packages/core-forger/__tests__/__fixtures__/delegate.ts new file mode 100644 index 0000000000..ab37cd7634 --- /dev/null +++ b/packages/core-forger/__tests__/__fixtures__/delegate.ts @@ -0,0 +1,4 @@ +export const delegate = { + username: "arkxdev", + publicKey: "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", +}; diff --git a/packages/core-forger/__tests__/__fixtures__/transaction.js b/packages/core-forger/__tests__/__fixtures__/transaction.js deleted file mode 100644 index 84a20a6472..0000000000 --- a/packages/core-forger/__tests__/__fixtures__/transaction.js +++ /dev/null @@ -1,16 +0,0 @@ -const { Transaction } = require('@arkecosystem/crypto').models - -module.exports = new Transaction({ - type: 0, - amount: 245098000000000, - fee: 0, - recipientId: 'AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri', - timestamp: 0, - asset: {}, - senderPublicKey: - '035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788', - signature: - '304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d', - id: 'db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd', - senderId: 'APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn', -}) diff --git a/packages/core-forger/__tests__/__fixtures__/transaction.ts b/packages/core-forger/__tests__/__fixtures__/transaction.ts new file mode 100644 index 0000000000..556098b367 --- /dev/null +++ b/packages/core-forger/__tests__/__fixtures__/transaction.ts @@ -0,0 +1,15 @@ +import { models } from "@arkecosystem/crypto"; + +export const sampleTransaction = new models.Transaction({ + type: 0, + amount: 245098000000000, + fee: 0, + recipientId: "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + timestamp: 0, + asset: {}, + senderPublicKey: "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + signature: + // tslint:disable-next-line:max-line-length + "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", + id: "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", +}); diff --git a/packages/core-forger/__tests__/__support__/setup.js b/packages/core-forger/__tests__/__support__/setup.js deleted file mode 100644 index 48e8a441d4..0000000000 --- a/packages/core-forger/__tests__/__support__/setup.js +++ /dev/null @@ -1,12 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -exports.setUp = async () => { - await appHelper.setUp({ - exit: '@arkecosystem/core-logger-winston', - }) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-forger/__tests__/__support__/setup.ts b/packages/core-forger/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..37ef936c75 --- /dev/null +++ b/packages/core-forger/__tests__/__support__/setup.ts @@ -0,0 +1,12 @@ +import { app } from "@arkecosystem/core-container"; +import { setUpContainer } from "@arkecosystem/core-test-utils/src/helpers/container"; + +export const setUp = async () => { + return setUpContainer({ + exit: "@arkecosystem/core-p2p", + }); +}; + +export const tearDown = async () => { + return app.tearDown(); +}; diff --git a/packages/core-forger/__tests__/client.test.js b/packages/core-forger/__tests__/client.test.js deleted file mode 100644 index 4edc22b5f6..0000000000 --- a/packages/core-forger/__tests__/client.test.js +++ /dev/null @@ -1,190 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const app = require('./__support__/setup') -const block = require('./__fixtures__/block') - -const mockAxios = new MockAdapter(axios) - -jest.setTimeout(30000) - -const host = `http://127.0.0.1:${process.env.ARK_P2P_PORT || 4000}` - -let Client -let client - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() - mockAxios.restore() -}) - -beforeEach(() => { - Client = require('../lib/client') - client = new Client(host) - - mockAxios.onGet(`${host}/peer/status`).reply(200) -}) - -afterEach(() => { - mockAxios.reset() -}) - -describe('Client', () => { - it('should be an object', () => { - expect(client).toBeObject() - }) - - describe('constructor', () => { - it('accepts 1 or more hosts as parameter', () => { - expect(new Client(host).hosts).toEqual([host]) - - const hosts = [host, 'http://localhost:4000'] - - expect(new Client(hosts).hosts).toEqual(hosts) - }) - }) - - describe('broadcast', () => { - it('should be a function', () => { - expect(client.broadcast).toBeFunction() - }) - - describe('when the host is available', () => { - it('should be truthy if broadcasts', async () => { - mockAxios.onPost(`${host}/internal/blocks`).reply(c => { - expect(JSON.parse(c.data).block).toMatchObject( - expect.objectContaining({ - id: block.data.id, - }), - ) - return [200, true] - }) - - await client.__chooseHost() - - const wasBroadcasted = await client.broadcast(block.toJson()) - expect(wasBroadcasted).toBeTruthy() - }) - }) - }) - - describe('getRound', () => { - it('should be a function', () => { - expect(client.getRound).toBeFunction() - }) - - describe('when the host is available', () => { - it('should be ok', async () => { - const expectedResponse = { foo: 'bar' } - mockAxios - .onGet(`${host}/internal/rounds/current`) - .reply(200, { data: expectedResponse }) - - const response = await client.getRound() - - expect(response).toEqual(expectedResponse) - }) - }) - }) - - describe('getTransactions', () => { - it('should be a function', () => { - expect(client.getTransactions).toBeFunction() - }) - - describe('when the host is available', () => { - it('should be ok', async () => { - const expectedResponse = { foo: 'bar' } - mockAxios - .onGet(`${host}/internal/transactions/forging`) - .reply(200, { data: expectedResponse }) - - await client.__chooseHost() - const response = await client.getTransactions() - - expect(response).toEqual(expectedResponse) - }) - }) - }) - - describe('getNetworkState', () => { - it('should be a function', () => { - expect(client.getNetworkState).toBeFunction() - }) - - describe('when the host is available', () => { - it('should be ok', async () => { - const expectedResponse = { foo: 'bar' } - mockAxios - .onGet(`${host}/internal/network/state`) - .reply(200, { data: expectedResponse }) - - await client.__chooseHost() - const response = await client.getNetworkState() - - expect(response).toEqual(expectedResponse) - }) - }) - }) - - describe('syncCheck', () => { - it('should be a function', () => { - expect(client.syncCheck).toBeFunction() - }) - - it('should induce network sync', async () => { - jest.spyOn(axios, 'get') - mockAxios.onGet(`${host}/internal/blockchain/sync`).reply(200) - - await client.syncCheck() - - expect(axios.get).toHaveBeenCalledWith( - `${host}/internal/blockchain/sync`, - expect.any(Object), - ) - }) - }) - - describe('getUsernames', () => { - it('should be a function', () => { - expect(client.getUsernames).toBeFunction() - }) - - it('should fetch usernames', async () => { - jest.spyOn(axios, 'get') - const expectedResponse = { foo: 'bar' } - mockAxios - .onGet(`${host}/internal/utils/usernames`) - .reply(200, { data: expectedResponse }) - - const response = await client.getUsernames() - - expect(response).toEqual(expectedResponse) - }) - }) - - describe('emitEvent', () => { - it('should be a function', () => { - expect(client.emitEvent).toBeFunction() - }) - it('should emit events', async () => { - jest.spyOn(axios, 'post') - mockAxios.onPost(`${host}/internal/utils/events`).reply(c => { - expect(JSON.parse(c.data)).toMatchObject({ event: 'foo', body: 'bar' }) - return [200] - }) - - await client.__chooseHost() - await client.emitEvent('foo', 'bar') - - expect(axios.post).toHaveBeenCalledWith( - `${host}/internal/utils/events`, - { event: 'foo', body: 'bar' }, - expect.any(Object), - ) - }) - }) -}) diff --git a/packages/core-forger/__tests__/client.test.ts b/packages/core-forger/__tests__/client.test.ts new file mode 100644 index 0000000000..f31c2c580a --- /dev/null +++ b/packages/core-forger/__tests__/client.test.ts @@ -0,0 +1,150 @@ +import "jest-extended"; + +import { NetworkState, NetworkStateStatus } from "@arkecosystem/core-p2p"; +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { Client } from "../src/client"; +import { sampleBlock } from "./__fixtures__/block"; +import { setUp, tearDown } from "./__support__/setup"; + +const mockAxios = new MockAdapter(axios); + +jest.setTimeout(30000); + +const host = `http://127.0.0.1:4000`; + +let client: Client; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); + mockAxios.restore(); +}); + +beforeEach(() => { + client = new Client(host); + + mockAxios.onGet(`${host}/peer/status`).reply(200); +}); + +afterEach(() => { + mockAxios.reset(); +}); + +describe("Client", () => { + describe("constructor", () => { + it("accepts 1 or more hosts as parameter", () => { + expect(new Client(host).hosts).toEqual([host]); + + const hosts = [host, "http://localhost:4000"]; + + expect(new Client(hosts).hosts).toEqual(hosts); + }); + }); + + describe("broadcast", () => { + describe("when the host is available", () => { + it("should be truthy if broadcasts", async () => { + mockAxios.onPost(`${host}/internal/blocks`).reply(c => { + expect(JSON.parse(c.data).block).toMatchObject( + expect.objectContaining({ + id: sampleBlock.data.id, + }), + ); + return [200, true]; + }); + + await client.__chooseHost(); + + const wasBroadcasted = await client.broadcast(sampleBlock.toJson()); + expect(wasBroadcasted).toBeTruthy(); + }); + }); + }); + + describe("getRound", () => { + describe("when the host is available", () => { + it("should be ok", async () => { + const expectedResponse = { foo: "bar" }; + mockAxios.onGet(`${host}/internal/rounds/current`).reply(200, { data: expectedResponse }); + + const response = await client.getRound(); + + expect(response).toEqual(expectedResponse); + }); + }); + }); + + describe("getTransactions", () => { + describe("when the host is available", () => { + it("should be ok", async () => { + const expectedResponse = { foo: "bar" }; + mockAxios.onGet(`${host}/internal/transactions/forging`).reply(200, { data: expectedResponse }); + + await client.__chooseHost(); + const response = await client.getTransactions(); + + expect(response).toEqual(expectedResponse); + }); + }); + }); + + describe("getNetworkState", () => { + describe("when the host is available", () => { + it("should be ok", async () => { + const expectedResponse = new NetworkState(NetworkStateStatus.Test); + mockAxios.onGet(`${host}/internal/network/state`).reply(200, { data: expectedResponse }); + + await client.__chooseHost(); + const response = await client.getNetworkState(); + + expect(response).toEqual(expectedResponse); + }); + }); + }); + + describe("syncCheck", () => { + it("should induce network sync", async () => { + jest.spyOn(axios, "get"); + mockAxios.onGet(`${host}/internal/blockchain/sync`).reply(200); + + await client.syncCheck(); + + expect(axios.get).toHaveBeenCalledWith(`${host}/internal/blockchain/sync`, expect.any(Object)); + }); + }); + + describe("getUsernames", () => { + it("should fetch usernames", async () => { + jest.spyOn(axios, "get"); + const expectedResponse = { foo: "bar" }; + mockAxios.onGet(`${host}/internal/utils/usernames`).reply(200, { data: expectedResponse }); + + const response = await client.getUsernames(); + + expect(response).toEqual(expectedResponse); + }); + }); + + describe("emitEvent", () => { + it("should emit events", async () => { + jest.spyOn(axios, "post"); + mockAxios.onPost(`${host}/internal/utils/events`).reply(c => { + expect(JSON.parse(c.data)).toMatchObject({ event: "foo", body: "bar" }); + return [200]; + }); + + await client.__chooseHost(); + await client.emitEvent("foo", "bar"); + + expect(axios.post).toHaveBeenCalledWith( + `${host}/internal/utils/events`, + { event: "foo", body: "bar" }, + expect.any(Object), + ); + }); + }); +}); diff --git a/packages/core-forger/__tests__/manager.test.js b/packages/core-forger/__tests__/manager.test.js deleted file mode 100644 index daa8a69d50..0000000000 --- a/packages/core-forger/__tests__/manager.test.js +++ /dev/null @@ -1,266 +0,0 @@ -const { Delegate, Transaction } = require('@arkecosystem/crypto').models -const app = require('./__support__/setup') -const defaultConfig = require('../lib/defaults') -const delegate = require('./__fixtures__/delegate') -const sampleTransaction = require('./__fixtures__/transaction') -const sampleBlock = require('./__fixtures__/block') - -jest.setTimeout(30000) -jest.mock('../lib/client') - -let forgeManager - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() - jest.restoreAllMocks() -}) - -beforeEach(() => { - const ForgeManager = require('../lib/manager') - defaultConfig.hosts = [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4000}`] - forgeManager = new ForgeManager(defaultConfig) -}) - -describe('Forger Manager', () => { - describe('loadDelegates', () => { - it('should be a function', () => { - expect(forgeManager.loadDelegates).toBeFunction() - }) - - it('should be ok with configured delegates', async () => { - const secret = 'a secret' - forgeManager.secrets = [secret] - forgeManager.client.getUsernames.mockReturnValue([]) - - const delegates = await forgeManager.loadDelegates() - - expect(delegates).toBeArray() - delegates.forEach(value => expect(value).toBeInstanceOf(Delegate)) - expect(forgeManager.client.getUsernames).toHaveBeenCalled() - }) - }) - - describe('startForging', () => { - it('should be a function', () => { - expect(forgeManager.startForging).toBeFunction() - }) - }) - - describe('__forgeNewBlock', () => { - it('should be a function', () => { - expect(forgeManager.__forgeNewBlock).toBeFunction() - }) - it('should forge a block', async () => { - forgeManager.client.getTransactions.mockReturnValue({ - transactions: [ - Transaction.serialize(sampleTransaction).toString('hex'), - ], - }) - forgeManager.usernames = [] - const del = new Delegate('a secret', 100) - const round = { - lastBlock: { id: sampleBlock.data.id, height: sampleBlock.data.height }, - timestamp: 1, - reward: 2, - } - - await forgeManager.__forgeNewBlock(del, round) - - expect(forgeManager.client.broadcast).toHaveBeenCalledWith( - expect.objectContaining({ - height: round.lastBlock.height + 1, - reward: round.reward, - }), - ) - expect(forgeManager.client.emitEvent).toHaveBeenCalledWith( - 'block.forged', - expect.any(Object), - ) - expect(forgeManager.client.emitEvent).toHaveBeenCalledWith( - 'transaction.forged', - expect.any(Object), - ) - }) - }) - - describe('__monitor', () => { - it('should be a function', () => { - expect(forgeManager.__monitor).toBeFunction() - }) - it('should emit failed event if error while monitoring', async () => { - forgeManager.client.getUsernames.mockRejectedValue( - new Error('oh bollocks'), - ) - - setTimeout(() => forgeManager.stop(), 1000) - await forgeManager.__monitor() - - expect(forgeManager.client.emitEvent).toHaveBeenCalledWith( - 'forger.failed', - 'oh bollocks', - ) - }) - }) - - describe('__getTransactionsForForging', () => { - it('should be a function', () => { - expect(forgeManager.__getTransactionsForForging).toBeFunction() - }) - it('should return zero transactions if none to forge', async () => { - forgeManager.client.getTransactions.mockReturnValue({}) - - const transactions = await forgeManager.__getTransactionsForForging() - - expect(transactions).toHaveLength(0) - expect(forgeManager.client.getTransactions).toHaveBeenCalled() - }) - it('should return deserialized transactions', async () => { - forgeManager.client.getTransactions.mockReturnValue({ - transactions: [ - Transaction.serialize(sampleTransaction).toString('hex'), - ], - }) - - const transactions = await forgeManager.__getTransactionsForForging() - - expect(transactions).toHaveLength(1) - expect(forgeManager.client.getTransactions).toHaveBeenCalled() - expect(transactions[0]).toBeInstanceOf(Transaction) - expect(transactions[0].data.recipientId).toEqual( - sampleTransaction.data.recipientId, - ) - expect(transactions[0].data.senderPublicKey).toEqual( - sampleTransaction.data.senderPublicKey, - ) - }) - }) - - describe('__isDelegateActivated', () => { - it('should be a function', () => { - expect(forgeManager.__isDelegateActivated).toBeFunction() - }) - - it('should be ok', async () => { - forgeManager.delegates = [ - { - username: 'arkxdev', - publicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - }, - ] - - const forger = await forgeManager.__isDelegateActivated( - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - ) - - expect(forger).toBeObject() - expect(forger.username).toBe('arkxdev') - expect(forger.publicKey).toBe( - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - ) - }) - }) - - describe('__analyseNetworkState', () => { - it('should be a function', () => { - expect(forgeManager.__analyseNetworkState).toBeFunction() - }) - - it('should be TRUE when quorum > 0.66', async () => { - const networkState = { - quorum: 0.9, - nodeHeight: 100, - lastBlockId: '1233443', - overHeightBlockHeader: {}, - minimumNetworkReach: true, - coldStart: false, - } - const canForge = await forgeManager.__analyseNetworkState( - networkState, - delegate, - ) - - expect(canForge).toBeTrue() - }) - - it('should be FALSE when quorum < 0.66', async () => { - const networkState = { - quorum: 0.65, - nodeHeight: 100, - lastBlockId: '1233443', - overHeightBlockHeader: {}, - minimumNetworkReach: true, - coldStart: false, - } - const canForge = await forgeManager.__analyseNetworkState( - networkState, - delegate, - ) - - expect(canForge).toBeFalse() - }) - - it('should be FALSE when coldStart is active', async () => { - const networkState = { - quorum: 1, - nodeHeight: 100, - lastBlockId: '1233443', - overHeightBlockHeader: {}, - minimumNetworkReach: true, - coldStart: true, - } - const canForge = await forgeManager.__analyseNetworkState( - networkState, - delegate, - ) - - expect(canForge).toBeFalse() - }) - - it('should be FALSE when minimumNetworkReach is not sufficient', async () => { - const networkState = { - quorum: 1, - nodeHeight: 100, - lastBlockId: '1233443', - overHeightBlockHeader: {}, - minimumNetworkReach: false, - coldStart: false, - } - const canForge = await forgeManager.__analyseNetworkState( - networkState, - delegate, - ) - - expect(canForge).toBeFalse() - }) - - it('should be FAIL and detect possible double forging', async () => { - forgeManager.usernames = [] - const overHeightBlockHeader = { - id: '2816806946235018296', - height: 2360065, - generatorPublicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - } - - const networkState = { - quorum: 1, - nodeHeight: 100, - lastBlockId: '1233443', - overHeightBlockHeader, - minimumNetworkReach: 10, - coldStart: false, - } - const canForge = await forgeManager.__analyseNetworkState( - networkState, - delegate, - ) - - expect(canForge).toBeFalse() - }) - }) -}) diff --git a/packages/core-forger/__tests__/manager.test.ts b/packages/core-forger/__tests__/manager.test.ts new file mode 100644 index 0000000000..8b5eb773d2 --- /dev/null +++ b/packages/core-forger/__tests__/manager.test.ts @@ -0,0 +1,204 @@ +import { generators } from "@arkecosystem/core-test-utils"; +import "jest-extended"; + +import { NetworkState, NetworkStateStatus } from "@arkecosystem/core-p2p"; +import { Bignum, models } from "@arkecosystem/crypto"; +import { testnet } from "../../crypto/src/networks"; +import { defaults } from "../src/defaults"; +import { ForgerManager } from "../src/manager"; +import { sampleBlock } from "./__fixtures__/block"; +import { delegate } from "./__fixtures__/delegate"; +import { sampleTransaction } from "./__fixtures__/transaction"; +import { setUp, tearDown } from "./__support__/setup"; + +const { Delegate, Transaction } = models; +const { generateTransfers } = generators; + +jest.setTimeout(30000); +jest.mock("../src/client"); + +let forgeManager; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); + jest.restoreAllMocks(); +}); + +beforeEach(() => { + defaults.hosts = [`http://127.0.0.1:4000`]; + forgeManager = new ForgerManager(defaults); +}); + +describe("Forger Manager", () => { + describe("loadDelegates", () => { + it("should be ok with configured delegates", async () => { + const secret = "a secret"; + forgeManager.secrets = [secret]; + forgeManager.client.getUsernames.mockReturnValue([]); + + const delegates = await forgeManager.loadDelegates(); + + expect(delegates).toBeArray(); + delegates.forEach(value => expect(value).toBeInstanceOf(Delegate)); + expect(forgeManager.client.getUsernames).toHaveBeenCalled(); + }); + }); + + describe("__forgeNewBlock", () => { + it("should forge a block", async () => { + // NOTE: make sure we have valid transactions from an existing wallet + const transactions = generateTransfers( + "testnet", + "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", + ); + + forgeManager.client.getTransactions.mockReturnValue({ + transactions: transactions.map(tx => tx.serialized), + }); + + forgeManager.usernames = []; + + const del = new Delegate("a secret", testnet.network); + const round = { + lastBlock: { id: sampleBlock.data.id, height: sampleBlock.data.height }, + timestamp: 1, + reward: 2 * 1e8, + }; + + await forgeManager.__forgeNewBlock(del, round); + + expect(forgeManager.client.broadcast).toHaveBeenCalledWith( + expect.objectContaining({ + height: round.lastBlock.height + 1, + reward: round.reward, + }), + ); + expect(forgeManager.client.emitEvent).toHaveBeenCalledWith("block.forged", expect.any(Object)); + expect(forgeManager.client.emitEvent).toHaveBeenCalledWith("transaction.forged", expect.any(Object)); + }); + }); + + describe("__monitor", () => { + it("should emit failed event if error while monitoring", async () => { + forgeManager.client.getUsernames.mockRejectedValue(new Error("oh bollocks")); + + setTimeout(() => forgeManager.stop(), 1000); + await forgeManager.__monitor(); + + expect(forgeManager.client.emitEvent).toHaveBeenCalledWith("forger.failed", "oh bollocks"); + }); + }); + + describe("__getTransactionsForForging", () => { + it("should return zero transactions if none to forge", async () => { + forgeManager.client.getTransactions.mockReturnValue({}); + + const transactions = await forgeManager.__getTransactionsForForging(); + + expect(transactions).toHaveLength(0); + expect(forgeManager.client.getTransactions).toHaveBeenCalled(); + }); + it("should return deserialized transactions", async () => { + forgeManager.client.getTransactions.mockReturnValue({ + transactions: [Transaction.serialize(sampleTransaction).toString("hex")], + }); + + const transactions = await forgeManager.__getTransactionsForForging(); + + expect(transactions).toHaveLength(1); + expect(forgeManager.client.getTransactions).toHaveBeenCalled(); + expect(transactions[0]).toBeInstanceOf(Transaction); + expect(transactions[0].data.recipientId).toEqual(sampleTransaction.data.recipientId); + expect(transactions[0].data.senderPublicKey).toEqual(sampleTransaction.data.senderPublicKey); + }); + }); + + describe("__isDelegateActivated", () => { + it("should be ok", async () => { + forgeManager.delegates = [ + { + username: "arkxdev", + publicKey: "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", + }, + ]; + + const forger = await forgeManager.__isDelegateActivated( + "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", + ); + + expect(forger).toBeObject(); + expect(forger.username).toBe("arkxdev"); + expect(forger.publicKey).toBe("0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0"); + }); + }); + + describe("__parseNetworkState", () => { + it("should be TRUE when quorum > 0.66", async () => { + const networkState = new NetworkState(NetworkStateStatus.Default); + Object.assign(networkState, { getQuorum: () => 0.9, nodeHeight: 100, lastBlockId: "1233443" }); + + const canForge = await forgeManager.__parseNetworkState(networkState, delegate); + + expect(canForge).toBeTrue(); + }); + + it("should be FALSE when unknown", async () => { + const networkState = new NetworkState(NetworkStateStatus.Unknown); + Object.assign(networkState, { getQuorum: () => 1, nodeHeight: 100, lastBlockId: "1233443" }); + + const canForge = await forgeManager.__parseNetworkState(networkState, delegate); + + expect(canForge).toBeFalse(); + }); + + it("should be FALSE when quorum < 0.66", async () => { + const networkState = new NetworkState(NetworkStateStatus.Default); + Object.assign(networkState, { getQuorum: () => 0.65, nodeHeight: 100, lastBlockId: "1233443" }); + + const canForge = await forgeManager.__parseNetworkState(networkState, delegate); + + expect(canForge).toBeFalse(); + }); + + it("should be FALSE when coldStart is active", async () => { + const networkState = new NetworkState(NetworkStateStatus.ColdStart); + const canForge = await forgeManager.__parseNetworkState(networkState, delegate); + + expect(canForge).toBeFalse(); + }); + + it("should be FALSE when minimumNetworkReach is not sufficient", async () => { + const networkState = new NetworkState(NetworkStateStatus.BelowMinimumPeers); + const canForge = await forgeManager.__parseNetworkState(networkState, delegate); + + expect(canForge).toBeFalse(); + }); + + it("should be FAIL and detect possible double forging", async () => { + forgeManager.usernames = []; + + const networkState = new NetworkState(NetworkStateStatus.Default); + Object.assign(networkState, { + getQuorum: () => 1, + nodeHeight: 100, + lastBlockId: "1233443", + quorumDetails: { + peersOverHeightBlockHeaders: { + "2816806946235018296": { + id: "2816806946235018296", + height: 2360065, + generatorPublicKey: "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", + }, + }, + }, + }); + + const canForge = await forgeManager.__parseNetworkState(networkState, delegate); + expect(canForge).toBeFalse(); + }); + }); +}); diff --git a/packages/core-forger/jest.config.js b/packages/core-forger/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-forger/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-forger/lib/client.js b/packages/core-forger/lib/client.js deleted file mode 100644 index 6bb85ffd2b..0000000000 --- a/packages/core-forger/lib/client.js +++ /dev/null @@ -1,186 +0,0 @@ -/* eslint max-len: "off" */ - -const axios = require('axios') -const sample = require('lodash/sample') -const delay = require('delay') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const config = app.resolvePlugin('config') - -module.exports = class Client { - /** - * Create a new client instance. - * @param {(Array|String)} hosts - Host or Array of hosts - */ - constructor(hosts) { - this.hosts = Array.isArray(hosts) ? hosts : [hosts] - - this.headers = { - version: app.getVersion(), - port: app.resolveOptions('p2p').port, - nethash: config.network.nethash, - 'x-auth': 'forger', - 'Content-Type': 'application/json', - } - } - - /** - * Send the given block to the relay. - * @param {(Block|Object)} block - * @return {Object} - */ - async broadcast(block) { - logger.debug( - `Broadcasting forged block id:${ - block.id - } at height:${block.height.toLocaleString()} with ${ - block.numberOfTransactions - } transactions to ${this.host} :package:`, - ) - - return this.__post(`${this.host}/internal/blocks`, { block }) - } - - /** - * Sends the WAKEUP signal to the to relay hosts to check if synced and sync if necesarry - */ - async syncCheck() { - await this.__chooseHost() - - logger.debug(`Sending wake-up check to relay node ${this.host}`) - - try { - await this.__get(`${this.host}/internal/blockchain/sync`) - } catch (error) { - logger.error(`Could not sync check: ${error.message}`) - } - } - - /** - * Get the current round. - * @return {Object} - */ - async getRound() { - try { - await this.__chooseHost() - - const response = await this.__get(`${this.host}/internal/rounds/current`) - - return response.data.data - } catch (e) { - return {} - } - } - - /** - * Get the current network quorum. - * @return {Object} - */ - async getNetworkState() { - try { - const response = await this.__get(`${this.host}/internal/network/state`) - - return response.data.data - } catch (e) { - return {} - } - } - - /** - * Get all transactions that are ready to be forged. - * @return {Object} - */ - async getTransactions() { - try { - const response = await this.__get( - `${this.host}/internal/transactions/forging`, - ) - - return response.data.data - } catch (e) { - return {} - } - } - - /** - * Get a list of all active delegate usernames. - * @return {Object} - */ - async getUsernames(wait = 0) { - await this.__chooseHost(wait) - - try { - const response = await this.__get(`${this.host}/internal/utils/usernames`) - - return response.data.data - } catch (e) { - return {} - } - } - - /** - * Emit the given event and payload to the local host. - * @param {String} event - * @param {Object} body - * @return {Object} - */ - async emitEvent(event, body) { - // NOTE: Events need to be emitted to the localhost. If you need to trigger - // actions on a remote host based on events you should be using webhooks - // that get triggered by the events you wish to react to. - - const allowedHosts = [ - 'localhost', - '127.0.0.1', - '::ffff:127.0.0.1', - '192.168.*', - ] - - const host = this.hosts.find(item => - allowedHosts.some(allowedHost => item.includes(allowedHost)), - ) - - if (!host) { - return logger.error('Was unable to find any local hosts.') - } - - try { - await this.__post(`${host}/internal/utils/events`, { event, body }) - } catch (error) { - logger.error(`Failed to emit "${event}" to "${host}"`) - } - } - - /** - * Chose a responsive host. - * @return {void} - */ - async __chooseHost(wait = 0) { - const host = sample(this.hosts) - - try { - await this.__get(`${host}/peer/status`) - - this.host = host - } catch (error) { - logger.debug( - `${host} didn't respond to the forger. Trying another host :sparkler:`, - ) - - if (wait > 0) { - await delay(wait) - } - - await this.__chooseHost(wait) - } - } - - async __get(url) { - return axios.get(url, { headers: this.headers, timeout: 2000 }) - } - - async __post(url, body) { - return axios.post(url, body, { headers: this.headers, timeout: 2000 }) - } -} diff --git a/packages/core-forger/lib/defaults.js b/packages/core-forger/lib/defaults.js deleted file mode 100644 index 43700e8a41..0000000000 --- a/packages/core-forger/lib/defaults.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4002}`], -} diff --git a/packages/core-forger/lib/index.js b/packages/core-forger/lib/index.js deleted file mode 100644 index c4a199a9ea..0000000000 --- a/packages/core-forger/lib/index.js +++ /dev/null @@ -1,53 +0,0 @@ -const pluralize = require('pluralize') -const ForgerManager = require('./manager') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'forger', - async register(container, options) { - const forgerManager = new ForgerManager(options) - const forgers = await forgerManager.loadDelegates( - options.bip38, - options.password, - ) - - if (!forgers) { - container - .resolvePlugin('logger') - .info('Forger is disabled :grey_exclamation:') - return - } - - // Don't keep bip38 password in memory - delete process.env.ARK_FORGER_PASSWORD - delete options.password - - container - .resolvePlugin('logger') - .info( - `Forger Manager started with ${pluralize( - 'forger', - forgers.length, - true, - )}`, - ) - - forgerManager.startForging() - - return forgerManager - }, - async deregister(container, options) { - const forger = container.resolvePlugin('forger') - - if (forger) { - container.resolvePlugin('logger').info('Stopping Forger Manager') - - return forger.stop() - } - }, -} diff --git a/packages/core-forger/lib/manager.js b/packages/core-forger/lib/manager.js deleted file mode 100644 index 63ed5e3305..0000000000 --- a/packages/core-forger/lib/manager.js +++ /dev/null @@ -1,320 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const delay = require('delay') - -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const config = app.resolvePlugin('config') - -const { slots } = require('@arkecosystem/crypto') -const { Delegate, Transaction } = require('@arkecosystem/crypto').models - -const isEmpty = require('lodash/isEmpty') -const uniq = require('lodash/uniq') -const pluralize = require('pluralize') - -const Client = require('./client') - -module.exports = class ForgerManager { - /** - * Create a new forger manager instance. - * @param {Object} options - */ - constructor(options) { - this.secrets = config.delegates ? config.delegates.secrets : null - this.network = config.network - this.client = new Client(options.hosts) - } - - /** - * Load all delegates that forge. - * @param {String} bip38 - * @param {String} password - * @return {Array} - */ - async loadDelegates(bip38, password) { - if ( - !bip38 && - (!this.secrets || !this.secrets.length || !Array.isArray(this.secrets)) - ) { - logger.warn( - 'No delegate found! Please check your "delegates.json" file and try again.', - ) - return - } - - this.secrets = uniq(this.secrets.map(secret => secret.trim())) - this.delegates = this.secrets.map( - passphrase => new Delegate(passphrase, this.network, password), - ) - - if (bip38) { - logger.info('BIP38 Delegate loaded') - - this.delegates.push(new Delegate(bip38, this.network, password)) - } - - await this.__loadUsernames(2000) - - const delegates = this.delegates.map( - delegate => - `${this.usernames[delegate.publicKey]} (${delegate.publicKey})`, - ) - - logger.debug( - `Loaded ${pluralize( - 'delegate', - delegates.length, - true, - )}: ${delegates.join(', ')}`, - ) - - return this.delegates - } - - /** - * Start forging on the given node. - * @return {Object} - */ - async startForging() { - const slot = slots.getSlotNumber() - - while (slots.getSlotNumber() === slot) { - await delay(100) - } - - return this.__monitor(null) - } - - /** - * Stop forging on the given node. - * @return {void} - */ - async stop() { - this.isStopped = true - } - - /** - * Monitor the node for any actions that trigger forging. - * @param {Object} round - * @return {Function} - */ - async __monitor(round) { - try { - if (this.isStopped) { - return - } - - await this.__loadUsernames() - - round = await this.client.getRound() - const delayTime = - parseInt(config.getConstants(round.lastBlock.height).blocktime) * 1000 - - 2000 - - if (!round.canForge) { - // logger.debug('Block already forged in current slot') - // technically it is possible to compute doing shennanigan with arkjs.slots lib - - await delay(200) // basically looping until we lock at beginning of next slot - - return this.__monitor(round) - } - - const delegate = this.__isDelegateActivated(round.currentForger.publicKey) - - if (!delegate) { - // logger.debug(`Current forging delegate ${ - // round.currentForger.publicKey - // } is not configured on this node.`) - - if (this.__isDelegateActivated(round.nextForger.publicKey)) { - const username = this.usernames[round.nextForger.publicKey] - logger.info( - `Next forging delegate ${username} (${ - round.nextForger.publicKey - }) is active on this node.`, - ) - await this.client.syncCheck() - } - - await delay(delayTime) // we will check at next slot - - return this.__monitor(round) - } - - const networkState = await this.client.getNetworkState() - - if (!this.__analyseNetworkState(networkState, delegate)) { - await delay(delayTime) // we will check at next slot - - return this.__monitor(round) - } - - await this.__forgeNewBlock(delegate, round) - - await delay(delayTime) // we will check at next slot - - return this.__monitor(round) - } catch (error) { - // README: The Blockchain is not ready, monitor until it is instead of crashing. - if (error.response && error.response.status === 503) { - logger.warn( - `Blockchain not ready - ${error.response.status} ${ - error.response.statusText - }`, - ) - - await delay(2000) - - return this.__monitor(round) - } - - // README: The Blockchain is ready but an action still failed. - logger.error(`Forging failed: ${error.message} :bangbang:`) - - if (!isEmpty(round)) { - logger.info( - `Round: ${round.current.toLocaleString()}, height: ${round.lastBlock.height.toLocaleString()}`, - ) - } - - await delay(2000) // no idea when this will be ok, so waiting 2s before checking again - - this.client.emitEvent('forger.failed', error.message) - - return this.__monitor(round) - } - } - - /** - * Creates new block by the delegate and sends it to relay node for verification and broadcast - * @param {Object} delegate - * @param {Object} round - */ - async __forgeNewBlock(delegate, round) { - // TODO: Disabled for now as this could cause a delay in forging that - // results in missing a block which we want to avoid. - // - // We should either use a very radical timeout like 500ms or look - // into another solution for broadcasting this specific event. - // - // this.client.emitEvent('forger.started', delegate.publicKey) - - const transactions = await this.__getTransactionsForForging() - - const blockOptions = {} - blockOptions.previousBlock = round.lastBlock - blockOptions.timestamp = round.timestamp - blockOptions.reward = round.reward - - const block = await delegate.forge(transactions, blockOptions) - - const username = this.usernames[delegate.publicKey] - logger.info( - `Forged new block ${block.data.id} by delegate ${username} (${ - delegate.publicKey - }) :trident:`, - ) - - await this.client.broadcast(block.toJson()) - - this.client.emitEvent('block.forged', block.data) - transactions.forEach(transaction => - this.client.emitEvent('transaction.forged', transaction.data), - ) - } - - /** - * Gets the unconfirmed transactions from the relay nodes transaction pool - */ - async __getTransactionsForForging() { - const response = await this.client.getTransactions() - - const transactions = response.transactions - ? response.transactions.map(serializedTx => - Transaction.fromBytes(serializedTx), - ) - : [] - - if (isEmpty(response)) { - logger.error( - 'Could not get unconfirmed transactions from transaction pool.', - ) - } else { - logger.debug( - `Received ${pluralize( - 'transaction', - transactions.length, - true, - )} from the pool containing ${response.poolSize} :money_with_wings:`, - ) - } - - return transactions - } - - /** - * Checks if delegate public key is in the loaded (active) delegates list - * @param {Object} PublicKey - * @return {Object} - */ - __isDelegateActivated(queryPublicKey) { - return this.delegates.find( - delegate => delegate.publicKey === queryPublicKey, - ) - } - - /** - * Analyses network state and decides if forging is allowed - * @param {Object} networkState internal response - * @param {Booolean} isAllowedToForge - */ - __analyseNetworkState(networkState, currentForger) { - const badState = message => { - logger.info(message) - logger.debug(`Network State: ${JSON.stringify(networkState, null, 4)}`) - - return false - } - - if (networkState.coldStart) { - return badState( - 'Not allowed to forge during the cold start period. Check peers.json for coldStart setting.', - ) - } - - if (!networkState.minimumNetworkReach) { - return badState('Network reach is not sufficient to get quorum.') - } - - if ( - networkState.overHeightBlockHeader && - networkState.overHeightBlockHeader.generatorPublicKey === - currentForger.publicKey - ) { - const usernames = this.usernames[currentForger.publicKey] - - return badState( - `Possible double forging for delegate: ${usernames} (${ - currentForger.publicKey - }).`, - ) - } - - if (networkState.quorum < 0.66) { - return badState('Fork 6 - Not enough quorum to forge next block.') - } - - return true - } - - /** - * Get a list of all active delegate usernames. - * @return {Object} - */ - async __loadUsernames(wait = 0) { - this.usernames = await this.client.getUsernames(wait) - } -} diff --git a/packages/core-forger/package.json b/packages/core-forger/package.json index 911602070f..e921091016 100644 --- a/packages/core-forger/package.json +++ b/packages/core-forger/package.json @@ -1,40 +1,60 @@ { - "name": "@arkecosystem/core-forger", - "description": "Forger for Ark Core", - "version": "0.2.0", - "contributors": [ - "François-Xavier Thoorens ", - "Kristjan Košič ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/crypto": "~0.2", - "axios": "^0.18.0", - "delay": "^4.1.0", - "lodash.isempty": "^4.4.0", - "lodash.sample": "^4.2.1", - "lodash.uniq": "^4.5.0", - "pluralize": "^7.0.0" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2", - "axios-mock-adapter": "^1.15.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-forger", + "description": "Forger for Ark Core", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Kristjan Košič ", + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-p2p": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/lodash.isempty": "^4.4.4", + "@types/lodash.sample": "^4.2.4", + "@types/lodash.uniq": "^4.5.4", + "@types/pluralize": "^0.0.29", + "axios": "^0.18.0", + "delay": "^4.1.0", + "lodash.isempty": "^4.4.0", + "lodash.sample": "^4.2.1", + "lodash.uniq": "^4.5.0", + "pluralize": "^7.0.0" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0", + "axios-mock-adapter": "^1.15.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-forger/src/client.ts b/packages/core-forger/src/client.ts new file mode 100644 index 0000000000..f19b579f8c --- /dev/null +++ b/packages/core-forger/src/client.ts @@ -0,0 +1,184 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { NetworkState, NetworkStateStatus } from "@arkecosystem/core-p2p"; +import axios from "axios"; +import delay from "delay"; +import sample from "lodash/sample"; +import { URL } from "url"; + +export class Client { + public hosts: string[]; + private host: any; + private headers: any; + private logger: Logger.ILogger; + + /** + * Create a new client instance. + * @param {(Array|String)} hosts - Host or Array of hosts + */ + constructor(hosts) { + this.hosts = Array.isArray(hosts) ? hosts : [hosts]; + this.logger = app.resolvePlugin("logger"); + + const { port } = new URL(this.hosts[0]); + + if (!port) { + throw new Error("Failed to determine the P2P communcation port."); + } + + this.headers = { + version: app.getVersion(), + port, + nethash: app.getConfig().get("network.nethash"), + "x-auth": "forger", + "Content-Type": "application/json", + }; + } + + /** + * Send the given block to the relay. + * @param {(Block|Object)} block + * @return {Object} + */ + public async broadcast(block) { + this.logger.debug( + `Broadcasting forged block id:${block.id} at height:${block.height.toLocaleString()} with ${ + block.numberOfTransactions + } transactions to ${this.host} :package:`, + ); + + return this.__post(`${this.host}/internal/blocks`, { block }); + } + + /** + * Sends the WAKEUP signal to the to relay hosts to check if synced and sync if necesarry + */ + public async syncCheck() { + await this.__chooseHost(); + + this.logger.debug(`Sending wake-up check to relay node ${this.host}`); + + try { + await this.__get(`${this.host}/internal/blockchain/sync`); + } catch (error) { + this.logger.error(`Could not sync check: ${error.message}`); + } + } + + /** + * Get the current round. + * @return {Object} + */ + public async getRound() { + try { + await this.__chooseHost(); + + const response = await this.__get(`${this.host}/internal/rounds/current`); + + return response.data.data; + } catch (e) { + return {}; + } + } + + /** + * Get the current network quorum. + * @return {NetworkState} + */ + public async getNetworkState(): Promise { + try { + const response = await this.__get(`${this.host}/internal/network/state`); + const { data } = response.data; + + return NetworkState.parse(data); + } catch (e) { + return new NetworkState(NetworkStateStatus.Unknown); + } + } + + /** + * Get all transactions that are ready to be forged. + * @return {Object} + */ + public async getTransactions() { + try { + const response = await this.__get(`${this.host}/internal/transactions/forging`); + + return response.data.data; + } catch (e) { + return {}; + } + } + + /** + * Get a list of all active delegate usernames. + * @return {Object} + */ + public async getUsernames(wait = 0) { + await this.__chooseHost(wait); + + try { + const response = await this.__get(`${this.host}/internal/utils/usernames`); + + return response.data.data; + } catch (e) { + return {}; + } + } + + /** + * Emit the given event and payload to the local host. + * @param {String} event + * @param {Object} body + * @return {Object} + */ + public async emitEvent(event, body) { + // NOTE: Events need to be emitted to the localhost. If you need to trigger + // actions on a remote host based on events you should be using webhooks + // that get triggered by the events you wish to react to. + + const allowedHosts = ["localhost", "127.0.0.1", "::ffff:127.0.0.1", "192.168.*"]; + + const host = this.hosts.find(item => allowedHosts.some(allowedHost => item.includes(allowedHost))); + + if (!host) { + return this.logger.error("Was unable to find any local hosts."); + } + + try { + await this.__post(`${host}/internal/utils/events`, { event, body }); + } catch (error) { + this.logger.error(`Failed to emit "${event}" to "${host}"`); + } + } + + /** + * Chose a responsive host. + * @return {void} + */ + public async __chooseHost(wait = 0) { + const host = sample(this.hosts); + + try { + await this.__get(`${host}/peer/status`); + + this.host = host; + } catch (error) { + this.logger.debug(`${host} didn't respond to the forger. Trying another host :sparkler:`); + + if (wait > 0) { + await delay(wait); + } + + await this.__chooseHost(wait); + } + } + + public async __get(url) { + return axios.get(url, { headers: this.headers, timeout: 2000 }); + } + + public async __post(url, body) { + return axios.post(url, body, { headers: this.headers, timeout: 2000 }); + } +} diff --git a/packages/core-forger/src/defaults.ts b/packages/core-forger/src/defaults.ts new file mode 100644 index 0000000000..8a79fae9c3 --- /dev/null +++ b/packages/core-forger/src/defaults.ts @@ -0,0 +1,3 @@ +export const defaults = { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4002}`], +}; diff --git a/packages/core-forger/src/index.ts b/packages/core-forger/src/index.ts new file mode 100644 index 0000000000..42d42007d8 --- /dev/null +++ b/packages/core-forger/src/index.ts @@ -0,0 +1,38 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import pluralize from "pluralize"; +import { defaults } from "./defaults"; +import { ForgerManager } from "./manager"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "forger", + async register(container: Container.IContainer, options) { + const forgerManager = new ForgerManager(options); + const forgers = await forgerManager.loadDelegates(options.bip38, options.password); + const logger = container.resolvePlugin("logger"); + + if (!forgers) { + logger.info("Forger is disabled :grey_exclamation:"); + return false; + } + + // Don't keep bip38 password in memory + delete process.env.CORE_FORGER_PASSWORD; + delete options.password; + + logger.info(`Forger Manager started with ${pluralize("forger", forgers.length, true)}`); + + forgerManager.startForging(); + + return forgerManager; + }, + async deregister(container: Container.IContainer, options) { + const forger = container.resolvePlugin("forger"); + + if (forger) { + container.resolvePlugin("logger").info("Stopping Forger Manager"); + return forger.stop(); + } + }, +}; diff --git a/packages/core-forger/src/manager.ts b/packages/core-forger/src/manager.ts new file mode 100644 index 0000000000..0f2d1b6ea7 --- /dev/null +++ b/packages/core-forger/src/manager.ts @@ -0,0 +1,306 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { NetworkStateStatus } from "@arkecosystem/core-p2p"; +import { models, slots } from "@arkecosystem/crypto"; +import delay from "delay"; +import isEmpty from "lodash/isEmpty"; +import uniq from "lodash/uniq"; +import pluralize from "pluralize"; + +import { Client } from "./client"; + +const { Delegate, Transaction } = models; + +export class ForgerManager { + private logger = app.resolvePlugin("logger"); + private config = app.getConfig(); + private secrets: any; + private network: any; + private client: any; + private delegates: any; + private usernames: any; + private isStopped: any; + + /** + * Create a new forger manager instance. + * @param {Object} options + */ + constructor(options) { + this.secrets = this.config.get("delegates.secrets"); + this.network = this.config.get("network"); + this.client = new Client(options.hosts); + } + + /** + * Load all delegates that forge. + * @param {String} bip38 + * @param {String} password + * @return {Array} + */ + public async loadDelegates(bip38, password) { + if (!bip38 && (!this.secrets || !this.secrets.length || !Array.isArray(this.secrets))) { + this.logger.warn('No delegate found! Please check your "delegates.json" file and try again.'); + return; + } + + this.secrets = uniq(this.secrets.map(secret => secret.trim())); + this.delegates = this.secrets.map(passphrase => new Delegate(passphrase, this.network, password)); + + if (bip38) { + this.logger.info("BIP38 Delegate loaded"); + + this.delegates.push(new Delegate(bip38, this.network, password)); + } + + await this.__loadUsernames(2000); + + const delegates = this.delegates.map( + delegate => `${this.usernames[delegate.publicKey]} (${delegate.publicKey})`, + ); + + this.logger.debug(`Loaded ${pluralize("delegate", delegates.length, true)}: ${delegates.join(", ")}`); + + return this.delegates; + } + + /** + * Start forging on the given node. + * @return {Object} + */ + public async startForging() { + const slot = slots.getSlotNumber(); + + while (slots.getSlotNumber() === slot) { + await delay(100); + } + + return this.__monitor(null); + } + + /** + * Stop forging on the given node. + * @return {void} + */ + public async stop() { + this.isStopped = true; + } + + /** + * Monitor the node for any actions that trigger forging. + * @param {Object} round + * @return {Function} + */ + public async __monitor(round): Promise { + try { + if (this.isStopped) { + return false; + } + + await this.__loadUsernames(); + + round = await this.client.getRound(); + const delayTime = +this.config.getMilestone(round.lastBlock.height).blocktime * 1000 - 2000; + + if (!round.canForge) { + // this.logger.debug('Block already forged in current slot') + // technically it is possible to compute doing shennanigan with arkjs.slots lib + + await delay(200); // basically looping until we lock at beginning of next slot + + return this.__monitor(round); + } + + const delegate = this.__isDelegateActivated(round.currentForger.publicKey); + + if (!delegate) { + // this.logger.debug(`Current forging delegate ${ + // round.currentForger.publicKey + // } is not configured on this node.`) + + if (this.__isDelegateActivated(round.nextForger.publicKey)) { + const username = this.usernames[round.nextForger.publicKey]; + this.logger.info( + `Next forging delegate ${username} (${round.nextForger.publicKey}) is active on this node.`, + ); + await this.client.syncCheck(); + } + + await delay(delayTime); // we will check at next slot + + return this.__monitor(round); + } + + const networkState = await this.client.getNetworkState(); + + if (!this.__parseNetworkState(networkState, delegate)) { + await delay(delayTime); // we will check at next slot + + return this.__monitor(round); + } + + await this.__forgeNewBlock(delegate, round); + + await delay(delayTime); // we will check at next slot + + return this.__monitor(round); + } catch (error) { + // README: The Blockchain is not ready, monitor until it is instead of crashing. + if (error.response && error.response.status === 503) { + this.logger.warn(`Blockchain not ready - ${error.response.status} ${error.response.statusText}`); + + await delay(2000); + + return this.__monitor(round); + } + + // README: The Blockchain is ready but an action still failed. + this.logger.error(`Forging failed: ${error.message} :bangbang:`); + + if (!isEmpty(round)) { + this.logger.info( + `Round: ${round.current.toLocaleString()}, height: ${round.lastBlock.height.toLocaleString()}`, + ); + } + + await delay(2000); // no idea when this will be ok, so waiting 2s before checking again + + this.client.emitEvent("forger.failed", error.message); + + return this.__monitor(round); + } + } + + /** + * Creates new block by the delegate and sends it to relay node for verification and broadcast + * @param {Object} delegate + * @param {Object} round + */ + public async __forgeNewBlock(delegate, round) { + // TODO: Disabled for now as this could cause a delay in forging that + // results in missing a block which we want to avoid. + // + // We should either use a very radical timeout like 500ms or look + // into another solution for broadcasting this specific event. + // + // this.client.emitEvent('forger.started', delegate.publicKey) + + const transactions = await this.__getTransactionsForForging(); + + const blockOptions = { + previousBlock: round.lastBlock, + timestamp: round.timestamp, + reward: round.reward, + }; + + const block = await delegate.forge(transactions, blockOptions); + + const username = this.usernames[delegate.publicKey]; + this.logger.info(`Forged new block ${block.data.id} by delegate ${username} (${delegate.publicKey}) :trident:`); + + await this.client.broadcast(block.toJson()); + + this.client.emitEvent("block.forged", block.data); + transactions.forEach(transaction => this.client.emitEvent("transaction.forged", transaction.data)); + } + + /** + * Gets the unconfirmed transactions from the relay nodes transaction pool + */ + public async __getTransactionsForForging() { + const response = await this.client.getTransactions(); + + const transactions = response.transactions + ? response.transactions.map(serializedTx => new Transaction(serializedTx)) + : []; + + if (isEmpty(response)) { + this.logger.error("Could not get unconfirmed transactions from transaction pool."); + } else { + this.logger.debug( + `Received ${pluralize("transaction", transactions.length, true)} from the pool containing ${ + response.poolSize + } :money_with_wings:`, + ); + } + + return transactions; + } + + /** + * Checks if delegate public key is in the loaded (active) delegates list + * @param {Object} PublicKey + * @return {Object} + */ + public __isDelegateActivated(queryPublicKey) { + return this.delegates.find(delegate => delegate.publicKey === queryPublicKey); + } + + /** + * Parses the given network state and decides if forging is allowed. + * @param {Object} networkState internal response + * @param {Booolean} isAllowedToForge + */ + public __parseNetworkState(networkState, currentForger) { + if (networkState.status === NetworkStateStatus.Unknown) { + this.logger.info("Failed to get network state from client."); + return false; + } + + if (networkState.status === NetworkStateStatus.ColdStart) { + this.logger.info( + "Not allowed to forge during the cold start period. Check peers.json for coldStart setting.", + ); + return false; + } + + if (networkState.status === NetworkStateStatus.BelowMinimumPeers) { + this.logger.info("Network reach is not sufficient to get quorum."); + return false; + } + + const overHeightBlockHeaders = networkState.getOverHeightBlockHeaders(); + if (overHeightBlockHeaders.length > 0) { + this.logger.info( + `Detected ${overHeightBlockHeaders.length} distinct overheight block ${pluralize( + "header", + overHeightBlockHeaders.length, + true, + )}.`, + ); + + let possibleDoubleForge = false; + for (const overHeightBlockHeader of overHeightBlockHeaders) { + if (overHeightBlockHeader.generatorPublicKey === currentForger.publicKey) { + const username = this.usernames[currentForger.publicKey]; + this.logger.warn( + `Possible double forging delegate: ${username} (${currentForger.publicKey}) - Block: ${ + overHeightBlockHeader.id + }`, + ); + possibleDoubleForge = true; + } + } + + if (possibleDoubleForge) { + this.logger.debug(`Network State: ${networkState.toJson()}`); + return false; + } + } + + if (networkState.getQuorum() < 0.66) { + this.logger.info("Fork 6 - Not enough quorum to forge next block."); + this.logger.debug(`Network State: ${networkState.toJson()}`); + return false; + } + + return true; + } + + /** + * Get a list of all active delegate usernames. + * @return {Object} + */ + public async __loadUsernames(wait = 0) { + this.usernames = await this.client.getUsernames(wait); + } +} diff --git a/packages/core-forger/tsconfig.json b/packages/core-forger/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-forger/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-graphql/CHANGELOG.md b/packages/core-graphql/CHANGELOG.md deleted file mode 100644 index 6782dc0f4b..0000000000 --- a/packages/core-graphql/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Increased test coverage - -### Fixed - -- Ensure order parameters are treated as lower-case -- Sorting and limit of records - -### Changed - -- Migrated to Apollo `2.0.0` -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Wallet queries and filtering - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-graphql/LICENSE b/packages/core-graphql/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-graphql/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-graphql/README.md b/packages/core-graphql/README.md index 3f661fbb29..7815d61650 100644 --- a/packages/core-graphql/README.md +++ b/packages/core-graphql/README.md @@ -14,8 +14,10 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Lúcio Rubens](https://github.com/luciorubeens) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Joshua Noack](https://github.com/supaiku0) +- [Lúcio Rubens](https://github.com/luciorubeens) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-graphql/__tests__/__support__/setup.js b/packages/core-graphql/__tests__/__support__/setup.js deleted file mode 100644 index 74736c6bbc..0000000000 --- a/packages/core-graphql/__tests__/__support__/setup.js +++ /dev/null @@ -1,18 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(60000) - -exports.setUp = async () => { - process.env.ARK_GRAPHQL_ENABLED = true - - await appHelper.setUp({ - exclude: ['@arkecosystem/core-api', '@arkecosystem/core-forger'], - }) - - return app -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-graphql/__tests__/__support__/setup.ts b/packages/core-graphql/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..326ac4d09f --- /dev/null +++ b/packages/core-graphql/__tests__/__support__/setup.ts @@ -0,0 +1,30 @@ +import { app } from "@arkecosystem/core-container"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; + +jest.setTimeout(60000); + +const options = { + enabled: true, + host: "0.0.0.0", + port: 4005, +}; + +export const setUp = async () => { + process.env.CORE_GRAPHQL_ENABLED = "true"; + + await setUpContainer({ + exclude: ["@arkecosystem/core-api", "@arkecosystem/core-forger", "@arkecosystem/core-graphql"], + }); + + const { plugin } = require("../../src"); + await registerWithContainer(plugin, options); + + return app; +}; + +export const tearDown = async () => { + await app.tearDown(); + + const { plugin } = require("../../src"); + await plugin.deregister(app, options); +}; diff --git a/packages/core-graphql/__tests__/__support__/utils.js b/packages/core-graphql/__tests__/__support__/utils.js deleted file mode 100644 index 55f834499d..0000000000 --- a/packages/core-graphql/__tests__/__support__/utils.js +++ /dev/null @@ -1,17 +0,0 @@ -const apiHelpers = require('@arkecosystem/core-test-utils/lib/helpers/api') - -class Helpers { - async request(query) { - const url = 'http://localhost:4005/graphql' - const server = require('@arkecosystem/core-container').resolvePlugin( - 'graphql', - ) - - return apiHelpers.request(server, 'POST', url, {}, { query }) - } -} - -/** - * @type {Helpers} - */ -module.exports = new Helpers() diff --git a/packages/core-graphql/__tests__/__support__/utils.ts b/packages/core-graphql/__tests__/__support__/utils.ts new file mode 100644 index 0000000000..c9685529ec --- /dev/null +++ b/packages/core-graphql/__tests__/__support__/utils.ts @@ -0,0 +1,16 @@ +import { app } from "@arkecosystem/core-container"; +import { ApiHelpers } from "../../../core-test-utils/src/helpers/api"; + +class Helpers { + public async request(query) { + const url = "http://localhost:4005/graphql"; + const server = app.resolvePlugin("graphql"); + + return ApiHelpers.request(server, "POST", url, {}, { query }); + } +} + +/** + * @type {Helpers} + */ +export const utils = new Helpers(); diff --git a/packages/core-graphql/__tests__/api/address.test.js b/packages/core-graphql/__tests__/api/address.test.js deleted file mode 100644 index d1f323d978..0000000000 --- a/packages/core-graphql/__tests__/api/address.test.js +++ /dev/null @@ -1,39 +0,0 @@ -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { address }', () => { - describe('GraphQL resolver for Address', () => { - it('should get wallter for a correctly formatted Address', async () => { - const query = '{ wallet(address: "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn") { producedBlocks } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.wallet).toBeObject() - - expect(data.wallet.producedBlocks).toBe(0) - }) - it('should return an error for an incorrectly formatted Address', async () => { - const query = '{ wallet(address: "bad address") { producedBlocks } }' - const response = await utils.request(query) - - expect(response).not.toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeFalsy() - expect(response.data.errors[0]).toBeObject() - expect(response.data.errors[0].message).not.toBeNull() - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/address.test.ts b/packages/core-graphql/__tests__/api/address.test.ts new file mode 100644 index 0000000000..0a3335b69d --- /dev/null +++ b/packages/core-graphql/__tests__/api/address.test.ts @@ -0,0 +1,40 @@ +import "@arkecosystem/core-test-utils"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(() => { + tearDown(); +}); + +describe("GraphQL API { address }", () => { + describe("GraphQL resolver for Address", () => { + it("should get wallter for a correctly formatted Address", async () => { + const query = '{ wallet(address: "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn") { producedBlocks } }'; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.wallet).toBeObject(); + + expect(data.wallet.producedBlocks).toBe(0); + }); + it("should return an error for an incorrectly formatted Address", async () => { + const query = '{ wallet(address: "bad address") { producedBlocks } }'; + const response = await utils.request(query); + + expect(response).not.toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeFalsy(); + expect(response.data.errors[0]).toBeObject(); + expect(response.data.errors[0].message).not.toBeNull(); + }); + }); +}); diff --git a/packages/core-graphql/__tests__/api/block.test.js b/packages/core-graphql/__tests__/api/block.test.js deleted file mode 100644 index 93ff1e3185..0000000000 --- a/packages/core-graphql/__tests__/api/block.test.js +++ /dev/null @@ -1,31 +0,0 @@ -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { block }', () => { - describe('GraphQL queries for Block', () => { - it('should get a block by its id', async () => { - const query = `{ block(id:"${genesisBlock.id}") { id } }` - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.block).toBeObject() - expect(data.block.id).toBe(genesisBlock.id) - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/block.test.ts b/packages/core-graphql/__tests__/api/block.test.ts new file mode 100644 index 0000000000..0ee445bf95 --- /dev/null +++ b/packages/core-graphql/__tests__/api/block.test.ts @@ -0,0 +1,29 @@ +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(() => { + tearDown(); +}); + +describe("GraphQL API { block }", () => { + describe("GraphQL queries for Block", () => { + it("should get a block by its id", async () => { + const query = `{ block(id:"${genesisBlock.id}") { id } }`; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.block).toBeObject(); + expect(data.block.id).toBe(genesisBlock.id); + }); + }); +}); diff --git a/packages/core-graphql/__tests__/api/blocks.test.js b/packages/core-graphql/__tests__/api/blocks.test.js deleted file mode 100644 index 1eb312ee51..0000000000 --- a/packages/core-graphql/__tests__/api/blocks.test.js +++ /dev/null @@ -1,60 +0,0 @@ -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { blocks }', () => { - describe('GraphQL queries for Blocks - filter by generatorPublicKey', () => { - it('should get blocks by generatorPublicKey', async () => { - const query = `{ blocks(filter: { generatorPublicKey: "${ - genesisBlock.generatorPublicKey - }" }) { id } }` - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.blocks).toEqual([{ id: genesisBlock.id }]) - }) - }) - - describe('GraphQL queries for Blocks - testing relationships', () => { - it('should verify that relationships are valid', async () => { - const query = '{ blocks(limit: 1) { generator { address } } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.blocks[0].generator.address).toEqual( - 'AP6kAVdX1zQ3S8mfDnnHx9GaAohEqQUins', - ) - }) - }) - - describe('GraphQL queries for Blocks - testing api errors', () => { - it('should not be a successful query', async () => { - const query = '{ blocks(filter: { vers } ) { id } }' - const response = await utils.request(query) - - expect(response).not.toBeSuccessfulResponse() - - const error = response.data.errors - expect(error).toBeArray() - expect(response.status).toEqual(400) - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/blocks.test.ts b/packages/core-graphql/__tests__/api/blocks.test.ts new file mode 100644 index 0000000000..1403cddc4a --- /dev/null +++ b/packages/core-graphql/__tests__/api/blocks.test.ts @@ -0,0 +1,54 @@ +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(() => { + tearDown(); +}); + +describe("GraphQL API { blocks }", () => { + describe("GraphQL queries for Blocks - filter by generatorPublicKey", () => { + it("should get blocks by generatorPublicKey", async () => { + const query = `{ blocks(filter: { generatorPublicKey: "${genesisBlock.generatorPublicKey}" }) { id } }`; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.blocks).toEqual([{ id: genesisBlock.id }]); + }); + }); + + describe("GraphQL queries for Blocks - testing relationships", () => { + it("should verify that relationships are valid", async () => { + const query = "{ blocks(limit: 1) { generator { address } } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.blocks[0].generator.address).toEqual("AP6kAVdX1zQ3S8mfDnnHx9GaAohEqQUins"); + }); + }); + + describe("GraphQL queries for Blocks - testing api errors", () => { + it("should not be a successful query", async () => { + const query = "{ blocks(filter: { vers } ) { id } }"; + const response = await utils.request(query); + + expect(response).not.toBeSuccessfulResponse(); + + const error = response.data.errors; + expect(error).toBeArray(); + expect(response.status).toEqual(400); + }); + }); +}); diff --git a/packages/core-graphql/__tests__/api/transaction.test.js b/packages/core-graphql/__tests__/api/transaction.test.js deleted file mode 100644 index c9b4aa0d4d..0000000000 --- a/packages/core-graphql/__tests__/api/transaction.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { transaction }', () => { - describe('GraphQL queries for Transaction', () => { - it('should get a transaction by its id', async () => { - const query = `{ transaction(id:"${ - genesisBlock.transactions[0].id - }") { id } }` - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transaction).toBeObject() - expect(data.transaction.id).toBe(genesisBlock.transactions[0].id) - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/transaction.test.ts b/packages/core-graphql/__tests__/api/transaction.test.ts new file mode 100644 index 0000000000..131d8f6150 --- /dev/null +++ b/packages/core-graphql/__tests__/api/transaction.test.ts @@ -0,0 +1,29 @@ +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll( async () => { + await tearDown(); +}); + +describe("GraphQL API { transaction }", () => { + describe("GraphQL queries for Transaction", () => { + it("should get a transaction by its id", async () => { + const query = `{ transaction(id:"${genesisBlock.transactions[0].id}") { id } }`; + const response = await utils.request(query); + + await expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transaction).toBeObject(); + expect(data.transaction.id).toBe(genesisBlock.transactions[0].id); + }); + }); +}); diff --git a/packages/core-graphql/__tests__/api/transactions.test.js b/packages/core-graphql/__tests__/api/transactions.test.js deleted file mode 100644 index 1a45068842..0000000000 --- a/packages/core-graphql/__tests__/api/transactions.test.js +++ /dev/null @@ -1,189 +0,0 @@ -/* eslint max-len: "off" */ - -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { transactions }', () => { - describe('GraphQL queries for Transactions - all', () => { - it('should get 100 transactions', async () => { - const query = '{ transactions { id } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions.length).toBe(100) - }) - }) - - describe('GraphQL queries for Transactions - orderBy', () => { - it('should get 100 transactionsin ascending order of their id', async () => { - const query = - '{ transactions(orderBy: { field: "id", direction: ASC }) { id } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions.length).toBe(100) - expect( - data.transactions.sort((a, b) => (parseInt(a) <= parseInt(b) ? -1 : 0)), - ).toEqual(data.transactions) - }) - }) - - describe('GraphQL queries for Transactions - filter by fee', () => { - it('should get all transactions with fee = 0', async () => { - const query = '{ transactions(filter: { fee: 0 }) { id } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions.length).toBe(100) // because of default limit = 100 - }) - - it('should get no transaction with fee = 987', async () => { - const query = '{ transactions(filter: { fee: 987 }) { id } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions.length).toBe(0) - }) - }) - - describe('GraphQL queries for Transactions - filter by blockId', () => { - it('should get transactions for given blockId', async () => { - const query = `{ transactions(filter: { blockId: "${ - genesisBlock.id - }" }) { id } }` - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - - const genesisBlockTransactionIds = genesisBlock.transactions.map( - transaction => transaction.id, - ) - data.transactions.forEach(transaction => { - expect(genesisBlockTransactionIds).toContain(transaction.id) - }) - }) - }) - - describe('GraphQL queries for Transactions - filter by senderPublicKey', () => { - it('should get transactions for given senderPublicKey', async () => { - const query = `{ transactions(filter: { senderPublicKey: "${ - genesisBlock.transactions[0].senderPublicKey - }" }) { id } }` - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions.length).toEqual(51) // number of outgoing transactions for the 0th transaction's sender address - - const genesisBlockTransactionIds = genesisBlock.transactions.map( - transaction => transaction.id, - ) - - data.transactions.forEach(transaction => { - expect(genesisBlockTransactionIds).toContain(transaction.id) - }) - }) - }) - - describe('GraphQL queries for Transactions - filter by recipientId', () => { - it('should get transactions for given recipientId', async () => { - const query = - '{ transactions(filter: { recipientId: "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" }) { id } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - - expect(data.transactions.length).toBe(2) - }) - }) - - describe('GraphQL queries for Transactions - filter by type', () => { - it('should get transactions for given type', async () => { - const query = '{ transactions(filter: { type: TRANSFER } ) { type } }' - const response = await utils.request(query) - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - - data.transactions.forEach(tx => { - expect(tx.type).toBe(Number(0)) - }) - }) - }) - - describe('GraphQL queries for Transactions - using orderBy, limit', () => { - it('should get 5 transactions in order of ASCending address', async () => { - const query = - '{ transactions(orderBy: { field: "id", direction: ASC }, limit: 5 ) { id } }' - const response = await utils.request(query) - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions.length).toBe(5) - - expect(parseInt(data.transactions[0].id, 16)).toBeLessThan( - parseInt(data.transactions[1].id, 16), - ) - }) - }) - - describe('GraphQL queries for Transactions - testing relationships', () => { - it('should verify that relationships are valid', async () => { - const query = '{ transactions(limit: 1) { recipient { address } } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.transactions[0].recipient.address).not.toBeNull() - }) - }) - - describe('GraphQL queries for Transactions - testing api errors', () => { - it('should not be a successful query', async () => { - const query = '{ transaction(filter: { vers } ) { id } }' - const response = await utils.request(query) - - expect(response).not.toBeSuccessfulResponse() - - const error = response.data.errors - expect(error).toBeArray() - expect(response.status).toEqual(400) - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/transactions.test.ts b/packages/core-graphql/__tests__/api/transactions.test.ts new file mode 100644 index 0000000000..6ed78078fc --- /dev/null +++ b/packages/core-graphql/__tests__/api/transactions.test.ts @@ -0,0 +1,174 @@ +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(() => { + tearDown(); +}); + +describe("GraphQL API { transactions }", () => { + describe("GraphQL queries for Transactions - all", () => { + it("should get 100 transactions", async () => { + const query = "{ transactions { id } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transactions.length).toBe(100); + }); + }); + + describe("GraphQL queries for Transactions - orderBy", () => { + it("should get 100 transactionsin ascending order of their id", async () => { + const query = '{ transactions(orderBy: { field: "id", direction: ASC }) { id } }'; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transactions.length).toBe(100); + expect(data.transactions.sort((a, b) => (+a <= +b ? -1 : 0))).toEqual(data.transactions); + }); + }); + + describe("GraphQL queries for Transactions - filter by fee", () => { + it("should get all transactions with fee = 0", async () => { + const query = "{ transactions(filter: { fee: 0 }) { id } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transactions.length).toBe(100); // because of default limit = 100 + }); + + it("should get no transaction with fee = 987", async () => { + const query = "{ transactions(filter: { fee: 987 }) { id } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transactions.length).toBe(0); + }); + }); + + describe("GraphQL queries for Transactions - filter by blockId", () => { + it("should get transactions for given blockId", async () => { + const query = `{ transactions(filter: { blockId: "${genesisBlock.id}" }) { id } }`; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + + const genesisBlockTransactionIds = genesisBlock.transactions.map(transaction => transaction.id); + data.transactions.forEach(transaction => { + expect(genesisBlockTransactionIds).toContain(transaction.id); + }); + }); + }); + + describe("GraphQL queries for Transactions - filter by senderPublicKey", () => { + it("should get transactions for given senderPublicKey", async () => { + const query = `{ transactions(filter: { senderPublicKey: "${ + genesisBlock.transactions[0].senderPublicKey + }" }) { id } }`; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + // tslint:disable-next-line:max-line-length + expect(data.transactions.length).toEqual(51); // number of outgoing transactions for the 0th transaction's sender address + + const genesisBlockTransactionIds = genesisBlock.transactions.map(transaction => transaction.id); + + data.transactions.forEach(transaction => { + expect(genesisBlockTransactionIds).toContain(transaction.id); + }); + }); + }); + + describe("GraphQL queries for Transactions - filter by recipientId", () => { + it("should get transactions for given recipientId", async () => { + const query = '{ transactions(filter: { recipientId: "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" }) { id } }'; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + + expect(data.transactions.length).toBe(2); + }); + }); + + describe("GraphQL queries for Transactions - filter by type", () => { + it("should get transactions for given type", async () => { + const query = "{ transactions(filter: { type: TRANSFER } ) { type } }"; + const response = await utils.request(query); + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + + data.transactions.forEach(tx => { + expect(tx.type).toBe(Number(0)); + }); + }); + }); + + describe("GraphQL queries for Transactions - using orderBy, limit", () => { + it("should get 5 transactions in order of ASCending address", async () => { + const query = '{ transactions(orderBy: { field: "id", direction: ASC }, limit: 5 ) { id } }'; + const response = await utils.request(query); + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transactions.length).toBe(5); + + expect(parseInt(data.transactions[0].id, 16)).toBeLessThan(parseInt(data.transactions[1].id, 16)); + }); + }); + + describe("GraphQL queries for Transactions - testing relationships", () => { + it("should verify that relationships are valid", async () => { + const query = "{ transactions(limit: 1) { recipient { address } } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.transactions[0].recipient.address).not.toBeNull(); + }); + }); + + describe("GraphQL queries for Transactions - testing api errors", () => { + it("should not be a successful query", async () => { + const query = "{ transaction(filter: { vers } ) { id } }"; + const response = await utils.request(query); + + expect(response).not.toBeSuccessfulResponse(); + + const error = response.data.errors; + expect(error).toBeArray(); + expect(response.status).toEqual(400); + }); + }); +}); diff --git a/packages/core-graphql/__tests__/api/wallet.test.js b/packages/core-graphql/__tests__/api/wallet.test.js deleted file mode 100644 index 24c3851620..0000000000 --- a/packages/core-graphql/__tests__/api/wallet.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - - genesisBlock = require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json') -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { wallet }', () => { - describe('GraphQL queries for Wallet', () => { - it('should get a wallet by address', async () => { - const query = `{ wallet(address:"${ - genesisBlock.transactions[0].senderId - }") { address } }` - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.wallet).toBeObject() - expect(data.wallet.address).toBe(genesisBlock.transactions[0].senderId) - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/wallet.test.ts b/packages/core-graphql/__tests__/api/wallet.test.ts new file mode 100644 index 0000000000..f0a12e6ff2 --- /dev/null +++ b/packages/core-graphql/__tests__/api/wallet.test.ts @@ -0,0 +1,29 @@ +import "@arkecosystem/core-test-utils"; +import genesisBlock from "../../../core-test-utils/src/config/testnet/genesisBlock.json"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(() => { + tearDown(); +}); + +describe("GraphQL API { wallet }", () => { + describe("GraphQL queries for Wallet", () => { + it("should get a wallet by address", async () => { + const query = `{ wallet(address:"${genesisBlock.transactions[0].senderId}") { address } }`; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.wallet).toBeObject(); + expect(data.wallet.address).toBe(genesisBlock.transactions[0].senderId); + }); + }); +}); diff --git a/packages/core-graphql/__tests__/api/wallets.test.js b/packages/core-graphql/__tests__/api/wallets.test.js deleted file mode 100644 index ec908b8d61..0000000000 --- a/packages/core-graphql/__tests__/api/wallets.test.js +++ /dev/null @@ -1,96 +0,0 @@ -/* eslint max-len: "off" */ - -const app = require('../__support__/setup') -const utils = require('../__support__/utils') -require('@arkecosystem/core-test-utils/lib/matchers') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(() => { - app.tearDown() -}) - -describe('GraphQL API { wallets }', () => { - describe('GraphQL queries for Wallets - get all', () => { - it('should get all wallets', async () => { - const query = '{ wallets { address } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.wallets.length).toBe(53) - // TODO why 53 ? From genesis block I can count 52, but there is an additional "AP6kAVdX1zQ3S8mfDnnHx9GaAohEqQUins" wallet. What did I miss ? - }) - }) - - describe('GraphQL queries for Wallets - filter by vote', () => { - it('should get all wallets with specific vote', async () => { - const query = - '{ wallets(filter: { vote: "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" }) { address } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.wallets.length).toBe(1) - }) - - it('should get no wallet with unknown vote', async () => { - const query = - '{ wallets(filter: { vote: "unknownPublicKey" }) { address } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.wallets.length).toBe(0) - }) - }) - - describe('GraphQL queries for Wallets - using orderBy, limit', () => { - it('should get 5 wallets in order of ASCending address', async () => { - const query = - '{ wallets(orderBy: { field: "address", direction: ASC }, limit: 5 ) { address } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - const data = response.data.data - expect(data).toBeObject() - expect(data.wallets.length).toBe(5) - expect( - data.wallets.sort((a, b) => (parseInt(a) <= parseInt(b) ? -1 : 0)), - ).toEqual(data.wallets) - }) - }) - - describe('GraphQL queries for Wallets - testing relationships', () => { - it('should verify that relationships are valid', async () => { - const query = '{ wallets(limit: 1) { transactions { id } } }' - const response = await utils.request(query) - - expect(response).toBeSuccessfulResponse() - - expect(response.data.errors[0]).toBeObject() // relationships doesn't function well (unimplemented) - }) - }) - - describe('GraphQL queries for Wallets - testing api errors', () => { - it('should not be a successful query', async () => { - const query = '{ wallets(filter: { vers } ) { address } }' - const response = await utils.request(query) - - expect(response).not.toBeSuccessfulResponse() - - const error = response.data.errors - expect(error).toBeArray() - expect(response.status).toEqual(400) - }) - }) -}) diff --git a/packages/core-graphql/__tests__/api/wallets.test.ts b/packages/core-graphql/__tests__/api/wallets.test.ts new file mode 100644 index 0000000000..cb1dc40d3a --- /dev/null +++ b/packages/core-graphql/__tests__/api/wallets.test.ts @@ -0,0 +1,93 @@ +/* tslint:disable:max-line-length */ + +import "@arkecosystem/core-test-utils"; + +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(() => { + tearDown(); +}); + +describe("GraphQL API { wallets }", () => { + describe("GraphQL queries for Wallets - get all", () => { + it("should get all wallets", async () => { + const query = "{ wallets { address } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.wallets.length).toBe(53); + // TODO why 53 ? From genesis block I can count 52, but there is an additional "AP6kAVdX1zQ3S8mfDnnHx9GaAohEqQUins" wallet. What did I miss ? + }); + }); + + describe("GraphQL queries for Wallets - filter by vote", () => { + it("should get all wallets with specific vote", async () => { + const query = + '{ wallets(filter: { vote: "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" }) { address } }'; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.wallets.length).toBe(1); + }); + + it("should get no wallet with unknown vote", async () => { + const query = '{ wallets(filter: { vote: "unknownPublicKey" }) { address } }'; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.wallets.length).toBe(0); + }); + }); + + describe("GraphQL queries for Wallets - using orderBy, limit", () => { + it("should get 5 wallets in order of ASCending address", async () => { + const query = '{ wallets(orderBy: { field: "address", direction: ASC }, limit: 5 ) { address } }'; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + const data = response.data.data; + expect(data).toBeObject(); + expect(data.wallets.length).toBe(5); + expect(data.wallets.sort((a, b) => (+a <= +b ? -1 : 0))).toEqual(data.wallets); + }); + }); + + describe("GraphQL queries for Wallets - testing relationships", () => { + it("should verify that relationships are valid", async () => { + const query = "{ wallets(limit: 1) { transactions { id } } }"; + const response = await utils.request(query); + + expect(response).toBeSuccessfulResponse(); + + expect(response.data.errors[0]).toBeObject(); // relationships doesn't function well (unimplemented) + }); + }); + + describe("GraphQL queries for Wallets - testing api errors", () => { + it("should not be a successful query", async () => { + const query = "{ wallets(filter: { vers } ) { address } }"; + const response = await utils.request(query); + + expect(response).not.toBeSuccessfulResponse(); + + const error = response.data.errors; + expect(error).toBeArray(); + expect(response.status).toEqual(400); + }); + }); +}); diff --git a/packages/core-graphql/jest.config.js b/packages/core-graphql/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-graphql/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-graphql/lib/defaults.js b/packages/core-graphql/lib/defaults.js deleted file mode 100644 index dfbc999d31..0000000000 --- a/packages/core-graphql/lib/defaults.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Default configuration for the @arkecosystem/core-graphql plugin - */ -module.exports = { - enabled: false, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4005, - path: '/graphql', -} diff --git a/packages/core-graphql/lib/defs/index.js b/packages/core-graphql/lib/defs/index.js deleted file mode 100644 index 643317b565..0000000000 --- a/packages/core-graphql/lib/defs/index.js +++ /dev/null @@ -1,13 +0,0 @@ -const inputs = require('./inputs') -const types = require('./types') -const root = require('./root') - -/** - * Concatenated strings following the GraphQL syntax to define Types - * processed by the schema. - */ -module.exports = ` - ${inputs} - ${root} - ${types} -` diff --git a/packages/core-graphql/lib/defs/inputs.js b/packages/core-graphql/lib/defs/inputs.js deleted file mode 100644 index 082682184d..0000000000 --- a/packages/core-graphql/lib/defs/inputs.js +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Template for the inputs of our schema's types. - * Scalars are the possible base values for query parameters. - * OrderDirection defaults to DESC in the resolvers. - * Includes an enum to check TransactionTypes with supplied query parameter. - * Has filters to be used against the main types of queriable objects. - * Order query by specified field, with respect to OrderDirection. - */ -module.exports = ` - scalar JSON - scalar Limit - scalar Offset - scalar Address - - enum OrderDirection { - ASC - DESC - } - - enum TransactionType { - TRANSFER, - SECOND_SIGNATURE, - DELEGATE, - VOTE, - MULTI_SIGNATURE, - IPFS, - TIMELOCK_TRANSFER, - MULTI_PAYMENT, - DELEGATE_RESIGNATION - } - - input TransactionFilter { - fee: Float - blockId: String - senderPublicKey: String - recipientId: String - type: TransactionType - } - - input BlockFilter { - generatorPublicKey: String - } - - input WalletFilter { - vote: String - } - - input OrderByInput { - field: String - direction: OrderDirection - } -` diff --git a/packages/core-graphql/lib/defs/root.js b/packages/core-graphql/lib/defs/root.js deleted file mode 100644 index 91e4c19759..0000000000 --- a/packages/core-graphql/lib/defs/root.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Necessary for the GraphQL engine to have a root schema and a base query. - * Here we have definitions for root queries, which are like endpoints in a - * REST API. Every root query has an associated return structure which is - * based on types defined in types.js. - */ -module.exports = ` - type Query { - block(id: String): Block - blocks(limit: Limit, offset: Offset, orderBy: OrderByInput, filter: BlockFilter): [Block] - transaction(id: String): Transaction - transactions(limit: Limit, orderBy: OrderByInput, filter: TransactionFilter): [Transaction] - wallet(address: Address, publicKey: String, username: String): Wallet - wallets(limit: Limit, orderBy: OrderByInput, filter: WalletFilter): [Wallet] - } - - schema { - query: Query - } -` diff --git a/packages/core-graphql/lib/defs/types.js b/packages/core-graphql/lib/defs/types.js deleted file mode 100644 index ff05a652ea..0000000000 --- a/packages/core-graphql/lib/defs/types.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Actual types which are relevant to queries issued to our GraphQL endpoint. - * The basic ones are Block, Wallet and Transaction. They each have specific - * properties and are representative of how they are stored in the Blockchain. - * For example, a Block type has an array of transactions [Transaction], and - * Transaction itself is a type which has sender and recipiet Wallet types. - * Same principles apply to Wallet types, there is interoperability between - * the defined types of this schema. - */ -module.exports = ` - type Block { - id: String - version: Int! - timestamp: Int! - previousBlock: String - height: Int! - numberOfTransactions: Int! - totalAmount: Float - totalFee: Float - reward: Float - payloadLength: Int! - payloadHash: String - generatorPublicKey: String - blockSignature: String - transactions(limit: Limit, offset: Offset, orderBy: OrderByInput, filter: TransactionFilter): [Transaction] - generator: Wallet - } - - type Transaction { - id: String - version: Int! - timestamp: Int! - senderPublicKey: String - recipientId: Address - type: Int! - vendorField: String - amount: Float - fee: Float - signature: String - block: Block - recipient: Wallet - sender: Wallet - } - - type Wallet { - address: Address - publicKey: String - secondPublicKey: String - vote: String - username: String - balance: Float - voteBalance: Float - producedBlocks: Float - missedBlocks: Float - transactions(limit: Limit, offset: Offset, orderBy: OrderByInput): [Transaction] - blocks(limit: Limit, offset: Offset, orderBy: OrderByInput): [Block] - } -` diff --git a/packages/core-graphql/lib/helpers/format-orderBy.js b/packages/core-graphql/lib/helpers/format-orderBy.js deleted file mode 100644 index 29961161f1..0000000000 --- a/packages/core-graphql/lib/helpers/format-orderBy.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Logic used by our orderBy input - * @param {Object} parameter - * @param {String} defaultValue - * @return {String} - */ -module.exports = (parameter, defaultValue) => { - let order - - if (parameter) { - order = `${parameter.field}:${parameter.direction.toLowerCase()}` - } - - return order || defaultValue -} diff --git a/packages/core-graphql/lib/helpers/index.js b/packages/core-graphql/lib/helpers/index.js deleted file mode 100644 index 4bb2364535..0000000000 --- a/packages/core-graphql/lib/helpers/index.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Root module for our two helper functions - */ -module.exports = { - formatOrderBy: require('./format-orderBy'), - unserializeTransactions: require('./unserialize-transactions'), -} diff --git a/packages/core-graphql/lib/helpers/unserialize-transactions.js b/packages/core-graphql/lib/helpers/unserialize-transactions.js deleted file mode 100644 index 37e9e9bb06..0000000000 --- a/packages/core-graphql/lib/helpers/unserialize-transactions.js +++ /dev/null @@ -1,20 +0,0 @@ -const { Transaction } = require('@arkecosystem/crypto').models - -/** - * Deserialize multiple transactions - */ -module.exports = async data => { - const deserialize = buffer => { - const serialized = Buffer.from(buffer).toString('hex') - return Transaction.deserialize(serialized) - } - - if (Array.isArray(data)) { - return data.reduce((total, value, key) => { - total.push(deserialize(value.serialized)) - - return total - }, []) - } - return deserialize(data) -} diff --git a/packages/core-graphql/lib/index.js b/packages/core-graphql/lib/index.js deleted file mode 100644 index d8137eb0fc..0000000000 --- a/packages/core-graphql/lib/index.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * The struct used by the plugin manager. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'graphql', - async register(container, options) { - if (!options.enabled) { - container - .resolvePlugin('logger') - .info('GraphQL API is disabled :grey_exclamation:') - - return - } - - return require('./server')(options) - }, - async deregister(container, options) { - if (options.enabled) { - container.resolvePlugin('logger').info('Stopping GraphQL API') - - return container.resolvePlugin('graphql').stop() - } - }, -} diff --git a/packages/core-graphql/lib/repositories/blocks.js b/packages/core-graphql/lib/repositories/blocks.js deleted file mode 100644 index c779a84d46..0000000000 --- a/packages/core-graphql/lib/repositories/blocks.js +++ /dev/null @@ -1,148 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') - -const buildFilterQuery = require('./utils/filter-query') -const Repository = require('./repository') - -class BlocksRepository extends Repository { - /** - * Get all blocks for the given parameters. - * @param {Object} parameters - * @return {Object} - */ - async findAll(parameters = {}) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - const applyConditions = queries => { - const conditions = Object.entries(this._formatConditions(parameters)) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first[0]].equals(first[1])) - - for (const condition of conditions) { - item.and(this.query[condition[0]].equals(condition[1])) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - return this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit || 100, - offset: parameters.offset || 0, - orderBy: this.__orderBy(parameters), - }) - } - - /** - * Get all blocks for the given generator. - * @param {String} generatorPublicKey - * @param {Object} paginator - * @return {Object} - */ - async findAllByGenerator(generatorPublicKey, paginator) { - return this.findAll({ ...{ generatorPublicKey }, ...paginator }) - } - - /** - * Get a block. - * @param {Number} id - * @return {Object} - */ - async findById(id) { - const query = this.query - .select() - .from(this.query) - .where(this.query.id.equals(id)) - - return this._find(query) - } - - /** - * Get the last block for the given generator. - * TODO is this right? - * @param {String} generatorPublicKey - * @return {Object} - */ - async findLastByPublicKey(generatorPublicKey) { - const query = this.query - .select(this.query.id, this.query.timestamp) - .from(this.query) - .where(this.query.generator_public_key.equals(generatorPublicKey)) - .order(this.query.height.desc) - - return this._find(query) - } - - /** - * Search all blocks. - * @param {Object} parameters - * @return {Object} - */ - async search(parameters) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - const applyConditions = queries => { - const conditions = buildFilterQuery(this._formatConditions(parameters), { - exact: [ - 'id', - 'version', - 'previous_block', - 'payload_hash', - 'generator_public_key', - 'block_signature', - ], - between: [ - 'timestamp', - 'height', - 'number_of_transactions', - 'total_amount', - 'total_fee', - 'reward', - 'payload_length', - ], - }) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first.column][first.method](first.value)) - - for (const condition of conditions) { - item.and( - this.query[condition.column][condition.method](condition.value), - ) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - return this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - } - - getModel() { - return database.models.block - } - - __orderBy(parameters) { - return parameters.orderBy - ? parameters.orderBy.split(':').map(p => p.toLowerCase()) - : ['height', 'desc'] - } -} - -module.exports = new BlocksRepository() diff --git a/packages/core-graphql/lib/repositories/index.js b/packages/core-graphql/lib/repositories/index.js deleted file mode 100644 index 5724a9e9b4..0000000000 --- a/packages/core-graphql/lib/repositories/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - blocks: require('./blocks'), - transactions: require('./transactions'), -} diff --git a/packages/core-graphql/lib/repositories/repository.js b/packages/core-graphql/lib/repositories/repository.js deleted file mode 100644 index 044d25fc3a..0000000000 --- a/packages/core-graphql/lib/repositories/repository.js +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint max-len: "off" */ - -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') - -module.exports = class Repository { - constructor() { - this.cache = database.getCache() - this.model = this.getModel() - this.query = this.model.query() - } - - async _find(query) { - return database.query.oneOrNone(query.toQuery()) - } - - async _findMany(query) { - return database.query.manyOrNone(query.toQuery()) - } - - async _findManyWithCount( - selectQuery, - countQuery, - { limit, offset, orderBy }, - ) { - const { count } = await this._find(countQuery) - - selectQuery - .order(this.query[orderBy[0]][orderBy[1]]) - .offset(offset) - .limit(limit) - - limit = 100 - offset = 0 - const rows = await this._findMany(selectQuery) - return { - rows, - count: +count, - } - } - - _makeCountQuery() { - return this.query.select('count(*) AS count').from(this.query) - } - - _makeEstimateQuery() { - return this.query - .select('count(*) AS count') - .from(`${this.model.getTable()} TABLESAMPLE SYSTEM (100)`) - } - - _formatConditions(parameters) { - const columns = this.model.getColumnSet().columns.map(column => ({ - name: column.name, - prop: column.prop || column.name, - })) - - const columnNames = columns.map(column => column.name) - const columnProps = columns.map(column => column.prop) - - const filter = args => - args.filter(arg => columnNames.includes(arg) || columnProps.includes(arg)) - - return filter(Object.keys(parameters)).reduce((items, item) => { - const columnName = columns.find(column => column.prop === item).name - - items[columnName] = parameters[item] - - return items - }, {}) - } -} diff --git a/packages/core-graphql/lib/repositories/transactions.js b/packages/core-graphql/lib/repositories/transactions.js deleted file mode 100644 index 11001079ac..0000000000 --- a/packages/core-graphql/lib/repositories/transactions.js +++ /dev/null @@ -1,451 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const database = app.resolvePlugin('database') - -const dayjs = require('dayjs-ext') -const { slots } = require('@arkecosystem/crypto') -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants -const buildFilterQuery = require('./utils/filter-query') -const Repository = require('./repository') - -class TransactionsRepository extends Repository { - /** - * Get all transactions. - * @param {Object} params - * @return {Object} - */ - async findAll(parameters = {}) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - if (parameters.senderId) { - const senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId) - - if (!senderPublicKey) { - return { rows: [], count: 0 } - } - - parameters.senderPublicKey = senderPublicKey - } - - if (parameters.type) { - parameters.type = TRANSACTION_TYPES[parameters.type] - } - - const applyConditions = queries => { - const conditions = Object.entries(this._formatConditions(parameters)) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first[0]].equals(first[1])) - - for (const condition of conditions) { - item.and(this.query[condition[0]].equals(condition[1])) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit || 100, - offset: parameters.offset || 0, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - return results - } - - /** - * Get all transactions (LEGACY, for V1 only). - * @param {Object} params - * @return {Object} - */ - async findAllLegacy(parameters = {}) { - const selectQuery = this.query - .select(this.query.block_id, this.query.serialized) - .from(this.query) - const countQuery = this._makeEstimateQuery() - - if (parameters.senderId) { - parameters.senderPublicKey = this.__publicKeyFromSenderId( - parameters.senderId, - ) - } - - const applyConditions = queries => { - const conditions = Object.entries(this._formatConditions(parameters)) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first[0]].equals(first[1])) - - for (const [key, value] of conditions) { - item.or(this.query[key].equals(value)) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - /** - * Get all transactions for the given Wallet object. - * @param {Wallet} wallet - * @param {Object} parameters - * @return {Object} - */ - async findAllByWallet(wallet, parameters = {}) { - const selectQuery = this.query - .select(this.query.block_id, this.query.serialized) - .from(this.query) - const countQuery = this._makeEstimateQuery() - - const applyConditions = queries => { - for (const item of queries) { - item - .where(this.query.sender_public_key.equals(wallet.publicKey)) - .or(this.query.recipient_id.equals(wallet.address)) - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - /** - * Get all transactions for the given sender public key. - * @param {String} senderPublicKey - * @param {Object} parameters - * @return {Object} - */ - async findAllBySender(senderPublicKey, parameters = {}) { - return this.findAll({ ...{ senderPublicKey }, ...parameters }) - } - - /** - * Get all transactions for the given recipient address. - * @param {String} recipientId - * @param {Object} parameters - * @return {Object} - */ - async findAllByRecipient(recipientId, parameters = {}) { - return this.findAll({ ...{ recipientId }, ...parameters }) - } - - /** - * Get all vote transactions for the given sender public key. - * TODO rename to findAllVotesBySender or not? - * @param {String} senderPublicKey - * @param {Object} parameters - * @return {Object} - */ - async allVotesBySender(senderPublicKey, parameters = {}) { - return this.findAll({ - ...{ senderPublicKey, type: TRANSACTION_TYPES.VOTE }, - ...parameters, - }) - } - - /** - * Get all transactions for the given block. - * @param {Number} blockId - * @param {Object} parameters - * @return {Object} - */ - async findAllByBlock(blockId, parameters = {}) { - return this.findAll({ ...{ blockId }, ...parameters }) - } - - /** - * Get all transactions for the given type. - * @param {Number} type - * @param {Object} parameters - * @return {Object} - */ - async findAllByType(type, parameters = {}) { - return this.findAll({ ...{ type }, ...parameters }) - } - - /** - * Get a transaction. - * @param {Number} id - * @return {Object} - */ - async findById(id) { - const query = this.query - .select(this.query.block_id, this.query.serialized) - .from(this.query) - .where(this.query.id.equals(id)) - - const transaction = await this._find(query) - - return this.__mapBlocksToTransactions(transaction) - } - - /** - * Get a transactions for the given type and id. - * @param {Number} type - * @param {Number} id - * @return {Object} - */ - async findByTypeAndId(type, id) { - const query = this.query - .select(this.query.block_id, this.query.serialized) - .from(this.query) - .where(this.query.id.equals(id).and(this.query.type.equals(type))) - - const transaction = await this._find(query) - - return this.__mapBlocksToTransactions(transaction) - } - - /** - * Get transactions for the given ids. - * @param {Array} ids - * @return {Object} - */ - async findByIds(ids) { - const query = this.query - .select(this.query.block_id, this.query.serialized) - .from(this.query) - .where(this.query.id.in(ids)) - - return this._findMany(query) - } - - /** - * Get all transactions that have a vendor field. - * @return {Object} - */ - async findWithVendorField() { - const query = this.query - .select(this.query.block_id, this.query.serialized) - .from(this.query) - .where(this.query.vendor_field_hex.isNotNull()) - - const transactions = await this._findMany(query) - - return this.__mapBlocksToTransactions(transactions) - } - - /** - * Calculates min, max and average fee statistics based on transactions table - * @return {Object} - */ - async getFeeStatistics() { - const query = this.query - .select( - this.query.type, - this.query.fee.min('minFee'), - this.query.fee.max('maxFee'), - this.query.fee.avg('avgFee'), - this.query.timestamp.max('timestamp'), - ) - .from(this.query) - .where( - this.query.timestamp.gte(slots.getTime(dayjs().subtract(30, 'days'))), - ) - .group(this.query.type) - .order('"timestamp" DESC') - - return this._findMany(query) - } - - /** - * Search all transactions. - * - * @param {Object} params - * @return {Object} - */ - async search(parameters) { - const selectQuery = this.query.select().from(this.query) - const countQuery = this._makeEstimateQuery() - - if (parameters.senderId) { - const senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId) - - if (senderPublicKey) { - parameters.senderPublicKey = senderPublicKey - } - } - - const applyConditions = queries => { - const conditions = buildFilterQuery(this._formatConditions(parameters), { - exact: [ - 'id', - 'block_id', - 'type', - 'version', - 'sender_public_key', - 'recipient_id', - ], - between: ['timestamp', 'amount', 'fee'], - wildcard: ['vendor_field_hex'], - }) - - if (conditions.length) { - const first = conditions.shift() - - for (const item of queries) { - item.where(this.query[first.column][first.method](first.value)) - - for (const condition of conditions) { - item.and( - this.query[condition.column][condition.method](condition.value), - ) - } - } - } - } - - applyConditions([selectQuery, countQuery]) - - const results = await this._findManyWithCount(selectQuery, countQuery, { - limit: parameters.limit, - offset: parameters.offset, - orderBy: this.__orderBy(parameters), - }) - - results.rows = await this.__mapBlocksToTransactions(results.rows) - - return results - } - - getModel() { - return database.models.transaction - } - - /** - * [__mapBlocksToTransactions description] - * @param {Array|Object} data - * @return {Object} - */ - async __mapBlocksToTransactions(data) { - const blockQuery = database.models.block.query() - - // Array... - if (Array.isArray(data)) { - // 1. get heights from cache - const missingFromCache = [] - - for (let i = 0; i < data.length; i++) { - const cachedBlock = this.__getBlockCache(data[i].blockId) - - if (cachedBlock) { - data[i].block = cachedBlock - } else { - missingFromCache.push({ - index: i, - blockId: data[i].blockId, - }) - } - } - - // 2. get missing heights from database - if (missingFromCache.length) { - const query = blockQuery - .select(blockQuery.id, blockQuery.height) - .from(blockQuery) - .where(blockQuery.id.in(missingFromCache.map(d => d.blockId))) - .group(blockQuery.id) - - const blocks = await this._findMany(query) - - for (const missing of missingFromCache) { - const block = blocks.find(item => item.id === missing.blockId) - if (block) { - data[missing.index].block = block - this.__setBlockCache(block) - } - } - } - - return data - } - - // Object... - if (data) { - const cachedBlock = this.__getBlockCache(data.blockId) - - if (cachedBlock) { - data.block = cachedBlock - } else { - const query = blockQuery - .select(blockQuery.id, blockQuery.height) - .from(blockQuery) - .where(blockQuery.id.equals(data.blockId)) - - data.block = await this._find(query) - - this.__setBlockCache(data.block) - } - } - - return data - } - - /** - * Tries to retrieve the height of the block from the cache - * @param {String} blockId - * @return {Object|null} - */ - __getBlockCache(blockId) { - const height = this.cache.get(`heights:${blockId}`) - - return height ? { height, id: blockId } : null - } - - /** - * Stores the height of the block on the cache - * @param {Object} block - * @param {String} block.id - * @param {Number} block.height - */ - __setBlockCache({ id, height }) { - this.cache.set(`heights:${id}`, height) - } - - /** - * Retrieves the publicKey of the address from the WalletManager in-memory data - * @param {String} senderId - * @return {String} - */ - __publicKeyFromSenderId(senderId) { - return database.walletManager.findByAddress(senderId).publicKey - } - - __orderBy(parameters) { - return parameters.orderBy - ? parameters.orderBy.split(':').map(p => p.toLowerCase()) - : ['timestamp', 'desc'] - } -} - -module.exports = new TransactionsRepository() diff --git a/packages/core-graphql/lib/repositories/utils/filter-query.js b/packages/core-graphql/lib/repositories/utils/filter-query.js deleted file mode 100644 index 5d7ce014e5..0000000000 --- a/packages/core-graphql/lib/repositories/utils/filter-query.js +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint no-prototype-builtins: "off" */ - -/** - * Create a "where" object for a sql query. - * @param {Object} parameters - * @param {Object} filters - * @return {Object} - */ -module.exports = (parameters, filters) => { - const where = [] - - if (filters.exact) { - for (const elem of filters.exact) { - if (typeof parameters[elem] !== 'undefined') { - where.push({ - column: elem, - method: 'equals', - value: parameters[elem], - }) - } - } - } - - if (filters.between) { - for (const elem of filters.between) { - if (!parameters[elem]) { - continue - } - - if (!parameters[elem].from && !parameters[elem].to) { - where.push({ - column: elem, - method: 'equals', - value: parameters[elem], - }) - } - - if (parameters[elem].from || parameters[elem].to) { - where[elem] = {} - - if (parameters[elem].from) { - where.push({ - column: elem, - method: 'gte', - value: parameters[elem].from, - }) - } - - if (parameters[elem].to) { - where.push({ - column: elem, - method: 'lte', - value: parameters[elem].to, - }) - } - } - } - } - - if (filters.wildcard) { - for (const elem of filters.wildcard) { - if (parameters[elem]) { - where.push({ - column: elem, - method: 'like', - value: `%${parameters[elem]}%`, - }) - } - } - } - - return where -} diff --git a/packages/core-graphql/lib/resolvers/index.js b/packages/core-graphql/lib/resolvers/index.js deleted file mode 100644 index cb1ddd8399..0000000000 --- a/packages/core-graphql/lib/resolvers/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const GraphQLTypes = require('graphql-tools-types') -const queries = require('./queries') -const Block = require('./relationship/block') -const Transaction = require('./relationship/transaction') -const Wallet = require('./relationship/wallet') - -/** - * Resolvers used by the executed schema when encountering a - * scalar or type. - * - * All of our scalars are based on graphql-tools-types which helps us with - * query standardization. - * - * We introduce relationships and queries for our own types, - * these hold the data processing responsibilities of the complete - * GraphQL query flow. - */ - -module.exports = { - JSON: GraphQLTypes.JSON({ name: 'Json' }), - Limit: GraphQLTypes.Int({ name: 'Limit', min: 1, max: 100 }), - Offset: GraphQLTypes.Int({ name: 'Offset', min: 0 }), - Address: GraphQLTypes.String({ - name: 'Address', - regex: /^[AaDd]{1}[0-9a-zA-Z]{33}/, - }), - Query: queries, - Block, - Transaction, - Wallet, -} diff --git a/packages/core-graphql/lib/resolvers/queries/block/block.js b/packages/core-graphql/lib/resolvers/queries/block/block.js deleted file mode 100644 index f6dbcd5967..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/block/block.js +++ /dev/null @@ -1,9 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) - -/** - * Get a single block from the database - * @return {Block} - */ -module.exports = (_, { id }) => database.db.blocks.findById(id) diff --git a/packages/core-graphql/lib/resolvers/queries/block/blocks.js b/packages/core-graphql/lib/resolvers/queries/block/blocks.js deleted file mode 100644 index 2caa207afe..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/block/blocks.js +++ /dev/null @@ -1,16 +0,0 @@ -const { formatOrderBy } = require('../../../helpers') -const { blocks: repository } = require('../../../repositories') - -/** - * Get multiple blocks from the database - * @return {Block[]} - */ -module.exports = async (_, args) => { - const { orderBy, filter } = args - - const order = formatOrderBy(orderBy, 'height:desc') - - const result = await repository.findAll({ ...filter, orderBy: order }) - - return result ? result.rows : [] -} diff --git a/packages/core-graphql/lib/resolvers/queries/index.js b/packages/core-graphql/lib/resolvers/queries/index.js deleted file mode 100644 index 204140ee6a..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/index.js +++ /dev/null @@ -1,18 +0,0 @@ -const block = require('./block/block') -const blocks = require('./block/blocks') -const transaction = require('./transaction/transaction') -const transactions = require('./transaction/transactions') -const wallet = require('./wallet/wallet') -const wallets = require('./wallet/wallets') - -/** - * Queries exposed by our GraphQL schema - */ -module.exports = { - block, - blocks, - transaction, - transactions, - wallet, - wallets, -} diff --git a/packages/core-graphql/lib/resolvers/queries/transaction/transaction.js b/packages/core-graphql/lib/resolvers/queries/transaction/transaction.js deleted file mode 100644 index 69fc196221..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/transaction/transaction.js +++ /dev/null @@ -1,9 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) - -/** - * Get a single transaction from the database - * @return {Transaction} - */ -module.exports = async (_, { id }) => database.db.transactions.findById(id) diff --git a/packages/core-graphql/lib/resolvers/queries/transaction/transactions.js b/packages/core-graphql/lib/resolvers/queries/transaction/transactions.js deleted file mode 100644 index 5e56d324b1..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/transaction/transactions.js +++ /dev/null @@ -1,14 +0,0 @@ -const { formatOrderBy } = require('../../../helpers') -const { transactions: repository } = require('../../../repositories') - -/** - * Get multiple transactions from the database - * @return {Transaction[]} - */ -module.exports = async (root, args) => { - const { orderBy, filter, limit } = args - const order = formatOrderBy(orderBy, 'timestamp:desc') - const result = await repository.findAll({ ...filter, orderBy: order, limit }) - const transactions = result ? result.rows : [] - return transactions -} diff --git a/packages/core-graphql/lib/resolvers/queries/wallet/wallet.js b/packages/core-graphql/lib/resolvers/queries/wallet/wallet.js deleted file mode 100644 index b7ae9d7d37..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/wallet/wallet.js +++ /dev/null @@ -1,12 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) - -/** - * Get a single wallet from the database - * @return {Wallet} - */ -module.exports = async (_, args) => { - const param = args.address || args.publicKey || args.username - return database.wallets.findById(param) -} diff --git a/packages/core-graphql/lib/resolvers/queries/wallet/wallets.js b/packages/core-graphql/lib/resolvers/queries/wallet/wallets.js deleted file mode 100644 index 2d1ff2b8d4..0000000000 --- a/packages/core-graphql/lib/resolvers/queries/wallet/wallets.js +++ /dev/null @@ -1,22 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) -const { formatOrderBy } = require('../../../helpers') - -/** - * Get multiple wallets from the database - * @return {Wallet[]} - */ -module.exports = async (_, args) => { - const { orderBy, filter, ...params } = args - - const order = formatOrderBy(orderBy, 'height:desc') - const result = filter && filter.vote - ? await database.wallets.findAllByVote(filter.vote, { - orderBy: order, - ...params, - }) - : await database.wallets.findAll({ orderBy: order, ...params }) - - return result ? result.rows : [] -} diff --git a/packages/core-graphql/lib/resolvers/relationship/block.js b/packages/core-graphql/lib/resolvers/relationship/block.js deleted file mode 100644 index dbd6e8b76c..0000000000 --- a/packages/core-graphql/lib/resolvers/relationship/block.js +++ /dev/null @@ -1,40 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) -const { formatOrderBy, unserializeTransactions } = require('../../helpers') - -/** - * Useful and common database operations with block data. - */ -module.exports = { - /** - * Get the transactions for a given block - * @param {Block}: block - * @param {Object}: args - * @return {Transaction[]} - */ - async transactions(block, args) { - const { orderBy, filter, ...params } = args - - const result = await database.transactions.findAll( - { - ...filter, - orderBy: formatOrderBy(orderBy, 'timestamp:DESC'), - ...params, - }, - false, - ) - const rows = result ? result.rows : [] - - return unserializeTransactions(rows) - }, - - /** - * Get the generator wallet for a given block - * @param {Block} block - * @return {Wallet} - */ - generator(block) { - return database.wallets.findById(block.generatorPublicKey) - }, -} diff --git a/packages/core-graphql/lib/resolvers/relationship/transaction.js b/packages/core-graphql/lib/resolvers/relationship/transaction.js deleted file mode 100644 index d0b5c7feb2..0000000000 --- a/packages/core-graphql/lib/resolvers/relationship/transaction.js +++ /dev/null @@ -1,33 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) - -/** - * Useful and common database operations with transaction data. - */ -module.exports = { - /** - * Get the block of a transaction - * @param {Transaction} transaction - * @return {Block} - */ - block: transaction => database.blocks.findById(transaction.blockId), - - /** - * Get the recipient of a transaction - * @param {Transaction} transaction - * @return {Wallet} - */ - recipient: transaction => (transaction.recipientId - ? database.wallets.findById(transaction.recipientId) - : []), - - /** - * Get the sender of a transaction - * @param {Transaction} transaction - * @return {Wallet} - */ - sender: transaction => (transaction.senderPublicKey - ? database.wallets.findById(transaction.senderPublicKey) - : []), -} diff --git a/packages/core-graphql/lib/resolvers/relationship/wallet.js b/packages/core-graphql/lib/resolvers/relationship/wallet.js deleted file mode 100644 index d266a216bd..0000000000 --- a/packages/core-graphql/lib/resolvers/relationship/wallet.js +++ /dev/null @@ -1,63 +0,0 @@ -const database = require('@arkecosystem/core-container').resolvePlugin( - 'database', -) -const { formatOrderBy, unserializeTransactions } = require('../../helpers') - -/** - * Useful and common database operations with wallet data. - */ -module.exports = { - /* - * Get the transactions for a given wallet. - * @param {Wallet} wallet - * @param {Object} args - * @return {Transaction[]} - */ - async transactions(wallet, args) { - const { orderBy, filter, ...params } = args - - const walletOr = database.createCondition('OR', [ - { - senderPublicKey: wallet.publicKey, - }, - { - recipientId: wallet.address, - }, - ]) - - const result = await database.transactions.findAll( - { - ...filter, - orderBy: formatOrderBy(orderBy, 'timestamp:DESC'), - ...walletOr, - ...params, - }, - false, - ) - const rows = result ? result.rows : [] - - return unserializeTransactions(rows) - }, - - /* - * Get the blocks generated for a given wallet. - * @param {Wallet} wallet - * @param {Object} args - * @return {Block[]} - */ - blocks(wallet, args) { - const { orderBy, ...params } = args - - params.generatorPublickKey = wallet.publicKey - - const result = database.blocks.findAll( - { - orderBy: formatOrderBy(orderBy, 'height:DESC'), - ...params, - }, - false, - ) - const rows = result ? result.rows : [] - return rows - }, -} diff --git a/packages/core-graphql/lib/schema.js b/packages/core-graphql/lib/schema.js deleted file mode 100644 index 20fde37a9a..0000000000 --- a/packages/core-graphql/lib/schema.js +++ /dev/null @@ -1,11 +0,0 @@ -const { ApolloServer } = require('apollo-server-hapi') -const resolvers = require('./resolvers') -const typeDefs = require('./defs') - -/** - * Schema used by the Apollo GraphQL plugin for the hapi.js server. - */ -module.exports = new ApolloServer({ - typeDefs, - resolvers, -}) diff --git a/packages/core-graphql/lib/server.js b/packages/core-graphql/lib/server.js deleted file mode 100644 index 0d7dcc2ff3..0000000000 --- a/packages/core-graphql/lib/server.js +++ /dev/null @@ -1,23 +0,0 @@ -const { createServer, mountServer } = require('@arkecosystem/core-http-utils') -const server = require('./schema') - -/** - * Create a new hapi.js server. - * @param {Object} config - * @return {Hapi.Server} - */ -module.exports = async config => { - const app = await createServer({ - host: config.host, - port: config.port, - }) - - await server.applyMiddleware({ - app, - path: config.path, - }) - - await server.installSubscriptionHandlers(app.listener) - - return mountServer('GraphQL', app) -} diff --git a/packages/core-graphql/package.json b/packages/core-graphql/package.json index 01ed48a395..4d04edd91f 100644 --- a/packages/core-graphql/package.json +++ b/packages/core-graphql/package.json @@ -1,35 +1,50 @@ { - "name": "@arkecosystem/core-graphql", - "description": "GraphQL Integration for Ark Core", - "version": "0.2.0", - "contributors": [ - "Lúcio Rubens " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "apollo-server-hapi": "^2.2.4", - "dayjs-ext": "^2.2.0", - "graphql-tools-types": "^1.1.26" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-graphql", + "description": "GraphQL Integration for Ark Core", + "version": "2.1.0", + "contributors": [ + "Lúcio Rubens " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "apollo-server-hapi": "^2.3.1", + "dayjs-ext": "^2.2.0", + "graphql-tools-types": "^1.1.26" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-graphql/src/apollo-server.ts b/packages/core-graphql/src/apollo-server.ts new file mode 100644 index 0000000000..0e76dfab3b --- /dev/null +++ b/packages/core-graphql/src/apollo-server.ts @@ -0,0 +1,11 @@ +import { ApolloServer } from "apollo-server-hapi"; +import { typeDefs } from "./defs"; +import { resolvers } from "./resolvers"; + +/** + * Schema used by the Apollo GraphQL plugin for the hapi.js server. + */ +export const apolloServer = new ApolloServer({ + typeDefs, + resolvers, +}); diff --git a/packages/core-graphql/src/defaults.ts b/packages/core-graphql/src/defaults.ts new file mode 100644 index 0000000000..a6ee749195 --- /dev/null +++ b/packages/core-graphql/src/defaults.ts @@ -0,0 +1,6 @@ +export const defaults = { + enabled: false, + host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", + port: process.env.CORE_GRAPHQL_PORT || 4005, + path: "/graphql", +}; diff --git a/packages/core-graphql/src/defs/index.ts b/packages/core-graphql/src/defs/index.ts new file mode 100644 index 0000000000..de6af74419 --- /dev/null +++ b/packages/core-graphql/src/defs/index.ts @@ -0,0 +1,9 @@ +import { inputs } from "./inputs"; +import { root } from "./root"; +import { types } from "./types"; + +export const typeDefs = ` + ${inputs} + ${root} + ${types} +`; diff --git a/packages/core-graphql/src/defs/inputs.ts b/packages/core-graphql/src/defs/inputs.ts new file mode 100644 index 0000000000..d6e087213c --- /dev/null +++ b/packages/core-graphql/src/defs/inputs.ts @@ -0,0 +1,44 @@ +export const inputs = ` + scalar JSON + scalar Limit + scalar Offset + scalar Address + + enum OrderDirection { + ASC + DESC + } + + enum TransactionType { + TRANSFER, + SECOND_SIGNATURE, + DELEGATE, + VOTE, + MULTI_SIGNATURE, + IPFS, + TIMELOCK_TRANSFER, + MULTI_PAYMENT, + DELEGATE_RESIGNATION + } + + input TransactionFilter { + fee: Float + blockId: String + senderPublicKey: String + recipientId: String + type: TransactionType + } + + input BlockFilter { + generatorPublicKey: String + } + + input WalletFilter { + vote: String + } + + input OrderByInput { + field: String + direction: OrderDirection + } +`; diff --git a/packages/core-graphql/src/defs/root.ts b/packages/core-graphql/src/defs/root.ts new file mode 100644 index 0000000000..25b2243ec1 --- /dev/null +++ b/packages/core-graphql/src/defs/root.ts @@ -0,0 +1,14 @@ +export const root = ` + type Query { + block(id: String): Block + blocks(limit: Limit, offset: Offset, orderBy: OrderByInput, filter: BlockFilter): [Block] + transaction(id: String): Transaction + transactions(limit: Limit, orderBy: OrderByInput, filter: TransactionFilter): [Transaction] + wallet(address: Address, publicKey: String, username: String): Wallet + wallets(limit: Limit, orderBy: OrderByInput, filter: WalletFilter): [Wallet] + } + + schema { + query: Query + } +`; diff --git a/packages/core-graphql/src/defs/types.ts b/packages/core-graphql/src/defs/types.ts new file mode 100644 index 0000000000..22db2aa611 --- /dev/null +++ b/packages/core-graphql/src/defs/types.ts @@ -0,0 +1,49 @@ +export const types = ` + type Block { + id: String + version: Int! + timestamp: Int! + previousBlock: String + height: Int! + numberOfTransactions: Int! + totalAmount: Float + totalFee: Float + reward: Float + payloadLength: Int! + payloadHash: String + generatorPublicKey: String + blockSignature: String + transactions(limit: Limit, offset: Offset, orderBy: OrderByInput, filter: TransactionFilter): [Transaction] + generator: Wallet + } + + type Transaction { + id: String + version: Int! + timestamp: Int! + senderPublicKey: String + recipientId: Address + type: Int! + vendorField: String + amount: Float + fee: Float + signature: String + block: Block + recipient: Wallet + sender: Wallet + } + + type Wallet { + address: Address + publicKey: String + secondPublicKey: String + vote: String + username: String + balance: Float + voteBalance: Float + producedBlocks: Float + missedBlocks: Float + transactions(limit: Limit, offset: Offset, orderBy: OrderByInput): [Transaction] + blocks(limit: Limit, offset: Offset, orderBy: OrderByInput): [Block] + } +`; diff --git a/packages/core-graphql/src/helpers/format-orderBy.ts b/packages/core-graphql/src/helpers/format-orderBy.ts new file mode 100644 index 0000000000..61b32556e9 --- /dev/null +++ b/packages/core-graphql/src/helpers/format-orderBy.ts @@ -0,0 +1,15 @@ +/** + * Logic used by our orderBy input + * @param {Object} parameter + * @param {String} defaultValue + * @return {String} + */ +export function formatOrderBy(parameter, defaultValue) { + let order; + + if (parameter) { + order = `${parameter.field}:${parameter.direction.toLowerCase()}`; + } + + return order || defaultValue; +} diff --git a/packages/core-graphql/src/helpers/index.ts b/packages/core-graphql/src/helpers/index.ts new file mode 100644 index 0000000000..d667ee29c0 --- /dev/null +++ b/packages/core-graphql/src/helpers/index.ts @@ -0,0 +1,4 @@ +import { formatOrderBy } from "./format-orderBy"; +import { unserializeTransactions } from "./unserialize-transactions"; + +export { formatOrderBy, unserializeTransactions }; diff --git a/packages/core-graphql/src/helpers/unserialize-transactions.ts b/packages/core-graphql/src/helpers/unserialize-transactions.ts new file mode 100644 index 0000000000..545edc2f43 --- /dev/null +++ b/packages/core-graphql/src/helpers/unserialize-transactions.ts @@ -0,0 +1,21 @@ +import { models } from "@arkecosystem/crypto"; +const { Transaction } = models; + +/** + * Deserialize multiple transactions + */ +export async function unserializeTransactions(data) { + const deserialize = buffer => { + const serialized = Buffer.from(buffer).toString("hex"); + return Transaction.deserialize(serialized); + }; + + if (Array.isArray(data)) { + return data.reduce((total, value, key) => { + total.push(deserialize(value.serialized)); + + return total; + }, []); + } + return deserialize(data); +} diff --git a/packages/core-graphql/src/index.ts b/packages/core-graphql/src/index.ts new file mode 100644 index 0000000000..867988e21d --- /dev/null +++ b/packages/core-graphql/src/index.ts @@ -0,0 +1,28 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { startServer } from "./server"; + +/** + * The struct used by the plugin manager. + * @type {Object} + */ +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "graphql", + async register(container: Container.IContainer, options) { + if (!options.enabled) { + container.resolvePlugin("logger").info("GraphQL API is disabled :grey_exclamation:"); + return; + } + + return startServer(options); + }, + async deregister(container: Container.IContainer, options) { + if (options.enabled) { + container.resolvePlugin("logger").info("Stopping GraphQL API"); + + return container.resolvePlugin("graphql").stop(); + } + }, +}; diff --git a/packages/core-graphql/src/repositories/blocks.ts b/packages/core-graphql/src/repositories/blocks.ts new file mode 100644 index 0000000000..f1e12c66a2 --- /dev/null +++ b/packages/core-graphql/src/repositories/blocks.ts @@ -0,0 +1,133 @@ +import { Repository } from "./repository"; +import { buildFilterQuery } from "./utils/filter-query"; + +class BlocksRepository extends Repository { + /** + * Get all blocks for the given parameters. + * @param {Object} parameters + * @return {Object} + */ + public async findAll(parameters: any = {}) { + const selectQuery = this.query.select().from(this.query); + const countQuery = this._makeEstimateQuery(); + + const applyConditions = queries => { + const conditions = Object.entries(this._formatConditions(parameters)); + + if (conditions.length) { + const first = conditions.shift(); + + for (const item of queries) { + item.where(this.query[first[0]].equals(first[1])); + + for (const condition of conditions) { + item.and(this.query[condition[0]].equals(condition[1])); + } + } + } + }; + + applyConditions([selectQuery, countQuery]); + + return this._findManyWithCount(selectQuery, countQuery, { + limit: parameters.limit || 100, + offset: parameters.offset || 0, + orderBy: this.__orderBy(parameters), + }); + } + + /** + * Get all blocks for the given generator. + * @param {String} generatorPublicKey + * @param {Object} paginator + * @return {Object} + */ + public async findAllByGenerator(generatorPublicKey, paginator) { + return this.findAll({ ...{ generatorPublicKey }, ...paginator }); + } + + /** + * Get a block. + * @param {Number} id + * @return {Object} + */ + public async findById(id) { + const query = this.query + .select() + .from(this.query) + .where(this.query.id.equals(id)); + + return this._find(query); + } + + /** + * Get the last block for the given generator. + * TODO is this right? + * @param {String} generatorPublicKey + * @return {Object} + */ + public async findLastByPublicKey(generatorPublicKey) { + const query = this.query + .select(this.query.id, this.query.timestamp) + .from(this.query) + .where(this.query.generator_public_key.equals(generatorPublicKey)) + .order(this.query.height.desc); + + return this._find(query); + } + + /** + * Search all blocks. + * @param {Object} parameters + * @return {Object} + */ + public async search(parameters) { + const selectQuery = this.query.select().from(this.query); + const countQuery = this._makeEstimateQuery(); + + const applyConditions = queries => { + const conditions = buildFilterQuery(this._formatConditions(parameters), { + exact: ["id", "version", "previous_block", "payload_hash", "generator_public_key", "block_signature"], + between: [ + "timestamp", + "height", + "number_of_transactions", + "total_amount", + "total_fee", + "reward", + "payload_length", + ], + }); + + if (conditions.length) { + const first = conditions.shift(); + + for (const item of queries) { + item.where(this.query[first.column][first.method](first.value)); + + for (const condition of conditions) { + item.and(this.query[condition.column][condition.method](condition.value)); + } + } + } + }; + + applyConditions([selectQuery, countQuery]); + + return this._findManyWithCount(selectQuery, countQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: this.__orderBy(parameters), + }); + } + + public getModel() { + return (this.databaseService.connection as any).models.block; + } + + public __orderBy(parameters) { + return parameters.orderBy ? parameters.orderBy.split(":").map(p => p.toLowerCase()) : ["height", "desc"]; + } +} + +export const blockRepository = new BlocksRepository(); diff --git a/packages/core-graphql/src/repositories/index.ts b/packages/core-graphql/src/repositories/index.ts new file mode 100644 index 0000000000..d747df1177 --- /dev/null +++ b/packages/core-graphql/src/repositories/index.ts @@ -0,0 +1,4 @@ +import { blockRepository } from "./blocks"; +import { transactionRepository } from "./transactions"; + +export { blockRepository, transactionRepository }; diff --git a/packages/core-graphql/src/repositories/repository.ts b/packages/core-graphql/src/repositories/repository.ts new file mode 100644 index 0000000000..21499a3fd0 --- /dev/null +++ b/packages/core-graphql/src/repositories/repository.ts @@ -0,0 +1,65 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, TransactionPool } from "@arkecosystem/core-interfaces"; + +export abstract class Repository { + public databaseService = app.resolvePlugin("database"); + public transactionPool = app.resolvePlugin("transactionPool"); + public cache = this.databaseService.cache; + public model = this.getModel(); + public query = this.model.query(); + + public abstract getModel(): any; + + public async _find(query) { + return (this.databaseService.connection as any).query.oneOrNone(query.toQuery()); + } + + public async _findMany(query) { + return (this.databaseService.connection as any).query.manyOrNone(query.toQuery()); + } + + public async _findManyWithCount(selectQuery, countQuery, { limit, offset, orderBy }) { + const { count } = await this._find(countQuery); + + selectQuery + .order(this.query[orderBy[0]][orderBy[1]]) + .offset(offset) + .limit(limit); + + limit = 100; + offset = 0; + const rows = await this._findMany(selectQuery); + return { + rows, + count: +count, + }; + } + + public _makeCountQuery() { + return this.query.select("count(*) AS count").from(this.query); + } + + public _makeEstimateQuery() { + return this.query.select("count(*) AS count").from(`${this.model.getTable()} TABLESAMPLE SYSTEM (100)`); + } + + public _formatConditions(parameters) { + const columns = this.model.getColumnSet().columns.map(column => ({ + name: column.name, + prop: column.prop || column.name, + })); + + const columnNames = columns.map(column => column.name); + const columnProps = columns.map(column => column.prop); + + const filter = args => args.filter(arg => columnNames.includes(arg) || columnProps.includes(arg)); + + return filter(Object.keys(parameters)).reduce((items, item) => { + const columnName = columns.find(column => column.prop === item).name; + + items[columnName] = parameters[item]; + + return items; + }, {}); + } +} diff --git a/packages/core-graphql/src/repositories/transactions.ts b/packages/core-graphql/src/repositories/transactions.ts new file mode 100644 index 0000000000..602eeb0c3f --- /dev/null +++ b/packages/core-graphql/src/repositories/transactions.ts @@ -0,0 +1,439 @@ +import { constants, slots } from "@arkecosystem/crypto"; +import dayjs from "dayjs-ext"; + +import { Repository } from "./repository"; +import { buildFilterQuery } from "./utils/filter-query"; + +const { TransactionTypes } = constants; + +class TransactionsRepository extends Repository { + /** + * Get all transactions. + * @param {Object} params + * @return {Object} + */ + public async findAll(parameters: any = {}) { + const selectQuery = this.query.select().from(this.query); + const countQuery = this._makeEstimateQuery(); + + if (parameters.senderId) { + const senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId); + + if (!senderPublicKey) { + return { rows: [], count: 0 }; + } + + parameters.senderPublicKey = senderPublicKey; + } + + if (parameters.type) { + parameters.type = TransactionTypes[parameters.type]; + } + + const applyConditions = queries => { + const conditions = Object.entries(this._formatConditions(parameters)); + + if (conditions.length) { + const first = conditions.shift(); + + for (const item of queries) { + item.where(this.query[first[0]].equals(first[1])); + + for (const condition of conditions) { + item.and(this.query[condition[0]].equals(condition[1])); + } + } + } + }; + + applyConditions([selectQuery, countQuery]); + + const results = await this._findManyWithCount(selectQuery, countQuery, { + limit: parameters.limit || 100, + offset: parameters.offset || 0, + orderBy: this.__orderBy(parameters), + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + return results; + } + + /** + * Get all transactions (LEGACY, for V1 only). + * @param {Object} params + * @return {Object} + */ + public async findAllLegacy(parameters: any = {}) { + const selectQuery = this.query.select(this.query.block_id, this.query.serialized).from(this.query); + const countQuery = this._makeEstimateQuery(); + + if (parameters.senderId) { + parameters.senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId); + } + + const applyConditions = queries => { + const conditions = Object.entries(this._formatConditions(parameters)); + + if (conditions.length) { + const first = conditions.shift(); + + for (const item of queries) { + item.where(this.query[first[0]].equals(first[1])); + + for (const [key, value] of conditions) { + item.or(this.query[key].equals(value)); + } + } + } + }; + + applyConditions([selectQuery, countQuery]); + + const results = await this._findManyWithCount(selectQuery, countQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: this.__orderBy(parameters), + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + /** + * Get all transactions for the given Wallet object. + * @param {Wallet} wallet + * @param {Object} parameters + * @return {Object} + */ + public async findAllByWallet(wallet, parameters: any = {}) { + const selectQuery = this.query.select(this.query.block_id, this.query.serialized).from(this.query); + const countQuery = this._makeEstimateQuery(); + + const applyConditions = queries => { + for (const item of queries) { + item.where(this.query.sender_public_key.equals(wallet.publicKey)).or( + this.query.recipient_id.equals(wallet.address), + ); + } + }; + + applyConditions([selectQuery, countQuery]); + + const results = await this._findManyWithCount(selectQuery, countQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: this.__orderBy(parameters), + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + /** + * Get all transactions for the given sender public key. + * @param {String} senderPublicKey + * @param {Object} parameters + * @return {Object} + */ + public async findAllBySender(senderPublicKey, parameters = {}) { + return this.findAll({ ...{ senderPublicKey }, ...parameters }); + } + + /** + * Get all transactions for the given recipient address. + * @param {String} recipientId + * @param {Object} parameters + * @return {Object} + */ + public async findAllByRecipient(recipientId, parameters = {}) { + return this.findAll({ ...{ recipientId }, ...parameters }); + } + + /** + * Get all vote transactions for the given sender public key. + * TODO rename to findAllVotesBySender or not? + * @param {String} senderPublicKey + * @param {Object} parameters + * @return {Object} + */ + public async allVotesBySender(senderPublicKey, parameters = {}) { + return this.findAll({ + ...{ senderPublicKey, type: TransactionTypes.Vote }, + ...parameters, + }); + } + + /** + * Get all transactions for the given block. + * @param {Number} blockId + * @param {Object} parameters + * @return {Object} + */ + public async findAllByBlock(blockId, parameters = {}) { + return this.findAll({ ...{ blockId }, ...parameters }); + } + + /** + * Get all transactions for the given type. + * @param {Number} type + * @param {Object} parameters + * @return {Object} + */ + public async findAllByType(type, parameters = {}) { + return this.findAll({ ...{ type }, ...parameters }); + } + + /** + * Get a transaction. + * @param {Number} id + * @return {Object} + */ + public async findById(id) { + const query = this.query + .select(this.query.block_id, this.query.serialized) + .from(this.query) + .where(this.query.id.equals(id)); + + const transaction = await this._find(query); + + return this.__mapBlocksToTransactions(transaction); + } + + /** + * Get a transactions for the given type and id. + * @param {Number} type + * @param {Number} id + * @return {Object} + */ + public async findByTypeAndId(type, id) { + const query = this.query + .select(this.query.block_id, this.query.serialized) + .from(this.query) + .where(this.query.id.equals(id).and(this.query.type.equals(type))); + + const transaction = await this._find(query); + + return this.__mapBlocksToTransactions(transaction); + } + + /** + * Get transactions for the given ids. + * @param {Array} ids + * @return {Object} + */ + public async findByIds(ids) { + const query = this.query + .select(this.query.block_id, this.query.serialized) + .from(this.query) + .where(this.query.id.in(ids)); + + return this._findMany(query); + } + + /** + * Get all transactions that have a vendor field. + * @return {Object} + */ + public async findWithVendorField() { + const query = this.query + .select(this.query.block_id, this.query.serialized) + .from(this.query) + .where(this.query.vendor_field_hex.isNotNull()); + + const rows = await this._findMany(query); + + return this.__mapBlocksToTransactions(rows); + } + + /** + * Calculates min, max and average fee statistics based on transactions table + * @return {Object} + */ + public async getFeeStatistics() { + const query = this.query + .select( + this.query.type, + this.query.fee.min("minFee"), + this.query.fee.max("maxFee"), + this.query.fee.avg("avgFee"), + this.query.timestamp.max("timestamp"), + ) + .from(this.query) + .where( + this.query.timestamp.gte( + slots.getTime( + dayjs() + .subtract(30, "day") + .valueOf(), + ), + ), + ) + .and(this.query.fee.gte(this.transactionPool.options.dynamicFees.minFeeBroadcast)) + .group(this.query.type) + .order('"timestamp" DESC'); + + return this._findMany(query); + } + + /** + * Search all transactions. + * + * @param {Object} params + * @return {Object} + */ + public async search(parameters) { + const selectQuery = this.query.select().from(this.query); + const countQuery = this._makeEstimateQuery(); + + if (parameters.senderId) { + const senderPublicKey = this.__publicKeyFromSenderId(parameters.senderId); + + if (senderPublicKey) { + parameters.senderPublicKey = senderPublicKey; + } + } + + const applyConditions = queries => { + const conditions = buildFilterQuery(this._formatConditions(parameters), { + exact: ["id", "block_id", "type", "version", "sender_public_key", "recipient_id"], + between: ["timestamp", "amount", "fee"], + wildcard: ["vendor_field_hex"], + }); + + if (conditions.length) { + const first = conditions.shift(); + + for (const item of queries) { + item.where(this.query[first.column][first.method](first.value)); + + for (const condition of conditions) { + item.and(this.query[condition.column][condition.method](condition.value)); + } + } + } + }; + + applyConditions([selectQuery, countQuery]); + + const results = await this._findManyWithCount(selectQuery, countQuery, { + limit: parameters.limit, + offset: parameters.offset, + orderBy: this.__orderBy(parameters), + }); + + results.rows = await this.__mapBlocksToTransactions(results.rows); + + return results; + } + + public getModel() { + return (this.databaseService.connection as any).models.transaction; + } + + /** + * [__mapBlocksToTransactions description] + * @param {Array|Object} data + * @return {Object} + */ + public async __mapBlocksToTransactions(data) { + const blockQuery = (this.databaseService.connection as any).models.block.query(); + + // Array... + if (Array.isArray(data)) { + // 1. get heights from cache + const missingFromCache = []; + + for (let i = 0; i < data.length; i++) { + const cachedBlock = this.__getBlockCache(data[i].blockId); + + if (cachedBlock) { + data[i].block = cachedBlock; + } else { + missingFromCache.push({ + index: i, + blockId: data[i].blockId, + }); + } + } + + // 2. get missing heights from database + if (missingFromCache.length) { + const query = blockQuery + .select(blockQuery.id, blockQuery.height) + .from(blockQuery) + .where(blockQuery.id.in(missingFromCache.map(d => d.blockId))) + .group(blockQuery.id); + + const blocks = await this._findMany(query); + + for (const missing of missingFromCache) { + const block = blocks.find(item => item.id === missing.blockId); + if (block) { + data[missing.index].block = block; + this.__setBlockCache(block); + } + } + } + + return data; + } + + // Object... + if (data) { + const cachedBlock = this.__getBlockCache(data.blockId); + + if (cachedBlock) { + data.block = cachedBlock; + } else { + const query = blockQuery + .select(blockQuery.id, blockQuery.height) + .from(blockQuery) + .where(blockQuery.id.equals(data.blockId)); + + data.block = await this._find(query); + + this.__setBlockCache(data.block); + } + } + + return data; + } + + /** + * Tries to retrieve the height of the block from the cache + * @param {String} blockId + * @return {Object|null} + */ + public __getBlockCache(blockId) { + const height = this.cache.get(`heights:${blockId}`); + + return height ? { height, id: blockId } : null; + } + + /** + * Stores the height of the block on the cache + * @param {Object} block + * @param {String} block.id + * @param {Number} block.height + */ + public __setBlockCache({ id, height }) { + this.cache.set(`heights:${id}`, height); + } + + /** + * Retrieves the publicKey of the address from the WalletManager in-memory data + * @param {String} senderId + * @return {String} + */ + public __publicKeyFromSenderId(senderId) { + return this.databaseService.walletManager.findByAddress(senderId).publicKey; + } + + public __orderBy(parameters) { + return parameters.orderBy ? parameters.orderBy.split(":").map(p => p.toLowerCase()) : ["timestamp", "desc"]; + } +} + +export const transactionRepository = new TransactionsRepository(); diff --git a/packages/core-graphql/src/repositories/utils/filter-query.ts b/packages/core-graphql/src/repositories/utils/filter-query.ts new file mode 100644 index 0000000000..3c41c8cd19 --- /dev/null +++ b/packages/core-graphql/src/repositories/utils/filter-query.ts @@ -0,0 +1,71 @@ +/** + * Create a "where" object for a sql query. + * @param {Object} parameters + * @param {Object} filters + * @return {Object} + */ +export function buildFilterQuery(parameters, filters) { + const where = []; + + if (filters.exact) { + for (const elem of filters.exact) { + if (typeof parameters[elem] !== "undefined") { + where.push({ + column: elem, + method: "equals", + value: parameters[elem], + }); + } + } + } + + if (filters.between) { + for (const elem of filters.between) { + if (!parameters[elem]) { + continue; + } + + if (!parameters[elem].from && !parameters[elem].to) { + where.push({ + column: elem, + method: "equals", + value: parameters[elem], + }); + } + + if (parameters[elem].from || parameters[elem].to) { + where[elem] = {}; + + if (parameters[elem].from) { + where.push({ + column: elem, + method: "gte", + value: parameters[elem].from, + }); + } + + if (parameters[elem].to) { + where.push({ + column: elem, + method: "lte", + value: parameters[elem].to, + }); + } + } + } + } + + if (filters.wildcard) { + for (const elem of filters.wildcard) { + if (parameters[elem]) { + where.push({ + column: elem, + method: "like", + value: `%${parameters[elem]}%`, + }); + } + } + } + + return where; +} diff --git a/packages/core-graphql/src/resolvers/index.ts b/packages/core-graphql/src/resolvers/index.ts new file mode 100644 index 0000000000..91626b41ea --- /dev/null +++ b/packages/core-graphql/src/resolvers/index.ts @@ -0,0 +1,31 @@ +import GraphQLTypes from "graphql-tools-types"; +import * as queries from "./queries"; +import { Block } from "./relationship/block"; +import { Transaction } from "./relationship/transaction"; +import { Wallet } from "./relationship/wallet"; + +/** + * Resolvers used by the executed schema when encountering a + * scalar or type. + * + * All of our scalars are based on graphql-tools-types which helps us with + * query standardization. + * + * We introduce relationships and queries for our own types, + * these hold the data processing responsibilities of the complete + * GraphQL query flow. + */ + +export const resolvers = { + JSON: GraphQLTypes.JSON({ name: "Json" }), + Limit: GraphQLTypes.Int({ name: "Limit", min: 1, max: 100 }), + Offset: GraphQLTypes.Int({ name: "Offset", min: 0 }), + Address: GraphQLTypes.String({ + name: "Address", + regex: /^[AaDd]{1}[0-9a-zA-Z]{33}/, + }), + Query: queries, + Block, + Transaction, + Wallet, +}; diff --git a/packages/core-graphql/src/resolvers/queries/block/block.ts b/packages/core-graphql/src/resolvers/queries/block/block.ts new file mode 100644 index 0000000000..d6e1fcbc0f --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/block/block.ts @@ -0,0 +1,10 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; + +/** + * Get a single block from the database + * @return {Block} + */ +export async function block(_, { id }) { + return app.resolvePlugin("database").connection.blocksRepository.findById(id); +} diff --git a/packages/core-graphql/src/resolvers/queries/block/blocks.ts b/packages/core-graphql/src/resolvers/queries/block/blocks.ts new file mode 100644 index 0000000000..1b4db4aff3 --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/block/blocks.ts @@ -0,0 +1,16 @@ +import { formatOrderBy } from "../../../helpers"; +import { blockRepository } from "../../../repositories"; + +/** + * Get multiple blocks from the database + * @return {Block[]} + */ +export async function blocks(_, args: any) { + const { orderBy, filter } = args; + + const order = formatOrderBy(orderBy, "height:desc"); + + const result = await blockRepository.findAll({ ...filter, orderBy: order }); + + return result ? result.rows : []; +} diff --git a/packages/core-graphql/src/resolvers/queries/index.ts b/packages/core-graphql/src/resolvers/queries/index.ts new file mode 100644 index 0000000000..e89c5b4cfb --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/index.ts @@ -0,0 +1,8 @@ +import { block } from "./block/block"; +import { blocks } from "./block/blocks"; +import { transaction } from "./transaction/transaction"; +import { transactions } from "./transaction/transactions"; +import { wallet } from "./wallet/wallet"; +import { wallets } from "./wallet/wallets"; + +export { block, blocks, transaction, transactions, wallet, wallets }; diff --git a/packages/core-graphql/src/resolvers/queries/transaction/transaction.ts b/packages/core-graphql/src/resolvers/queries/transaction/transaction.ts new file mode 100644 index 0000000000..e2c3b8d8c3 --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/transaction/transaction.ts @@ -0,0 +1,10 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; + +/** + * Get a single transaction from the database + * @return {Transaction} + */ +export async function transaction(_, { id }) { + return app.resolvePlugin("database").connection.transactionsRepository.findById(id); +} diff --git a/packages/core-graphql/src/resolvers/queries/transaction/transactions.ts b/packages/core-graphql/src/resolvers/queries/transaction/transactions.ts new file mode 100644 index 0000000000..9c6b993b2b --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/transaction/transactions.ts @@ -0,0 +1,13 @@ +import { formatOrderBy } from "../../../helpers"; +import { transactionRepository } from "../../../repositories"; + +/** + * Get multiple transactions from the database + * @return {Transaction[]} + */ +export async function transactions(_, args: any) { + const { orderBy, filter, limit } = args; + const order = formatOrderBy(orderBy, "timestamp:desc"); + const result = await transactionRepository.findAll({ ...filter, orderBy: order, limit }); + return result ? result.rows : []; +} diff --git a/packages/core-graphql/src/resolvers/queries/wallet/wallet.ts b/packages/core-graphql/src/resolvers/queries/wallet/wallet.ts new file mode 100644 index 0000000000..81feab2dd7 --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/wallet/wallet.ts @@ -0,0 +1,13 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; + +const databaseService = app.resolvePlugin("database"); + +/** + * Get a single wallet from the database + * @return {Wallet} + */ +export async function wallet(_, args: any) { + const param = args.address || args.publicKey || args.username; + return databaseService.wallets.findById(param); +} diff --git a/packages/core-graphql/src/resolvers/queries/wallet/wallets.ts b/packages/core-graphql/src/resolvers/queries/wallet/wallets.ts new file mode 100644 index 0000000000..0ae2f10353 --- /dev/null +++ b/packages/core-graphql/src/resolvers/queries/wallet/wallets.ts @@ -0,0 +1,24 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { formatOrderBy } from "../../../helpers"; + +const databaseService = app.resolvePlugin("database"); + +/** + * Get multiple wallets from the database + * @return {Wallet[]} + */ +export async function wallets(_, args: any) { + const { orderBy, filter, ...params } = args; + + const order = formatOrderBy(orderBy, "height:desc"); + const result = + filter && filter.vote + ? await databaseService.wallets.findAllByVote(filter.vote, { + orderBy: order, + ...params, + }) + : await databaseService.wallets.findAll({ orderBy: order, ...params }); + + return result ? result.rows : []; +} diff --git a/packages/core-graphql/src/resolvers/relationship/block.ts b/packages/core-graphql/src/resolvers/relationship/block.ts new file mode 100644 index 0000000000..7457dfd2ba --- /dev/null +++ b/packages/core-graphql/src/resolvers/relationship/block.ts @@ -0,0 +1,43 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { formatOrderBy, unserializeTransactions } from "../../helpers"; + +const databaseService = app.resolvePlugin("database"); + +/** + * Useful and common database operations with block data. + */ +export const Block = { + /** + * Get the transactions for a given block + * @param {Block}: block + * @param {Object}: args + * @return {Transaction[]} + */ + async transactions(block, args) { + const { orderBy, filter, ...params } = args; + + /* .findAll() method never existed on the TransactionRepository in core-database-postgres. This code would've blown chunks + const result = await database.connection.transactionsRepository.findAll( + { + ...filter, + orderBy: formatOrderBy(orderBy, "timestamp:DESC"), + ...params, + }, + false, + );*/ + const result = null; + const rows = result ? result.rows : []; + + return unserializeTransactions(rows); + }, + + /** + * Get the generator wallet for a given block + * @param {Block} block + * @return {Wallet} + */ + generator(block) { + return databaseService.wallets.findById(block.generatorPublicKey); + }, +}; diff --git a/packages/core-graphql/src/resolvers/relationship/transaction.ts b/packages/core-graphql/src/resolvers/relationship/transaction.ts new file mode 100644 index 0000000000..5999c67b99 --- /dev/null +++ b/packages/core-graphql/src/resolvers/relationship/transaction.ts @@ -0,0 +1,30 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; + +const databaseService = app.resolvePlugin("database"); + +/** + * Useful and common database operations with transaction data. + */ +export const Transaction = { + /** + * Get the block of a transaction + * @param {Transaction} transaction + * @return {Block} + */ + block: transaction => databaseService.connection.blocksRepository.findById(transaction.blockId), + + /** + * Get the recipient of a transaction + * @param {Transaction} transaction + * @return {Wallet} + */ + recipient: transaction => (transaction.recipientId ? databaseService.wallets.findById(transaction.recipientId) : []), + + /** + * Get the sender of a transaction + * @param {Transaction} transaction + * @return {Wallet} + */ + sender: transaction => (transaction.senderPublicKey ? databaseService.wallets.findById(transaction.senderPublicKey) : []), +}; diff --git a/packages/core-graphql/src/resolvers/relationship/wallet.ts b/packages/core-graphql/src/resolvers/relationship/wallet.ts new file mode 100644 index 0000000000..b0216a4722 --- /dev/null +++ b/packages/core-graphql/src/resolvers/relationship/wallet.ts @@ -0,0 +1,69 @@ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { formatOrderBy, unserializeTransactions } from "../../helpers"; + +const databaseService = app.resolvePlugin("database"); + +/** + * Useful and common database operations with wallet data. + */ +export const Wallet = { + /* + * Get the transactions for a given wallet. + * @param {Wallet} wallet + * @param {Object} args + * @return {Transaction[]} + */ + async transactions(wallet, args) { + const { orderBy, filter, ...params } = args; + + const walletOr = (databaseService.connection as any).createCondition("OR", [ + { + senderPublicKey: wallet.publicKey, + }, + { + recipientId: wallet.address, + }, + ]); + + /* TODO .findAll() method never existed on the TransactionRepository in core-database-postgres. This code would've blown chunks + const result = await databaseService.connection.transactionsRepository.findAll( + { + ...filter, + orderBy: formatOrderBy(orderBy, "timestamp:DESC"), + ...walletOr, + ...params, + }, + false, + );*/ + const result = null; + const rows = result ? result.rows : []; + + return unserializeTransactions(rows); + }, + + /* + * Get the blocks generated for a given wallet. + * @param {Wallet} wallet + * @param {Object} args + * @return {Block[]} + */ + blocks(wallet, args) { + const { orderBy, ...params } = args; + + params.generatorPublickKey = wallet.publicKey; + + + /* TODO: .findAll() method never existed on the TransactionRepository in core-database-postgres. This code would've blown chunks + const result = databaseService.connection.blocksRepository.findAll( + { + orderBy: formatOrderBy(orderBy, "height:DESC"), + ...params, + }, + false, + );*/ + const result = null; + const rows = result ? result.rows : []; + return rows; + }, +}; diff --git a/packages/core-graphql/src/server.ts b/packages/core-graphql/src/server.ts new file mode 100644 index 0000000000..59cdba2f97 --- /dev/null +++ b/packages/core-graphql/src/server.ts @@ -0,0 +1,18 @@ +import { createServer, mountServer } from "@arkecosystem/core-http-utils"; +import { apolloServer } from "./apollo-server"; + +export async function startServer(config) { + const app = await createServer({ + host: config.host, + port: config.port, + }); + + await apolloServer.applyMiddleware({ + app, + path: config.path, + }); + + await apolloServer.installSubscriptionHandlers(app.listener); + + return mountServer("GraphQL", app); +} diff --git a/packages/core-graphql/tsconfig.json b/packages/core-graphql/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-graphql/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-http-utils/CHANGELOG.md b/packages/core-http-utils/CHANGELOG.md deleted file mode 100644 index 5d0bb1d1fe..0000000000 --- a/packages/core-http-utils/CHANGELOG.md +++ /dev/null @@ -1,25 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- cors-headers plugin for better CORS handling -- transaction payload size validation plugin - -### Changed - -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.0 - 2018-10-25 - -### Added - -- initial release diff --git a/packages/core-http-utils/LICENSE b/packages/core-http-utils/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-http-utils/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-http-utils/README.md b/packages/core-http-utils/README.md index 12390ba2f9..7295c0d50a 100644 --- a/packages/core-http-utils/README.md +++ b/packages/core-http-utils/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-http-utils/__tests__/__support__/mocks/core-container.js b/packages/core-http-utils/__tests__/__support__/mocks/core-container.js deleted file mode 100644 index 03fbe49a7b..0000000000 --- a/packages/core-http-utils/__tests__/__support__/mocks/core-container.js +++ /dev/null @@ -1,14 +0,0 @@ -jest.mock('@arkecosystem/core-container', () => ({ - resolvePlugin: name => { - if (name === 'logger') { - return { - info: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - debug: jest.fn(), - } - } - - return {} - }, -})) diff --git a/packages/core-http-utils/__tests__/__support__/mocks/core-container.ts b/packages/core-http-utils/__tests__/__support__/mocks/core-container.ts new file mode 100644 index 0000000000..35ba7deb18 --- /dev/null +++ b/packages/core-http-utils/__tests__/__support__/mocks/core-container.ts @@ -0,0 +1,18 @@ +jest.mock("@arkecosystem/core-container", () => { + return { + app: { + resolvePlugin: name => { + if (name === "logger") { + return { + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + debug: jest.fn(), + }; + } + + return {}; + }, + }, + }; +}); diff --git a/packages/core-http-utils/__tests__/plugins/content-type.test.js b/packages/core-http-utils/__tests__/plugins/content-type.test.js deleted file mode 100644 index 68f790eb60..0000000000 --- a/packages/core-http-utils/__tests__/plugins/content-type.test.js +++ /dev/null @@ -1,49 +0,0 @@ -require('../__support__/mocks/core-container') // before all so that the mock is used -const axios = require('axios') -const createServer = require('../../lib/server/create') -const mountServer = require('../../lib/server/mount') -const contentType = require('../../lib/plugins/content-type') - -let server -beforeAll(async () => { - server = await createServer({ - host: '0.0.0.0', - port: 3000, - }) - - await server.register({ plugin: contentType }) - - server.route({ - method: 'GET', - path: '/', - handler: (request, h) => 'Hello!', - }) - - await mountServer('Dummy', server) -}) - -afterAll(async () => { - await server.stop() -}) - -describe('Plugins - Content-Type', () => { - describe('GET /', () => { - it('should return code 200', async () => { - const response = await axios.get('http://0.0.0.0:3000/', { - headers: { 'Content-Type': 'application/json' }, - }) - - expect(response.status).toBe(200) - }) - - it('should return code 415', async () => { - try { - await axios.get('http://0.0.0.0:3000/', { - headers: { 'Content-Type': 'application/text' }, - }) - } catch (e) { - expect(e.response.status).toBe(415) - } - }) - }) -}) diff --git a/packages/core-http-utils/__tests__/plugins/content-type.test.ts b/packages/core-http-utils/__tests__/plugins/content-type.test.ts new file mode 100644 index 0000000000..7f9a3df0fc --- /dev/null +++ b/packages/core-http-utils/__tests__/plugins/content-type.test.ts @@ -0,0 +1,50 @@ +import "../__support__/mocks/core-container"; + +import axios from "axios"; +import { contentType } from "../../src/plugins/content-type"; +import { createServer } from "../../src/server/create"; +import { mountServer } from "../../src/server/mount"; + +let server; +beforeAll(async () => { + server = await createServer({ + host: "0.0.0.0", + port: 3000, + }); + + await server.register({ plugin: contentType }); + + server.route({ + method: "GET", + path: "/", + handler: (request, h) => "Hello!", + }); + + await mountServer("Dummy", server); +}); + +afterAll(async () => { + await server.stop(); +}); + +describe("Plugins - Content-Type", () => { + describe("GET /", () => { + it("should return code 200", async () => { + const response = await axios.get("http://0.0.0.0:3000/", { + headers: { "Content-Type": "application/json" }, + }); + + expect(response.status).toBe(200); + }); + + it("should return code 415", async () => { + try { + await axios.get("http://0.0.0.0:3000/", { + headers: { "Content-Type": "application/text" }, + }); + } catch (e) { + expect(e.response.status).toBe(415); + } + }); + }); +}); diff --git a/packages/core-http-utils/jest.config.js b/packages/core-http-utils/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-http-utils/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-http-utils/lib/index.js b/packages/core-http-utils/lib/index.js deleted file mode 100644 index f74cf387a8..0000000000 --- a/packages/core-http-utils/lib/index.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - createServer: require('./server/create'), - createSecureServer: require('./server/create-secure'), - monitorServer: require('./server/monitor'), - mountServer: require('./server/mount'), - plugins: { - contentType: require('./plugins/content-type'), - corsHeaders: require('./plugins/cors-headers'), - transactionPayload: require('./plugins/transaction-payload'), - whitelist: require('./plugins/whitelist'), - }, -} diff --git a/packages/core-http-utils/lib/plugins/content-type.js b/packages/core-http-utils/lib/plugins/content-type.js deleted file mode 100644 index 74bdca7db8..0000000000 --- a/packages/core-http-utils/lib/plugins/content-type.js +++ /dev/null @@ -1,22 +0,0 @@ -const Boom = require('boom') - -const register = async (server, options) => { - server.ext({ - type: 'onPreHandler', - async method(request, h) { - const contentType = request.headers['content-type'] - - if (contentType !== 'application/json') { - return Boom.unsupportedMediaType() - } - - return h.continue - }, - }) -} - -exports.plugin = { - name: 'content-type', - version: '0.1.0', - register, -} diff --git a/packages/core-http-utils/lib/plugins/cors-headers.js b/packages/core-http-utils/lib/plugins/cors-headers.js deleted file mode 100644 index 9817ddee74..0000000000 --- a/packages/core-http-utils/lib/plugins/cors-headers.js +++ /dev/null @@ -1,42 +0,0 @@ -// Based on https://github.com/gr2m/hapi-cors-headers which was never updated to support hapi.js 17 - -const register = async (server, options) => { - server.ext({ - type: 'onPreResponse', - method: (request, h) => { - if (!request.headers.origin) { - return h.continue - } - - const response = request.response.isBoom - ? request.response.output - : request.response - response.headers['access-control-allow-origin'] = request.headers.origin - response.headers['access-control-allow-credentials'] = 'true' - - if (request.method !== 'options') { - return h.continue - } - - response.statusCode = 200 - response.headers['access-control-expose-headers'] = 'content-type, content-length, etag' - response.headers['access-control-max-age'] = options.maxAge || 60 * 10 - - if (request.headers['access-control-request-headers']) { - response.headers['access-control-allow-headers'] = request.headers['access-control-request-headers'] - } - - if (request.headers['access-control-request-method']) { - response.headers['access-control-allow-methods'] = request.headers['access-control-request-method'] - } - - return h.continue - }, - }) -} - -exports.plugin = { - name: 'cors-headers', - version: '0.1.0', - register, -} diff --git a/packages/core-http-utils/lib/plugins/transaction-payload.js b/packages/core-http-utils/lib/plugins/transaction-payload.js deleted file mode 100644 index 223aef676b..0000000000 --- a/packages/core-http-utils/lib/plugins/transaction-payload.js +++ /dev/null @@ -1,54 +0,0 @@ -const Boom = require('boom') -const app = require('@arkecosystem/core-container') - -const register = async (server, options) => { - server.ext({ - type: 'onPostAuth', - async method(request, h) { - const route = options.routes.find(item => item.path === request.path) - - if (!route) { - return h.continue - } - - if (route.method.toLowerCase() !== request.method.toLowerCase()) { - return h.continue - } - - const transactionPool = app.resolveOptions('transactionPool') - - if (!transactionPool) { - return h.continue - } - - // NOTE: this will only trigger if the JSON content-type header is not - // present. This will be avoided by the "content-type.js" plugin in the - // future which is currently disabled due to v1 still being on mainnet. - if (!request.payload.transactions) { - return Boom.badRequest() - } - - const transactionsCount = request.payload.transactions.length - const maxTransactionsPerRequest = - transactionPool.maxTransactionsPerRequest - - if (transactionsCount > maxTransactionsPerRequest) { - return Boom.entityTooLarge( - `Received ${transactionsCount} transactions. Only ${maxTransactionsPerRequest} are allowed per request.`, - { - allowed: maxTransactionsPerRequest, - received: transactionsCount, - }, - ) - } - - return h.continue - }, - }) -} - -exports.plugin = { - name: 'transaction-payload', - version: '0.1.0', - register, -} diff --git a/packages/core-http-utils/lib/plugins/whitelist.js b/packages/core-http-utils/lib/plugins/whitelist.js deleted file mode 100644 index 33cf8ac2d6..0000000000 --- a/packages/core-http-utils/lib/plugins/whitelist.js +++ /dev/null @@ -1,34 +0,0 @@ -const Boom = require('boom') -const mm = require('micromatch') -const logger = require('@arkecosystem/core-container').resolvePlugin('logger') - -const register = async (server, options) => { - server.ext({ - type: 'onRequest', - async method(request, h) { - const remoteAddress = request.info.remoteAddress - - if (Array.isArray(options.whitelist)) { - for (const ip of options.whitelist) { - if (mm.isMatch(remoteAddress, ip)) { - return h.continue - } - } - } - - logger.warn( - `${remoteAddress} tried to access the ${ - options.name - } without being whitelisted :warning:`, - ) - - return Boom.forbidden() - }, - }) -} - -exports.plugin = { - name: 'whitelist', - version: '0.1.0', - register, -} diff --git a/packages/core-http-utils/lib/server/create-secure.js b/packages/core-http-utils/lib/server/create-secure.js deleted file mode 100644 index 6609283afe..0000000000 --- a/packages/core-http-utils/lib/server/create-secure.js +++ /dev/null @@ -1,14 +0,0 @@ -const fs = require('fs') -const expandHomeDir = require('expand-home-dir') -const createServer = require('./create') - -module.exports = async (options, callback, secure) => { - options.host = secure.host - options.port = secure.port - options.tls = { - key: fs.readFileSync(expandHomeDir(secure.key)), - cert: fs.readFileSync(expandHomeDir(secure.cert)), - } - - return createServer(options, callback, secure) -} diff --git a/packages/core-http-utils/lib/server/create.js b/packages/core-http-utils/lib/server/create.js deleted file mode 100644 index 44600929c5..0000000000 --- a/packages/core-http-utils/lib/server/create.js +++ /dev/null @@ -1,23 +0,0 @@ -const Hapi = require('hapi') -const registerMonitor = require('./monitor') - -module.exports = async (options, callback) => { - const server = new Hapi.Server(options) - - await server.register([require('vision'), require('inert'), require('lout')]) - - await server.register({ - plugin: require('hapi-trailing-slash'), - options: { method: 'remove' }, - }) - - if (callback) { - await callback(server) - } - - if (process.env.NODE_ENV === 'test') { - await registerMonitor(server) - } - - return server -} diff --git a/packages/core-http-utils/lib/server/monitor.js b/packages/core-http-utils/lib/server/monitor.js deleted file mode 100644 index 62207b97f2..0000000000 --- a/packages/core-http-utils/lib/server/monitor.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = async server => server.register({ - plugin: require('good'), - options: { - reporters: { - console: [ - { - module: 'good-squeeze', - name: 'Squeeze', - args: [{ log: '*', response: '*', request: '*' }], - }, - { - module: 'good-console', - }, - 'stdout', - ], - }, - }, -}) diff --git a/packages/core-http-utils/lib/server/mount.js b/packages/core-http-utils/lib/server/mount.js deleted file mode 100644 index 4c8d3db6c6..0000000000 --- a/packages/core-http-utils/lib/server/mount.js +++ /dev/null @@ -1,15 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') - -module.exports = async (name, server) => { - try { - await server.start() - - logger.info(`${name} Server running at: ${server.info.uri}`) - - return server - } catch (error) { - app.forceExit(`Could not start ${name} Server!`, error) - } -} diff --git a/packages/core-http-utils/package.json b/packages/core-http-utils/package.json index 9965a726f2..b15f95cc2a 100644 --- a/packages/core-http-utils/package.json +++ b/packages/core-http-utils/package.json @@ -1,41 +1,60 @@ { - "name": "@arkecosystem/core-http-utils", - "description": "Http Utilities for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "boom": "^7.3.0", - "expand-home-dir": "^0.0.3", - "good": "^8.1.1", - "good-console": "^7.1.0", - "good-squeeze": "^5.1.0", - "hapi": "^17.8.1", - "hapi-trailing-slash": "^3.0.1", - "inert": "^5.1.2", - "lout": "^11.1.0", - "micromatch": "^3.1.10", - "vision": "^5.4.3" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - }, - "devDependencies": { - "axios": "^0.18.0" - } + "name": "@arkecosystem/core-http-utils", + "description": "Http Utilities for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-container": "^2.1.0", + "@types/boom": "^7.2.1", + "@types/micromatch": "^3.1.0", + "boom": "^7.3.0", + "expand-home-dir": "^0.0.3", + "good": "^8.1.1", + "good-console": "^7.1.0", + "good-squeeze": "^5.1.0", + "hapi": "^17.8.1", + "hapi-trailing-slash": "^3.0.1", + "inert": "^5.1.2", + "lout": "^11.1.0", + "micromatch": "^3.1.10", + "vision": "^5.4.4" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + }, + "devDependencies": { + "@types/hapi": "^17.8.2", + "@types/inert": "^5.1.2", + "@types/vision": "^5.3.5", + "axios": "^0.18.0" + } } diff --git a/packages/core-http-utils/src/index.ts b/packages/core-http-utils/src/index.ts new file mode 100644 index 0000000000..0faa3c3dc9 --- /dev/null +++ b/packages/core-http-utils/src/index.ts @@ -0,0 +1,7 @@ +import * as plugins from "./plugins"; +import { createServer } from "./server/create"; +import { createSecureServer } from "./server/create-secure"; +import { monitorServer } from "./server/monitor"; +import { mountServer } from "./server/mount"; + +export { createServer, createSecureServer, monitorServer, mountServer, plugins }; diff --git a/packages/core-http-utils/src/plugins/content-type.ts b/packages/core-http-utils/src/plugins/content-type.ts new file mode 100644 index 0000000000..1beacd97c5 --- /dev/null +++ b/packages/core-http-utils/src/plugins/content-type.ts @@ -0,0 +1,20 @@ +import Boom from "boom"; + +export const contentType = { + name: "content-type", + version: "0.1.0", + register(server, options) { + server.ext({ + type: "onPreHandler", + async method(request, h) { + const header = request.headers["content-type"]; + + if (header !== "application/json") { + return Boom.unsupportedMediaType(); + } + + return h.continue; + }, + }); + }, +}; diff --git a/packages/core-http-utils/src/plugins/cors-headers.ts b/packages/core-http-utils/src/plugins/cors-headers.ts new file mode 100644 index 0000000000..5ba0c1482e --- /dev/null +++ b/packages/core-http-utils/src/plugins/cors-headers.ts @@ -0,0 +1,37 @@ +export const corsHeaders = { + name: "cors-headers", + version: "0.1.0", + register(server, options) { + server.ext({ + type: "onPreResponse", + method: (request, h) => { + if (!request.headers.origin) { + return h.continue; + } + + const response = request.response.isBoom ? request.response.output : request.response; + response.headers["access-control-allow-origin"] = request.headers.origin; + response.headers["access-control-allow-credentials"] = "true"; + + if (request.method !== "options") { + return h.continue; + } + + response.statusCode = 200; + response.headers["access-control-expose-headers"] = "content-type, content-length, etag"; + response.headers["access-control-max-age"] = options.maxAge || 60 * 10; + + if (request.headers["access-control-request-headers"]) { + response.headers["access-control-allow-headers"] = + request.headers["access-control-request-headers"]; + } + + if (request.headers["access-control-request-method"]) { + response.headers["access-control-allow-methods"] = request.headers["access-control-request-method"]; + } + + return h.continue; + }, + }); + }, +}; diff --git a/packages/core-http-utils/src/plugins/index.ts b/packages/core-http-utils/src/plugins/index.ts new file mode 100644 index 0000000000..51ec0fcb1c --- /dev/null +++ b/packages/core-http-utils/src/plugins/index.ts @@ -0,0 +1,6 @@ +import { contentType } from "./content-type"; +import { corsHeaders } from "./cors-headers"; +import { transactionPayload } from "./transaction-payload"; +import { whitelist } from "./whitelist"; + +export { contentType, corsHeaders, transactionPayload, whitelist }; diff --git a/packages/core-http-utils/src/plugins/transaction-payload.ts b/packages/core-http-utils/src/plugins/transaction-payload.ts new file mode 100644 index 0000000000..04db3aa482 --- /dev/null +++ b/packages/core-http-utils/src/plugins/transaction-payload.ts @@ -0,0 +1,51 @@ +import { app } from "@arkecosystem/core-container"; +import Boom from "boom"; + +export const transactionPayload = { + name: "transaction-payload", + version: "0.1.0", + register(server, options) { + server.ext({ + type: "onPostAuth", + async method(request, h) { + const route = options.routes.find(item => item.path === request.path); + + if (!route) { + return h.continue; + } + + if (route.method.toLowerCase() !== request.method.toLowerCase()) { + return h.continue; + } + + const transactionPool = app.resolveOptions("transactionPool"); + + if (!transactionPool) { + return h.continue; + } + + // NOTE: this will only trigger if the JSON content-type header is not + // present. This will be avoided by the "content-type.js" plugin in the + // future which is currently disabled due to v1 still being on mainnet. + if (!request.payload.transactions) { + return Boom.badRequest(); + } + + const transactionsCount = request.payload.transactions.length; + const maxTransactionsPerRequest = transactionPool.maxTransactionsPerRequest; + + if (transactionsCount > maxTransactionsPerRequest) { + return Boom.entityTooLarge( + `Received ${transactionsCount} transactions. Only ${maxTransactionsPerRequest} are allowed per request.`, + { + allowed: maxTransactionsPerRequest, + received: transactionsCount, + }, + ); + } + + return h.continue; + }, + }); + }, +}; diff --git a/packages/core-http-utils/src/plugins/whitelist.ts b/packages/core-http-utils/src/plugins/whitelist.ts new file mode 100644 index 0000000000..9bed9351aa --- /dev/null +++ b/packages/core-http-utils/src/plugins/whitelist.ts @@ -0,0 +1,34 @@ +import { app } from "@arkecosystem/core-container"; +import Boom from "boom"; +import mm from "micromatch"; + +export const whitelist = { + name: "whitelist", + version: "0.1.0", + register(server, options) { + server.ext({ + type: "onRequest", + async method(request, h) { + const remoteAddress = request.info.remoteAddress; + + if (Array.isArray(options.whitelist)) { + for (const ip of options.whitelist) { + try { + if (mm.isMatch(remoteAddress, ip)) { + return h.continue; + } + } catch { + return Boom.forbidden(); + } + } + } + + app.resolvePlugin("logger").warn( + `${remoteAddress} tried to access the ${options.name} without being whitelisted :warning:`, + ); + + return Boom.forbidden(); + }, + }); + }, +}; diff --git a/packages/core-http-utils/src/server/create-secure.ts b/packages/core-http-utils/src/server/create-secure.ts new file mode 100644 index 0000000000..29f47097e8 --- /dev/null +++ b/packages/core-http-utils/src/server/create-secure.ts @@ -0,0 +1,16 @@ +import expandHomeDir from "expand-home-dir"; +import { readFileSync } from "fs"; +import { createServer } from "./create"; + +async function createSecureServer(options, callback, secure) { + options.host = secure.host; + options.port = secure.port; + options.tls = { + key: readFileSync(expandHomeDir(secure.key)), + cert: readFileSync(expandHomeDir(secure.cert)), + }; + + return createServer(options, callback); +} + +export { createSecureServer }; diff --git a/packages/core-http-utils/src/server/create.ts b/packages/core-http-utils/src/server/create.ts new file mode 100644 index 0000000000..3025af1071 --- /dev/null +++ b/packages/core-http-utils/src/server/create.ts @@ -0,0 +1,25 @@ +import Hapi from "hapi"; +import { monitorServer } from "./monitor"; + +async function createServer(options, callback: any = null) { + const server = new Hapi.Server(options); + + await server.register([require("vision"), require("inert"), require("lout")]); + + await server.register({ + plugin: require("hapi-trailing-slash"), + options: { method: "remove" }, + }); + + if (callback) { + await callback(server); + } + + if (process.env.NODE_ENV === "test") { + await monitorServer(server); + } + + return server; +} + +export { createServer }; diff --git a/packages/core-http-utils/src/server/monitor.ts b/packages/core-http-utils/src/server/monitor.ts new file mode 100644 index 0000000000..08499fce71 --- /dev/null +++ b/packages/core-http-utils/src/server/monitor.ts @@ -0,0 +1,22 @@ +async function monitorServer(server) { + return server.register({ + plugin: require("good"), + options: { + reporters: { + console: [ + { + module: "good-squeeze", + name: "Squeeze", + args: [{ log: "*", response: "*", request: "*" }], + }, + { + module: "good-console", + }, + "stdout", + ], + }, + }, + }); +} + +export { monitorServer }; diff --git a/packages/core-http-utils/src/server/mount.ts b/packages/core-http-utils/src/server/mount.ts new file mode 100644 index 0000000000..a2b08e6ae0 --- /dev/null +++ b/packages/core-http-utils/src/server/mount.ts @@ -0,0 +1,15 @@ +import { app } from "@arkecosystem/core-container"; + +async function mountServer(name, server) { + try { + await server.start(); + + app.resolvePlugin("logger").info(`${name} Server running at: ${server.info.uri}`); + + return server; + } catch (error) { + app.forceExit(`Could not start ${name} Server!`, error); + } +} + +export { mountServer }; diff --git a/packages/core-http-utils/tsconfig.json b/packages/core-http-utils/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-http-utils/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-interfaces/package.json b/packages/core-interfaces/package.json new file mode 100644 index 0000000000..64f4e0b3f7 --- /dev/null +++ b/packages/core-interfaces/package.json @@ -0,0 +1,39 @@ +{ + "name": "@arkecosystem/core-interfaces", + "description": "Interface types for essential Ark core modules", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Kristjan Košič ", + "Brian Faust ", + "Alex Barnsley " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' --fix", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/crypto": "^2.1.0", + "awilix": "^4.0.1", + "eventemitter3": "^3.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + } +} diff --git a/packages/core-interfaces/src/core-blockchain/blockchain.ts b/packages/core-interfaces/src/core-blockchain/blockchain.ts new file mode 100644 index 0000000000..17607f938d --- /dev/null +++ b/packages/core-interfaces/src/core-blockchain/blockchain.ts @@ -0,0 +1,205 @@ +import { models } from "@arkecosystem/crypto"; +import { IMonitor } from "../core-p2p"; +import { ITransactionPool } from "../core-transaction-pool"; +import { IStateStorage } from "./state-storage"; + +export interface IBlockchain { + /** + * Get the state of the blockchain. + * @return {IStateStorage} + */ + readonly state: IStateStorage; + + /** + * Get the network (p2p) interface. + */ + readonly p2p: IMonitor; + + /** + * Get the transaction handler. + * @return {ITransactionPool} + */ + readonly transactionPool: ITransactionPool; + + /** + * Get the database connection. + * @return {ConnectionInterface} + */ + readonly database: any; + + dispatch(event: any): any; + + /** + * Start the blockchain and wait for it to be ready. + * @return {void} + */ + start(skipStartedCheck?: boolean): Promise; + + stop(): Promise; + + checkNetwork(): void; + + /** + * Update network status. + * @return {void} + */ + updateNetworkStatus(): Promise; + + /** + * Rebuild N blocks in the blockchain. + * @param {Number} nblocks + * @return {void} + */ + rebuild(nblocks?: number): void; + + /** + * Reset the state of the blockchain. + * @return {void} + */ + resetState(): void; + + /** + * Clear and stop the queue. + * @return {void} + */ + clearAndStopQueue(): void; + + /** + * Hand the given transactions to the transaction handler. + * @param {Array} transactions + * @return {void} + */ + postTransactions(transactions: models.Transaction[]): Promise; + + /** + * Push a block to the process queue. + * @param {Block} block + * @return {void} + */ + handleIncomingBlock(block: models.Block): void; + + /** + * Rollback all blocks up to the previous round. + * @return {void} + */ + rollbackCurrentRound(): Promise; + + /** + * Remove N number of blocks. + * @param {Number} nblocks + * @return {void} + */ + removeBlocks(nblocks: number): Promise; + + /** + * Remove the top blocks from database. + * NOTE: Only used when trying to restore database integrity. + * @param {Number} count + * @return {void} + */ + removeTopBlocks(count: any): Promise; + + /** + * Hande a block during a rebuild. + * NOTE: We should be sure this is fail safe (ie callback() is being called only ONCE) + * @param {Block} block + * @param {Function} callback + * @return {Object} + */ + rebuildBlock(block: models.Block, callback: any): Promise; + + /** + * Process the given block. + * NOTE: We should be sure this is fail safe (ie callback() is being called only ONCE) + * @param {Block} block + * @param {Function} callback + * @return {(Function|void)} + */ + processBlock(block: models.Block, callback: any): Promise; + + /** + * Called by forger to wake up and sync with the network. + * It clears the checkLaterTimeout if set. + * @param {Number} blockSize + * @param {Boolean} forForging + * @return {Object} + */ + forceWakeup(): void; + + /** + * Fork the chain at the given block. + * @param {Block} block + * @returns {void} + */ + forkBlock(block: models.Block): void; + + /** + * Get unconfirmed transactions for the specified block size. + * @param {Number} blockSize + * @param {Boolean} forForging + * @return {Object} + */ + getUnconfirmedTransactions( + blockSize: any, + ): { + transactions: any[]; + poolSize: any; + count: number; + }; + + /** + * Determine if the blockchain is synced. + * @param {Block} [block=getLastBlock()] block + * @return {Boolean} + */ + isSynced(block?: models.Block): boolean; + + /** + * Determine if the blockchain is synced after a rebuild. + * @param {Block} block + * @return {Boolean} + */ + isRebuildSynced(block?: models.Block): boolean; + + /** + * Get the last block of the blockchain. + * @return {Object} + */ + getLastBlock(): models.Block; + + /** + * Get the last height of the blockchain. + * @return {Object} + */ + getLastHeight(): any; + + /** + * Get the last downloaded block of the blockchain. + * @return {Object} + */ + getLastDownloadedBlock(): { data: models.IBlockData }; + + /** + * Get the block ping. + * @return {Object} + */ + getBlockPing(): any; + + /** + * Ping a block. + * @return {Object} + */ + pingBlock(incomingBlock: models.IBlockData): any; + + /** + * Push ping block. + * @return {Object} + */ + pushPingBlock(block: models.IBlockData): void; + + /** + * Get the list of events that are available. + * @return {Array} + */ + getEvents(): string[]; +} diff --git a/packages/core-interfaces/src/core-blockchain/index.ts b/packages/core-interfaces/src/core-blockchain/index.ts new file mode 100644 index 0000000000..45a3b126ef --- /dev/null +++ b/packages/core-interfaces/src/core-blockchain/index.ts @@ -0,0 +1,2 @@ +export * from "./blockchain"; +export * from "./state-storage"; diff --git a/packages/core-interfaces/src/core-blockchain/state-storage.ts b/packages/core-interfaces/src/core-blockchain/state-storage.ts new file mode 100644 index 0000000000..e7c908f317 --- /dev/null +++ b/packages/core-interfaces/src/core-blockchain/state-storage.ts @@ -0,0 +1,78 @@ +import { models } from "@arkecosystem/crypto"; + +export interface IStateStorage { + reset(): void; + + /** + * Clear last blocks. + */ + clear(): void; + + /** + * Clear wakeup timeout. + */ + clearWakeUpTimeout(): void; + + /** + * Get the last block. + */ + getLastBlock(): models.Block | null; + + /** + * Sets the last block. + * @returns {void} + */ + setLastBlock(block: models.Block): void; + + /** + * Get the last blocks. + * @returns {Array} + */ + getLastBlocks(): models.Block[]; + + /** + * Get the last block ids. + * @returns {Array} + */ + getLastBlockIds(): string[]; + + /** + * Get last blocks in the given height range in ascending order. + * @param {Number} start + * @param {Number} end + */ + getLastBlocksByHeight(start: number, end?: number): models.IBlockData[]; + + /** + * Get common blocks for the given IDs. + * @returns {Array} + */ + getCommonBlocks(ids: string[]): models.IBlockData[]; + + /** + * Cache the ids of the given transactions. + */ + cacheTransactions( + transactions: models.ITransactionData[], + ): { [key in "added" | "notAdded"]: models.ITransactionData[] }; + + /** + * Remove the given transaction ids from the cache. + */ + removeCachedTransactionIds(transactionIds: string[]): void; + + /** + * Get cached transaction ids. + */ + getCachedTransactionIds(): string[]; + + /** + * Ping a block. + */ + pingBlock(incomingBlock: models.IBlockData): boolean; + + /** + * Push ping block + */ + pushPingBlock(block: models.IBlockData): void; +} diff --git a/packages/core-interfaces/src/core-container/container.ts b/packages/core-interfaces/src/core-container/container.ts new file mode 100644 index 0000000000..4490a16c16 --- /dev/null +++ b/packages/core-interfaces/src/core-container/container.ts @@ -0,0 +1,97 @@ +import { Resolver } from "awilix"; + +export interface PluginDescriptor { + alias: string; + pkg: any; + defaults?: any; + extends?: string; + register(container: IContainer, options?: any): Promise; + deregister?(container: IContainer, options?: any): Promise; +} + +export interface PluginConfig { + name: string; + version: string; + options: { [key: string]: any }; + plugin: T; +} + +export interface IContainer { + silentShutdown: boolean; + + isReady: boolean; + + setUp(version: string, variables: any, options?: any): Promise; + + getConfig(): any; + /** + * Tear down the app. + * @return {Promise} + */ + tearDown(): Promise; + + /** + * Add a new registration. + */ + register(name: string, resolver: Resolver): this; + + /** + * Resolve a registration. + * @param {string} key + * @return {Object} + * @throws {Error} + */ + resolve(key: any): T; + + /** + * Resolve a plugin. + * @param {string} key + * @return {Object} + * @throws {Error} + */ + resolvePlugin(key: any): T; + + /** + * Resolve the options of a plugin. Available before a plugin mounts. + * @param {string} key + * @return {Object} + * @throws {Error} + */ + resolveOptions(key: any): any; + + /** + * Determine if the given registration exists. + * @param {String} key + * @return {Boolean} + */ + has(key: any): boolean; + + /** + * Force the container to exit and print the given message and associated error. + */ + forceExit(message: string, error?: Error): void; + + /** + * Exit the container with the given exitCode, message and associated error. + */ + exit(exitCode: number, message: string, error?: Error): void; + + /** + * Get the application git commit hash. + * @throws {String} + */ + getHashid(): string; + + /** + * Get the application version. + * @throws {String} + */ + getVersion(): string; + + /** + * Set the application version. + * @param {String} version + * @return {void} + */ + setVersion(version: any): void; +} diff --git a/packages/core-interfaces/src/core-container/index.ts b/packages/core-interfaces/src/core-container/index.ts new file mode 100644 index 0000000000..c5b5e8f38f --- /dev/null +++ b/packages/core-interfaces/src/core-container/index.ts @@ -0,0 +1 @@ +export * from "./container"; diff --git a/packages/core-interfaces/src/core-database/business-repository/delegates-business-repository.ts b/packages/core-interfaces/src/core-database/business-repository/delegates-business-repository.ts new file mode 100644 index 0000000000..48e65c523c --- /dev/null +++ b/packages/core-interfaces/src/core-database/business-repository/delegates-business-repository.ts @@ -0,0 +1,15 @@ +import { models } from "@arkecosystem/crypto"; +import { IParameters } from "./parameters"; + +export interface IDelegatesBusinessRepository { + + getLocalDelegates(): models.Wallet[]; + + findAll(params?: IParameters): { count: number, rows: models.Wallet[] } + + search(params: T): { count: number, rows: models.Wallet[] } + + findById(id: string): models.Wallet; + + getActiveAtHeight(height: number): Promise> +} diff --git a/packages/core-interfaces/src/core-database/business-repository/index.ts b/packages/core-interfaces/src/core-database/business-repository/index.ts new file mode 100644 index 0000000000..a30abaafdc --- /dev/null +++ b/packages/core-interfaces/src/core-database/business-repository/index.ts @@ -0,0 +1,3 @@ +export * from "./wallets-business-repository"; +export * from "./delegates-business-repository" +export * from "./parameters"; diff --git a/packages/core-interfaces/src/core-database/business-repository/parameters.ts b/packages/core-interfaces/src/core-database/business-repository/parameters.ts new file mode 100644 index 0000000000..27df1df680 --- /dev/null +++ b/packages/core-interfaces/src/core-database/business-repository/parameters.ts @@ -0,0 +1,6 @@ +export interface IParameters { + offset?: number; + limit?: number; + orderBy?: string, + [key: string]: object | number | string | boolean +} diff --git a/packages/core-interfaces/src/core-database/business-repository/wallets-business-repository.ts b/packages/core-interfaces/src/core-database/business-repository/wallets-business-repository.ts new file mode 100644 index 0000000000..ad5f6f05e8 --- /dev/null +++ b/packages/core-interfaces/src/core-database/business-repository/wallets-business-repository.ts @@ -0,0 +1,20 @@ +import { models } from "@arkecosystem/crypto"; +import { IParameters } from "./parameters"; + +export interface IWalletsBusinessRepository { + + all(): models.Wallet[]; + + findAll(params?: IParameters): { count: number, rows: models.Wallet[] } + + findAllByVote(publicKey: string, params?: IParameters): { count: number, rows: models.Wallet[] }; + + findById(id: string): models.Wallet; + + count(): number; + + top(params?: IParameters): { count: number, rows: models.Wallet[] } + + search(params: T): { count: number, rows: models.Wallet[] } + +} diff --git a/packages/core-interfaces/src/core-database/database-connection.ts b/packages/core-interfaces/src/core-database/database-connection.ts new file mode 100644 index 0000000000..7ce5688307 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-connection.ts @@ -0,0 +1,41 @@ +import { IBlocksRepository } from "./database-repository"; +import { IRoundsRepository } from "./database-repository"; +import { ITransactionsRepository } from "./database-repository"; +import { IWalletsRepository } from "./database-repository"; + +import { models } from "@arkecosystem/crypto"; + +export interface IDatabaseConnection { + + options: any; + + blocksRepository : IBlocksRepository; + walletsRepository: IWalletsRepository; + roundsRepository: IRoundsRepository; + transactionsRepository: ITransactionsRepository; + + make(): Promise; + + connect(): Promise; + + disconnect(): Promise; + + buildWallets(height: number) : Promise; + + /* We have these methods on the connection since they rely on transactions, which is a DB specific detail + Keep DB specifics away from the service layer + */ + saveWallets(wallets: any[], force?: boolean) : Promise; + + saveBlock(block: models.Block): Promise; + + deleteBlock(block: models.Block): Promise; + + enqueueDeleteBlock(block: models.Block); + + enqueueDeleteRound(height: number); + + enqueueSaveBlock(block: models.Block); + + commitQueuedQueries(); +} diff --git a/packages/core-interfaces/src/core-database/database-repository/blocks-repository.ts b/packages/core-interfaces/src/core-database/database-repository/blocks-repository.ts new file mode 100644 index 0000000000..a1e377b542 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-repository/blocks-repository.ts @@ -0,0 +1,56 @@ +import { Bignum } from "@arkecosystem/crypto"; +import { IRepository } from "./repository"; + +export interface IBlocksRepository extends IRepository { + + /** + * Find a block by its ID. + */ + findById(id: string): Promise; + + /** + * Count the number of records in the database. + */ + count(): Promise; + + /** + * Get all of the common blocks from the database. + */ + common(ids: string[]): Promise + + /** + * Get all of the blocks within the given height range and order them by height. + */ + heightRange(start: number, end: number): Promise; + + /** + * Get the last created block from the database. + */ + latest(): Promise; + + /** + * Get the most recently created blocks ids from the database. + * @return {Promise} + */ + recent(count: number): Promise; + + /** + * Get statistics about all blocks from the database. + */ + statistics(): Promise<{ + numberOfTransactions: number, + totalFee: Bignum, + totalAmount: Bignum, + count: number + }>; + + /** + * Get top count blocks + */ + top(count: number): Promise; + + /** + * Delete the block from the database. + */ + delete(id: string): Promise; +} diff --git a/packages/core-interfaces/src/core-database/database-repository/index.ts b/packages/core-interfaces/src/core-database/database-repository/index.ts new file mode 100644 index 0000000000..104bbed884 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-repository/index.ts @@ -0,0 +1,5 @@ +export * from "./transactions-repository"; +export * from "./rounds-repository"; +export * from "./wallets-repository"; +export * from "./blocks-repository"; +export * from "./repository"; diff --git a/packages/core-interfaces/src/core-database/database-repository/repository.ts b/packages/core-interfaces/src/core-database/database-repository/repository.ts new file mode 100644 index 0000000000..20dbcfcc82 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-repository/repository.ts @@ -0,0 +1,11 @@ +export interface IRepository { + + estimate() : Promise; + + truncate(): Promise; + + insert(item: any | any[]) : Promise; + + update(item: any | any[]) : Promise; + +} diff --git a/packages/core-interfaces/src/core-database/database-repository/rounds-repository.ts b/packages/core-interfaces/src/core-database/database-repository/rounds-repository.ts new file mode 100644 index 0000000000..468ca091c1 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-repository/rounds-repository.ts @@ -0,0 +1,13 @@ +import { IRepository } from "./repository"; + +export interface IRoundsRepository extends IRepository { + /** + * Find a round by its ID. + */ + findById(id: number): Promise; + + /** + * Delete the round from the database. + */ + delete(id: number): Promise; +} diff --git a/packages/core-interfaces/src/core-database/database-repository/transactions-repository.ts b/packages/core-interfaces/src/core-database/database-repository/transactions-repository.ts new file mode 100644 index 0000000000..7df0d8c633 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-repository/transactions-repository.ts @@ -0,0 +1,45 @@ +import { Bignum } from "@arkecosystem/crypto"; +import { IRepository } from "./repository"; + +export interface ITransactionsRepository extends IRepository { + + /** + * Find a transactions by its ID. + */ + findById(id: string): Promise; + + /** + * Find multiple transactionss by their block ID. + */ + findByBlockId(blockId: string): Promise; + + /** + * Find multiple transactionss by their block ID and order them by sequence. + */ + latestByBlock(blockId: string): Promise; + + /** + * Find multiple transactionss by their block IDs and order them by sequence. + */ + latestByBlocks(blockIds: string[]): Promise; + + /** + * Get all of the forged transaction ids from the database. + */ + forged(ids: string[]): Promise; + + /** + * Get statistics about all transactions from the database. + */ + statistics(): Promise<{ + count: number, + totalFee: Bignum, + totalAmount: Bignum + }>; + + /** + * Delete transactions with blockId + */ + deleteByBlockId(blockId: string): Promise; + +} diff --git a/packages/core-interfaces/src/core-database/database-repository/wallets-repository.ts b/packages/core-interfaces/src/core-database/database-repository/wallets-repository.ts new file mode 100644 index 0000000000..b0046fa8b7 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-repository/wallets-repository.ts @@ -0,0 +1,28 @@ +import { IRepository } from "./repository"; + +export interface IWalletsRepository extends IRepository { + /** + * Get all of the wallets from the database. + */ + all(): Promise; + + /** + * Find a wallet by its address. + */ + findByAddress(address: string): Promise + + /** + * Get the count of wallets that have a negative balance. + */ + tallyWithNegativeBalance(): Promise; + + /** + * Get the count of wallets that have a negative vote balance. + */ + tallyWithNegativeVoteBalance(): Promise; + + /** + * Create or update a record matching the attributes, and fill it with values. + */ + updateOrCreate(wallet: any): Promise; +} diff --git a/packages/core-interfaces/src/core-database/database-service.ts b/packages/core-interfaces/src/core-database/database-service.ts new file mode 100644 index 0000000000..d290febd72 --- /dev/null +++ b/packages/core-interfaces/src/core-database/database-service.ts @@ -0,0 +1,90 @@ +import { models } from "@arkecosystem/crypto"; +import { EventEmitter, Logger } from "../index"; +import { IDelegatesBusinessRepository, IWalletsBusinessRepository } from "./business-repository"; +import { IDatabaseConnection } from "./database-connection"; +import { IWalletManager } from "./wallet-manager"; + +export interface IDatabaseService { + + walletManager: IWalletManager; + + wallets: IWalletsBusinessRepository; + + delegates: IDelegatesBusinessRepository; + + connection: IDatabaseConnection; + + logger: Logger.ILogger; + + emitter: EventEmitter.EventEmitter; + + config: any; + + options: any; + + cache: Map; + + restoredDatabaseIntegrity: boolean; + + verifyBlockchain(): Promise<{ valid: boolean, errors: any[] }>; + + getActiveDelegates(height: number, delegates?: any[]): Promise; + + buildWallets(height: number): Promise; + + saveWallets(force: boolean): Promise; + + saveBlock(block: models.Block): Promise; + + // TODO: These methods are exposing database terminology on the business layer, not a fan... + + enqueueSaveBlock(block: models.Block): void; + + enqueueDeleteBlock(block: models.Block): void; + + enqueueDeleteRound(height: number): void; + + commitQueuedQueries(): Promise; + + deleteBlock(block: models.Block): Promise; + + getBlock(id: string): Promise; + + getLastBlock(): Promise; + + getBlocks(offset: number, limit: number): Promise; + + getTopBlocks(count): Promise; + + getRecentBlockIds(): Promise; + + saveRound(activeDelegates: object[]): Promise; + + deleteRound(round: any): Promise; + + getTransaction(id: string): Promise; + + getForgedTransactionsIds(ids: string[]): Promise; + + init(): Promise; + + loadBlocksFromCurrentRound(): Promise; + + loadTransactionsForBlocks(blocks): Promise; + + updateDelegateStats(delegates: any[]): void; + + applyRound(height: number): Promise; + + revertRound(height: number): Promise; + + applyBlock(block: models.Block): Promise; + + revertBlock(block: models.Block): Promise; + + verifyTransaction(transaction: models.Transaction): Promise; + + getBlocksForRound(round?: number): Promise; + + getCommonBlocks(ids: string[]): Promise; +} diff --git a/packages/core-interfaces/src/core-database/event-types.ts b/packages/core-interfaces/src/core-database/event-types.ts new file mode 100644 index 0000000000..4f16f870a4 --- /dev/null +++ b/packages/core-interfaces/src/core-database/event-types.ts @@ -0,0 +1,6 @@ +export enum DatabaseEvents { + PRE_CONNECT = "database.pre_connect", + POST_CONNECT = "database.post_connect", + PRE_DISCONNECT = "database.pre_disconnect", + POST_DISCONNECT = "databse.post_disconnect" +} diff --git a/packages/core-interfaces/src/core-database/index.ts b/packages/core-interfaces/src/core-database/index.ts new file mode 100644 index 0000000000..fd44d022fe --- /dev/null +++ b/packages/core-interfaces/src/core-database/index.ts @@ -0,0 +1,6 @@ +export * from "./database-repository"; +export * from "./business-repository"; +export * from "./database-connection"; +export * from "./wallet-manager"; +export * from "./database-service"; +export * from "./event-types"; diff --git a/packages/core-interfaces/src/core-database/wallet-manager.ts b/packages/core-interfaces/src/core-database/wallet-manager.ts new file mode 100644 index 0000000000..565089b195 --- /dev/null +++ b/packages/core-interfaces/src/core-database/wallet-manager.ts @@ -0,0 +1,61 @@ +import { models } from "@arkecosystem/crypto"; +import { Logger } from "../index"; + +export interface IWalletManager { + + logger: Logger.ILogger; + + config: any; + + reset(): void; + + allByAddress(): models.Wallet[]; + + allByPublicKey(): models.Wallet[]; + + allByUsername(): models.Wallet[]; + + findByAddress(address: string): models.Wallet; + + exists(addressOrPublicKey: string): boolean; + + findByPublicKey(publicKey: string): models.Wallet; + + findByUsername(username: string): models.Wallet; + + index(wallets: models.Wallet[]): void; + + reindex(wallet: models.Wallet): void; + + clear(): void; + + loadActiveDelegateList(maxDelegateCount: number, height?: number): any[]; + + buildVoteBalances(): void; + + applyBlock(block: models.Block): void; + + revertBlock(block: models.Block): void; + + applyTransaction(transaction: models.Transaction): models.Transaction; + + revertTransaction(transaction: models.Transaction): any; + + isDelegate(publicKey: string): boolean; + + canBePurged(wallet: models.Wallet): boolean; + + forgetByAddress(address: string): void; + + forgetByPublicKey( publicKey: string): void; + + forgetByUsername(username: string): void; + + setByAddress(address: string, wallet: models.Wallet): void; + + setByPublicKey(publicKey: string, wallet: models.Wallet): void; + + setByUsername(username: string, wallet: models.Wallet): void; + + purgeEmptyNonDelegates(): void; +} diff --git a/packages/core-interfaces/src/core-event-emitter/index.ts b/packages/core-interfaces/src/core-event-emitter/index.ts new file mode 100644 index 0000000000..478f9bc498 --- /dev/null +++ b/packages/core-interfaces/src/core-event-emitter/index.ts @@ -0,0 +1,2 @@ +import EventEmitter from "eventemitter3"; +export { EventEmitter }; diff --git a/packages/core-interfaces/src/core-logger/index.ts b/packages/core-interfaces/src/core-logger/index.ts new file mode 100644 index 0000000000..41c7bf273e --- /dev/null +++ b/packages/core-interfaces/src/core-logger/index.ts @@ -0,0 +1 @@ +export * from "./logger"; diff --git a/packages/core-interfaces/src/core-logger/logger.ts b/packages/core-interfaces/src/core-logger/logger.ts new file mode 100644 index 0000000000..c01cbd5a8d --- /dev/null +++ b/packages/core-interfaces/src/core-logger/logger.ts @@ -0,0 +1,49 @@ +export interface ILogger { + /** + * Make the logger instance. + * @return {Object} + */ + make(): ILogger; + + /** + * Log an error message. + * @param {String} message + * @return {void} + */ + error(message: string): void; + + /** + * Log a warning message. + * @param {String} message + * @return {void} + */ + warn(message: string): void; + + /** + * Log an info message. + * @param {String} message + * @return {void} + */ + info(message: string): void; + + /** + * Log a debug message. + * @param {String} message + * @return {void} + */ + debug(message: string): void; + + /** + * Log a verbose message. + * @param {String} message + * @return {void} + */ + verbose(message: string): void; + + /** + * Suppress console output. + * @param {Boolean} + * @return {void} + */ + suppressConsoleOutput(suppress: boolean): void; +} diff --git a/packages/core-interfaces/src/core-p2p/index.ts b/packages/core-interfaces/src/core-p2p/index.ts new file mode 100644 index 0000000000..43beba7675 --- /dev/null +++ b/packages/core-interfaces/src/core-p2p/index.ts @@ -0,0 +1,2 @@ +export * from "./monitor"; +export * from "./peer"; diff --git a/packages/core-interfaces/src/core-p2p/monitor.ts b/packages/core-interfaces/src/core-p2p/monitor.ts new file mode 100644 index 0000000000..bb709935a5 --- /dev/null +++ b/packages/core-interfaces/src/core-p2p/monitor.ts @@ -0,0 +1,152 @@ +export interface IMonitor { + peers: { [ip: string]: any }; + + start(options: any): Promise; + + /** + * Update network status (currently only peers are updated). + * @param {Boolean} networkStart + * @return {Promise} + */ + updateNetworkStatus(networkStart?: boolean): Promise; + + /** + * Accept and store a valid peer. + * @param {IPeer} peer + * @throws {Error} If invalid peer + */ + acceptNewPeer(peer: any): Promise; + + /** + * Remove peer from monitor. + * @param {IPeer} peer + */ + removePeer(peer: any): void; + + /** + * Clear peers which aren't responding. + * @param {Boolean} fast + * @param {Boolean} tracker + * @param {Boolean} forcePing + */ + cleanPeers(fast?: boolean, forcePing?: boolean): Promise; + + /** + * Suspend an existing peer. + * @param {IPeer} peer + * @return {void} + */ + suspendPeer(ip: any): void; + + /** + * Get a list of all suspended peers. + * @return {void} + */ + getSuspendedPeers(): { [ip: string]: any }; + + /** + * Get all available peers. + * @return {IPeer[]} + */ + getPeers(): any[]; + + /** + * Get the peer available peers. + * @param {String} ip + * @return {IPeer} + */ + getPeer(ip: any): any; + + peerHasCommonBlocks(peer: any, blockIds: any): Promise; + + /** + * Get a random, available peer. + * @param {(Number|undefined)} acceptableDelay + * @return {IPeer} + */ + getRandomPeer(acceptableDelay?: any, downloadSize?: any, failedAttempts?: any): any; + + /** + * Get a random, available peer which can be used for downloading blocks. + * @return {IPeer} + */ + getRandomDownloadBlocksPeer(minHeight: any): any; + + /** + * Populate list of available peers from random peers. + */ + discoverPeers(): Promise; + + /** + * Check if we have any peers. + * @return {bool} + */ + hasPeers(): boolean; + + /** + * Get the median network height. + * @return {Number} + */ + getNetworkHeight(): any; + + /** + * Get the PBFT Forging status. + * @return {Number} + */ + getPBFTForgingStatus(): number; + + /** + * Refresh all peers after a fork. Peers with no common blocks are + * suspended. + * @return {void} + */ + refreshPeersAfterFork(): Promise; + + /** + * Download blocks from a random peer. + * @param {Number} fromBlockHeight + * @return {Object[]} + */ + downloadBlocks(fromBlockHeight: any): any; + + /** + * Broadcast block to all peers. + * @param {Block} block + * @return {Promise} + */ + broadcastBlock(block: any): Promise; + + /** + * Broadcast transactions to a fixed number of random peers. + * @param {Transaction[]} transactions + */ + broadcastTransactions(transactions: any): Promise; + + /** + * Update all peers based on height and last block id. + * + * Grouping peers by height and then by common id results in one of the following + * scenarios: + * + * 1) Same height, same common id + * 2) Same height, mixed common id + * 3) Mixed height, same common id + * 4) Mixed height, mixed common id + * + * Scenario 1: Do nothing. + * Scenario 2-4: + * - If own height is ahead of majority do nothing for now. + * - Pick most common id from peers with most common height and calculate quota, + * depending on which the node rolls back or waits. + * + * NOTE: Only called when the network is consecutively missing blocks `p2pUpdateCounter` times. + * @return {String} + */ + updatePeersOnMissingBlocks(): Promise; + + /** + * Dump the list of active peers. + * @return {void} + */ + dumpPeers(): void; +} diff --git a/packages/core-interfaces/src/core-p2p/peer.ts b/packages/core-interfaces/src/core-p2p/peer.ts new file mode 100644 index 0000000000..d3ae8efb3c --- /dev/null +++ b/packages/core-interfaces/src/core-p2p/peer.ts @@ -0,0 +1,78 @@ +import { models } from "@arkecosystem/crypto"; + +export interface IPeer { + setHeaders(headers: any): void; + + /** + * Set the given status for the peer. + * @param {String} value + * @return {void} + */ + setStatus(value: any): void; + + /** + * Get information to broadcast. + * @return {Object} + */ + toBroadcastInfo(): { + ip: any; + port: number; + nethash: any; + milestoneHash: string; + version: any; + os: any; + status: any; + height: any; + delay: any; + }; + + /** + * Perform POST request for a block. + * @param {Block} block + * @return {(Object|undefined)} + */ + postBlock(block: models.Block): Promise; + + /** + * Perform POST request for a transactions. + * @param {Transaction[]} transactions + * @return {(Object|undefined)} + */ + postTransactions(transactions: models.Transaction[]): Promise; + + /** + * Download blocks from peer. + * @param {Number} fromBlockHeight + * @return {(Object[]|undefined)} + */ + downloadBlocks(fromBlockHeight: number): Promise; + + /** + * Perform ping request on this peer if it has not been + * recently pinged. + * @param {Number} [delay=5000] + * @param {Boolean} force + * @return {Object} + * @throws {Error} If fail to get peer status. + */ + ping(delay: any, force?: boolean): Promise; + + /** + * Returns true if this peer was pinged the past 2 minutes. + * @return {Boolean} + */ + recentlyPinged(): boolean; + + /** + * Refresh peer list. It removes blacklisted peers from the fetch + * @return {Object[]} + */ + getPeers(): Promise; + + /** + * Check if peer has common blocks. + * @param {[]String} ids + * @return {Boolean} + */ + hasCommonBlocks(ids: string[]): Promise; +} diff --git a/packages/core-interfaces/src/core-transaction-pool/index.ts b/packages/core-interfaces/src/core-transaction-pool/index.ts new file mode 100644 index 0000000000..e11bb11e00 --- /dev/null +++ b/packages/core-interfaces/src/core-transaction-pool/index.ts @@ -0,0 +1,2 @@ +export * from "./transaction-pool"; +export * from "./transaction-guard"; diff --git a/packages/core-interfaces/src/core-transaction-pool/transaction-guard.ts b/packages/core-interfaces/src/core-transaction-pool/transaction-guard.ts new file mode 100644 index 0000000000..35f03e2b52 --- /dev/null +++ b/packages/core-interfaces/src/core-transaction-pool/transaction-guard.ts @@ -0,0 +1,20 @@ +import { models } from "@arkecosystem/crypto"; + +export interface TransactionErrorDTO { + type: string; + message: string; +} + +export interface ValidationResultDTO { + accept: string[]; + broadcast: string[]; + invalid: string[]; + excess: string[]; + errors: { [key: string]: TransactionErrorDTO[] } | null; +} + +export interface ITransactionGuard { + validate(transactions: models.Transaction[]): Promise; + + getBroadcastTransactions(): models.Transaction[]; +} diff --git a/packages/core-interfaces/src/core-transaction-pool/transaction-pool.ts b/packages/core-interfaces/src/core-transaction-pool/transaction-pool.ts new file mode 100644 index 0000000000..9f56dac6c5 --- /dev/null +++ b/packages/core-interfaces/src/core-transaction-pool/transaction-pool.ts @@ -0,0 +1,172 @@ +import dayjs from "dayjs-ext"; + +import { constants, models } from "@arkecosystem/crypto"; + +export interface AddTransactionResponseDTO { + success: boolean; +} +export interface AddTransactionErrorDTO extends AddTransactionResponseDTO { + transaction: models.Transaction; + type: string; + message: string; +} + +export interface ITransactionPool { + options: any; + + make(): Promise; + + /** + * Get a driver instance. + */ + driver(): () => any; + + /** + * Disconnect from transaction pool. + * @return {void} + */ + disconnect(): void; + + /** + * Get the number of transactions in the pool. + */ + getPoolSize(): number; + + /** + * Get the number of transactions in the pool from a specific sender\ + */ + getSenderSize(senderPublicKey: string): number; + + /** + * Add many transactions to the pool. + * @param {Array} transactions, already transformed and verified + * by transaction guard - must have serialized field + * @return {Object} like + * { + * added: [ ... successfully added transactions ... ], + * notAdded: [ { transaction: Transaction, type: String, message: String }, ... ] + * } + */ + addTransactions( + transactions: models.Transaction[], + ): { + added: models.Transaction[]; + notAdded: AddTransactionErrorDTO[]; + }; + + /** + * Add a transaction to the pool. + */ + addTransaction(transaction: models.Transaction): AddTransactionResponseDTO; + + /** + * Remove a transaction from the pool by transaction object. + * @param {Transaction} transaction + * @return {void} + */ + removeTransaction(transaction: models.Transaction): void; + + /** + * Remove a transaction from the pool by id. + */ + removeTransactionById(id: string, senderPublicKey?: string): void; + + /** + * Get all transactions that are ready to be forged. + */ + getTransactionsForForging(blockSize: number): models.Transaction[]; + + /** + * Get a transaction by transaction id. + */ + getTransaction(id: string): models.Transaction; + + /** + * Get all transactions within the specified range [start, start + size), ordered by fee. + * @return {(Array|void)} array of serialized transaction hex strings + */ + getTransactions(start: number, size: number): string[]; + + /** + * Get all transactions within the specified range [start, start + size). + * @return {Array} array of transactions IDs in the specified range + */ + getTransactionIdsForForging(start: number, size: number): string[]; + + /** + * Get data from all transactions within the specified range [start, start + size). + * Transactions are ordered by fee (highest fee first) or by + * insertion time, if fees equal (earliest transaction first). + * @return {Array} array of transaction[property] + */ + getTransactionsData(start: number, size: number, property: string): any[]; + + /** + * Remove all transactions from the transaction pool belonging to specific sender. + */ + removeTransactionsForSender(senderPublicKey: string): void; + + /** + * Check whether sender of transaction has exceeded max transactions in queue. + */ + hasExceededMaxTransactions(transaction: models.Transaction): boolean; + + /** + * Flush the pool (delete all transactions from it). + */ + flush(): void; + + /** + * Checks if a transaction exists in the pool. + */ + transactionExists(transactionId: string): any; + + /** + * Check if transaction sender is blocked + * @return {Boolean} + */ + isSenderBlocked(senderPublicKey: string): boolean; + + /** + * Blocks sender for a specified time + */ + blockSender(senderPublicKey: string): dayjs.Dayjs; + + /** + * Processes recently accepted block by the blockchain. + * It removes block transaction from the pool and adjusts + * pool wallets for non existing transactions. + * + * @param {Object} block + * @return {void} + */ + acceptChainedBlock(block: models.Block): void; + + /** + * Rebuild pool manager wallets + * Removes all the wallets from pool manager and applies transaction from pool - if any + * It waits for the node to sync, and then check the transactions in pool + * and validates them and apply to the pool manager. + */ + buildWallets(): Promise; + + purgeByPublicKey(senderPublicKey: string): void; + + /** + * Purges all transactions from senders with at least one + * invalid transaction. + */ + purgeSendersWithInvalidTransactions(block: models.Block): void; + + /** + * Purges all transactions from the block. + * Purges if transaction exists. It assumes that if trx exists that also wallet exists in pool + */ + purgeBlock(block: models.Block): void; + + /** + * Check whether a given sender has any transactions of the specified type + * in the pool. + */ + senderHasTransactionsOfType(senderPublicKey: string, transactionType: constants.TransactionTypes): boolean; +} diff --git a/packages/core-interfaces/src/index.ts b/packages/core-interfaces/src/index.ts new file mode 100644 index 0000000000..367e0a230a --- /dev/null +++ b/packages/core-interfaces/src/index.ts @@ -0,0 +1,10 @@ +import * as Blockchain from "./core-blockchain"; +import * as Container from "./core-container"; +import * as Database from "./core-database"; +import * as EventEmitter from "./core-event-emitter"; +import * as Logger from "./core-logger"; +import * as P2P from "./core-p2p"; +import * as TransactionPool from "./core-transaction-pool"; +import * as Shared from "./shared"; + +export { Container, Logger, Blockchain, TransactionPool, Shared, EventEmitter, P2P, Database }; diff --git a/packages/core-interfaces/src/shared/config.ts b/packages/core-interfaces/src/shared/config.ts new file mode 100644 index 0000000000..1563710e3d --- /dev/null +++ b/packages/core-interfaces/src/shared/config.ts @@ -0,0 +1,18 @@ +import get from "lodash/get"; +import set from "lodash/set"; + +export class Config { + private config: any; + + public init(options: any): void { + this.config = options; + } + + public get(key: string, defaultValue: any = null): any { + return get(this.config, key, defaultValue); + } + + public set(key: string, value: any): void { + set(this.config, key, value); + } +} diff --git a/packages/core-interfaces/src/shared/index.ts b/packages/core-interfaces/src/shared/index.ts new file mode 100644 index 0000000000..5c62e04f5e --- /dev/null +++ b/packages/core-interfaces/src/shared/index.ts @@ -0,0 +1 @@ +export * from "./config"; diff --git a/packages/core-interfaces/tsconfig.json b/packages/core-interfaces/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-interfaces/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-config/.gitattributes b/packages/core-jest-matchers/.gitattributes similarity index 100% rename from packages/core-config/.gitattributes rename to packages/core-jest-matchers/.gitattributes diff --git a/packages/core-jest-matchers/README.md b/packages/core-jest-matchers/README.md new file mode 100644 index 0000000000..24cba42650 --- /dev/null +++ b/packages/core-jest-matchers/README.md @@ -0,0 +1,24 @@ +# Ark Core - Jest Matchers + +

+ +

+ +## Documentation + +You can find installation instructions and detailed instructions on how to use this package at the [dedicated documentation site](https://docs.ark.io/guidebook/core/plugins/core-jest-matchers.html). + +## Security + +If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. + +## Credits + +- [Brian Faust](https://github.com/faustbrian) +- [Erwann Gentric](https://github.com/air1one) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) + +## License + +[MIT](LICENSE) © [ArkEcosystem](https://ark.io) diff --git a/packages/core-jest-matchers/__tests__/api/block.test.ts b/packages/core-jest-matchers/__tests__/api/block.test.ts new file mode 100644 index 0000000000..1275c5346e --- /dev/null +++ b/packages/core-jest-matchers/__tests__/api/block.test.ts @@ -0,0 +1,49 @@ +import "../../src/api/block"; + +let block; + +beforeEach(() => { + block = { + blockSignature: "", + createdAt: "", + generatorPublicKey: "", + height: "", + id: "", + numberOfTransactions: "", + payloadHash: "", + payloadLength: "", + previousBlock: "", + reward: "", + timestamp: "", + totalAmount: "", + totalFee: "", + transactions: "", + updatedAt: "", + version: "", + }; +}); +describe(".toBeValidBlock", () => { + test("passes when given a valid block", () => { + expect(block).toBeValidBlock(); + }); + + test("fails given an invalid block", () => { + delete block.reward; + expect(expect(block).toBeValidBlock).toThrowError(/Expected .* to be a valid block/); + }); +}); + +describe(".toBeValidArrayOfBlocks", () => { + test("passes given a valid array of blocks", () => { + expect([block, block]).toBeValidArrayOfBlocks(); + }); + + test("fails given an array with an invalid block", () => { + delete block.reward; + expect(expect([block, block]).toBeValidArrayOfBlocks).toThrowError(/Expected .* to be a valid array of blocks/); + }); + + test("fails when not given an array of blocks", () => { + expect(expect(block).toBeValidArrayOfBlocks).toThrowError(/Expected .* to be a valid array of blocks/); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/api/peer.test.ts b/packages/core-jest-matchers/__tests__/api/peer.test.ts new file mode 100644 index 0000000000..d6c7e9a786 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/api/peer.test.ts @@ -0,0 +1,33 @@ +import "../../src/api/peer"; + +let peer; + +beforeEach(() => { + peer = { ip: "", port: "" }; +}); + +describe(".toBeValidPeer", () => { + test("passes pass given a valid peer", () => { + expect(peer).toBeValidPeer(); + }); + + test("fails given an invalid peer", () => { + delete peer.ip; + expect(expect(peer).toBeValidPeer).toThrowError(/Expected .* to be a valid peer/); + }); +}); + +describe(".toBeValidArrayOfPeers", () => { + test("passes given an array of valid peers", () => { + expect([peer, peer]).toBeValidArrayOfPeers(); + }); + + test("fails given an array with an invalid peer", () => { + delete peer.ip; + expect(expect([peer, peer]).toBeValidArrayOfPeers).toThrowError(/Expected .* to be a valid array of peers/); + }); + + test("fails when not given an array of peers", () => { + expect(expect(peer).toBeValidArrayOfPeers).toThrowError(/Expected .* to be a valid array of peers/); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/api/response.test.ts b/packages/core-jest-matchers/__tests__/api/response.test.ts new file mode 100644 index 0000000000..b405a58b93 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/api/response.test.ts @@ -0,0 +1,48 @@ +import "../../src/api/response"; + +let response; + +beforeEach(() => { + response = { + status: 200, + headers: {}, + data: { + meta: { + pageCount: "", + totalCount: "", + next: "", + previous: "", + self: "", + first: "", + last: "", + }, + }, + }; +}); + +describe(".toBeSuccessfulResponse", () => { + test("passes when given a successful response", () => { + expect(response).toBeSuccessfulResponse(); + }); + + test("fails when given an unsuccessful response", () => { + response.status = 404; + expect(expect(response).toBeSuccessfulResponse).toThrowError(/Expected .* to be a successful response/); + }); + + test("fails when not given a response object", () => { + response = "invalid"; + expect(expect(response).toBeSuccessfulResponse).toThrowError(/Expected .* to be a successful response/); + }); +}); + +describe(".toBePaginated", () => { + test("passes when given a paginated response", () => { + expect(response).toBePaginated(); + }); + + test("fails when not given a paginated response", () => { + delete response.data.meta.pageCount; + expect(expect(response).toBePaginated).toThrowError(/Expected .* to be a paginated response/); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/api/transaction.test.ts b/packages/core-jest-matchers/__tests__/api/transaction.test.ts new file mode 100644 index 0000000000..870982590f --- /dev/null +++ b/packages/core-jest-matchers/__tests__/api/transaction.test.ts @@ -0,0 +1,26 @@ +import "../../src/api/transaction"; + +const transaction = { + id: "", + blockid: "", + type: "", + timestamp: "", + amount: "", + fee: "", + senderId: "", + senderPublicKey: "", + signature: "", + asset: "", + confirmations: "", +}; + +describe(".toBeApiTransaction", () => { + test("passes pass given a valid transaction", () => { + expect(transaction).toBeApiTransaction(); + }); + + test("fails given an invalid transaction", () => { + delete transaction.id; + expect(expect(transaction).toBeApiTransaction).toThrowError(/Expected .* to be a valid transaction/); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/blockchain/dispatch.test.ts b/packages/core-jest-matchers/__tests__/blockchain/dispatch.test.ts new file mode 100644 index 0000000000..7d157f2a04 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/blockchain/dispatch.test.ts @@ -0,0 +1,19 @@ +import "../../src/blockchain/dispatch"; + +describe(".toDispatch", () => { + const blockchain = { + dispatch(event) { + return event; + }, + }; + + test("passes when the dispatch method is called with the argument", () => { + expect(() => blockchain.dispatch("EVENT")).toDispatch(blockchain, "EVENT"); + }); + + test("fails when the dispatch method is not called with the argument", async () => { + // tslint:disable-next-line:no-empty + await expect(() => {}).not.toDispatch(blockchain, "FAKE-EVENT"); + await expect(() => blockchain.dispatch("OTHER-EVENT")).not.toDispatch(blockchain, "EVENT"); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/blockchain/execute-on-entry.test.ts b/packages/core-jest-matchers/__tests__/blockchain/execute-on-entry.test.ts new file mode 100644 index 0000000000..90d4aab3ff --- /dev/null +++ b/packages/core-jest-matchers/__tests__/blockchain/execute-on-entry.test.ts @@ -0,0 +1,37 @@ +import { Machine } from "xstate"; +import "../../src/blockchain/execute-on-entry"; + +describe(".toExecuteOnEntry", () => { + const machine = Machine({ + initial: "a", + states: { + a: { + onEntry: ["action-a"], + on: { + START: "b", + }, + }, + b: { + on: { + END: "a", + }, + }, + }, + }); + + test("passes when the state machine executes all the actions when enters a state", () => { + expect(machine).toExecuteOnEntry({ state: "a", actions: ["action-a"] }); + }); + + test("fails when the state machine does not execute all the actions when enters a state", () => { + expect(machine).not.toExecuteOnEntry({ + state: "a", + actions: ["no-action"], + }); + expect(machine).not.toExecuteOnEntry({ state: "b", actions: ["action-a"] }); + expect(machine).not.toExecuteOnEntry({ + state: "a", + actions: ["action-a", "no-action"], + }); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/blockchain/transition.test.ts b/packages/core-jest-matchers/__tests__/blockchain/transition.test.ts new file mode 100644 index 0000000000..b04619008a --- /dev/null +++ b/packages/core-jest-matchers/__tests__/blockchain/transition.test.ts @@ -0,0 +1,47 @@ +import { Machine } from "xstate"; +import "../../src/blockchain/transition"; + +describe(".toTransition", () => { + const machine = Machine({ + initial: "a", + states: { + a: { + on: { + START: "b", + SUB: "c", + }, + }, + b: { + on: { + END: "a", + }, + }, + c: { + on: { + END: "a", + }, + initial: "c-1", + states: { + "c-1": { + on: { + BACK: "b", + }, + }, + }, + }, + }, + }); + + test("passes when the state machine transitions from one state to other on an event", () => { + expect(machine).toTransition({ from: "a", on: "START", to: "b" }); + }); + + test("passes when the state machine transitions from one state to other on an event, even sub-states", () => { + expect(machine).toTransition({ from: "a", on: "SUB", to: "c" }); + }); + + test("fails when the state machine does not transition from one state to other on an event", () => { + expect(machine).not.toTransition({ from: "a", on: "FAKE", to: "b" }); + expect(machine).not.toTransition({ from: "a", on: "END", to: "b" }); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/fields/address.test.ts b/packages/core-jest-matchers/__tests__/fields/address.test.ts new file mode 100644 index 0000000000..ab407dd005 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/fields/address.test.ts @@ -0,0 +1,11 @@ +import "../../src/fields/address"; + +describe(".toBeAddress", () => { + test("passes when given a valid address", () => { + expect("DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN").toBeAddress(); + }); + + test("fails when not given a valid address", () => { + expect(expect("invalid-address").toBeAddress).toThrowError("Expected value to be a valid address"); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/fields/public-key.test.ts b/packages/core-jest-matchers/__tests__/fields/public-key.test.ts new file mode 100644 index 0000000000..b8bc3dc207 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/fields/public-key.test.ts @@ -0,0 +1,11 @@ +import "../../src/fields/public-key"; + +describe(".toBePublicKey", () => { + test("passes when given a valid public key", () => { + expect("022cca9529ec97a772156c152a00aad155ee6708243e65c9d211a589cb5d43234d").toBePublicKey(); + }); + + test("fails when not given a valid public key", () => { + expect(expect("invalid-pubkey").toBePublicKey).toThrowError("Expected value to be a valid public key"); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/models/delegate.test.ts b/packages/core-jest-matchers/__tests__/models/delegate.test.ts new file mode 100644 index 0000000000..24ed905f2f --- /dev/null +++ b/packages/core-jest-matchers/__tests__/models/delegate.test.ts @@ -0,0 +1,17 @@ +import "../../src/models/delegate"; + +describe(".toBeDelegate", () => { + const delegate = { + username: "arkxdev", + address: "DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh", + publicKey: "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", + }; + + test("passes when given a valid delegate", () => { + expect(delegate).toBeDelegate(); + }); + + test("fails when given an invalid delegate", () => { + expect(expect({ fake: "news" }).toBeDelegate).toThrowError("Expected value to be a valid delegate"); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/models/transaction.test.ts b/packages/core-jest-matchers/__tests__/models/transaction.test.ts new file mode 100644 index 0000000000..09707b4271 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/models/transaction.test.ts @@ -0,0 +1,28 @@ +import "../../src/models/transaction"; + +describe(".toBeTransaction", () => { + const transaction = { + version: 1, + network: 23, + type: 0, + timestamp: 35672738, + senderPublicKey: "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + fee: 10000000, + vendorFieldHex: "5449443a2030", + amount: 200000000, + expiration: 0, + recipientId: "AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5", + signature: + "304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619", + vendorField: "TID: 0", + id: "a5e9e6039675563959a783fa672c0ffe65369168a1ecffa3c89bf82961d8dbad", + }; + + test("passes when given a valid transaction", () => { + expect(transaction).toBeTransaction(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ fake: "news" }).toBeTransaction).toThrowError("Expected value to be a valid transaction"); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/models/wallet.test.ts b/packages/core-jest-matchers/__tests__/models/wallet.test.ts new file mode 100644 index 0000000000..2435817eb8 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/models/wallet.test.ts @@ -0,0 +1,16 @@ +import "../../src/models/wallet"; + +describe(".toBeWallet", () => { + const wallet = { + address: "DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh", + publicKey: "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", + }; + + test("passes when given a valid wallet", () => { + expect(wallet).toBeWallet(); + }); + + test("fails when given an invalid wallet", () => { + expect(expect({ fake: "news" }).toBeWallet).toThrowError("Expected value to be a valid wallet"); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/delegate-resignation.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/delegate-resignation.test.ts new file mode 100644 index 0000000000..297b284e74 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/delegate-resignation.test.ts @@ -0,0 +1,18 @@ +import "../../../src/transactions/types/delegate-resignation"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeDelegateResignationType", () => { + test("passes when given a valid transaction", () => { + expect({ + type: TransactionTypes.DelegateResignation, + }).toBeDelegateResignationType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeDelegateResignationType).toThrowError( + "Expected value to be a valid DelegateResignation transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/delegate.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/delegate.test.ts new file mode 100644 index 0000000000..b18381d22d --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/delegate.test.ts @@ -0,0 +1,18 @@ +import "../../../src/transactions/types/delegate"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeDelegateType", () => { + test("passes when given a valid transaction", () => { + expect({ + type: TransactionTypes.DelegateRegistration, + }).toBeDelegateType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeDelegateType).toThrowError( + "Expected value to be a valid DELEGATE transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/ipfs.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/ipfs.test.ts new file mode 100644 index 0000000000..0c21fa6449 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/ipfs.test.ts @@ -0,0 +1,14 @@ +import "../../../src/transactions/types/ipfs"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeIpfsType", () => { + test("passes when given a valid transaction", () => { + expect({ type: TransactionTypes.Ipfs }).toBeIpfsType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeIpfsType).toThrowError("Expected value to be a valid IPFS transaction."); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/multi-payment.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/multi-payment.test.ts new file mode 100644 index 0000000000..f57f367e0d --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/multi-payment.test.ts @@ -0,0 +1,16 @@ +import "../../../src/transactions/types/multi-payment"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeMultiPaymentType", () => { + test("passes when given a valid transaction", () => { + expect({ type: TransactionTypes.MultiPayment }).toBeMultiPaymentType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeMultiPaymentType).toThrowError( + "Expected value to be a valid MultiPayment transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/multi-signature.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/multi-signature.test.ts new file mode 100644 index 0000000000..3804fdba1f --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/multi-signature.test.ts @@ -0,0 +1,18 @@ +import "../../../src/transactions/types/multi-signature"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeMultiSignatureType", () => { + test("passes when given a valid transaction", () => { + expect({ + type: TransactionTypes.MultiSignature, + }).toBeMultiSignatureType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeMultiSignatureType).toThrowError( + "Expected value to be a valid MultiSignature transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/second-signature.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/second-signature.test.ts new file mode 100644 index 0000000000..84fff3080a --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/second-signature.test.ts @@ -0,0 +1,18 @@ +import "../../../src/transactions/types/second-signature"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeSecondSignatureType", () => { + test("passes when given a valid transaction", () => { + expect({ + type: TransactionTypes.SecondSignature, + }).toBeSecondSignatureType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeSecondSignatureType).toThrowError( + "Expected value to be a valid SecondSignature transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/timelock-transfer.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/timelock-transfer.test.ts new file mode 100644 index 0000000000..5496680bdb --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/timelock-transfer.test.ts @@ -0,0 +1,18 @@ +import "../../../src/transactions/types/timelock-transfer"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeTimelockTransferType", () => { + test("passes when given a valid transaction", () => { + expect({ + type: TransactionTypes.TimelockTransfer, + }).toBeTimelockTransferType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeTimelockTransferType).toThrowError( + "Expected value to be a valid TimelockTransfer transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/transfer.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/transfer.test.ts new file mode 100644 index 0000000000..7bafd29abb --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/transfer.test.ts @@ -0,0 +1,16 @@ +import "../../../src/transactions/types/transfer"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeTransferType", () => { + test("passes when given a valid transaction", () => { + expect({ type: TransactionTypes.Transfer }).toBeTransferType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeTransferType).toThrowError( + "Expected value to be a valid Transfer transaction.", + ); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/types/vote.test.ts b/packages/core-jest-matchers/__tests__/transactions/types/vote.test.ts new file mode 100644 index 0000000000..82a8c6f2e6 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/types/vote.test.ts @@ -0,0 +1,14 @@ +import "../../../src/transactions/types/vote"; + +import { constants } from "@arkecosystem/crypto"; +const { TransactionTypes } = constants; + +describe(".toBeVoteType", () => { + test("passes when given a valid transaction", () => { + expect({ type: TransactionTypes.Vote }).toBeVoteType(); + }); + + test("fails when given an invalid transaction", () => { + expect(expect({ type: "invalid" }).toBeVoteType).toThrowError("Expected value to be a valid VOTE transaction."); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/valid-second-signature.test.ts b/packages/core-jest-matchers/__tests__/transactions/valid-second-signature.test.ts new file mode 100644 index 0000000000..af98d2c564 --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/valid-second-signature.test.ts @@ -0,0 +1,91 @@ +import "../../src/transactions/valid-second-signature"; + +// import { generators } from "@arkecosystem/core-test-utils"; +// const wallets = generators.generateWallets("testnet", 2); +// const transaction = generators.generateTransfers("testnet", wallets.map(w => w.passphrase))[0]; + +const wallets = [ + { + address: "AWTRWfm2qdEwxbXnLXxQnviMywFGSHdgkn", + passphrase: "poet virtual attend winter mushroom near manual dish exact palm siren motion", + publicKey: "0322bb7969362a15c78b70be2adcfd270a2ad9f2cd0faed14a5d809c7fd5773e48", + }, + { + address: "AaWAUV5hgDdUnpWHkD1a65AFQBayGgTaFF", + passphrase: "obtain flower vital stone song express combine issue used excite despair trash", + publicKey: "02300c5296527a2ff8586b6d2af383db4e487e6c9a45b6b55dd795393ef460568c", + }, +]; + +const transaction = { + serialized: + "ff01170088795c030322bb7969362a15c78b70be2adcfd270a2ad9f2cd0faed14a5d809c7fd5773e4880969800000000001254657374205472616e73616374696f6e203102000000000000000000000017a10c9543273d90fb4200897a47f51164ae3c92653045022100ef114e808311bd49edc18c7fe83357bb3fdf67d69c32153ed683150aa1b3143802207635a5594c5b394409af49b8289a5c47f4ae5785f5c62ea95a207c66fe30dd943045022100fff70add8db1d54d1c504c354417c2ba5a03401e93cd9ec3f75605a64888d04502203c70c1a86c3c416af913a4d657b461d73f2297061801982463d7bd47f5a91cfe", + verified: true, + id: "ca29499181a5b8145caacb21c638b63de60a9e7cc3a4d4bef367e653c4242f61", + sequence: undefined, + version: 1, + timestamp: 56392072, + senderPublicKey: "0322bb7969362a15c78b70be2adcfd270a2ad9f2cd0faed14a5d809c7fd5773e48", + recipientId: "AWTRWfm2qdEwxbXnLXxQnviMywFGSHdgkn", + type: 0, + vendorField: "Test Transaction 1", + vendorFieldHex: "54657374205472616e73616374696f6e2031", + amount: 2, + fee: 10000000, + blockId: undefined, + signature: + "3045022100ef114e808311bd49edc18c7fe83357bb3fdf67d69c32153ed683150aa1b3143802207635a5594c5b394409af49b8289a5c47f4ae5785f5c62ea95a207c66fe30dd94", + signatures: undefined, + secondSignature: + "3045022100fff70add8db1d54d1c504c354417c2ba5a03401e93cd9ec3f75605a64888d04502203c70c1a86c3c416af913a4d657b461d73f2297061801982463d7bd47f5a91cfe", + signSignature: + "3045022100fff70add8db1d54d1c504c354417c2ba5a03401e93cd9ec3f75605a64888d04502203c70c1a86c3c416af913a4d657b461d73f2297061801982463d7bd47f5a91cfe", + asset: undefined, + expiration: 0, + timelock: undefined, + timelockType: undefined, + data: { + version: 1, + network: 23, + type: 0, + timestamp: 56392072, + senderPublicKey: "0322bb7969362a15c78b70be2adcfd270a2ad9f2cd0faed14a5d809c7fd5773e48", + fee: 10000000, + vendorFieldHex: "54657374205472616e73616374696f6e2031", + amount: 2, + expiration: 0, + recipientId: "AWTRWfm2qdEwxbXnLXxQnviMywFGSHdgkn", + signature: + "3045022100ef114e808311bd49edc18c7fe83357bb3fdf67d69c32153ed683150aa1b3143802207635a5594c5b394409af49b8289a5c47f4ae5785f5c62ea95a207c66fe30dd94", + secondSignature: + "3045022100fff70add8db1d54d1c504c354417c2ba5a03401e93cd9ec3f75605a64888d04502203c70c1a86c3c416af913a4d657b461d73f2297061801982463d7bd47f5a91cfe", + signSignature: + "3045022100fff70add8db1d54d1c504c354417c2ba5a03401e93cd9ec3f75605a64888d04502203c70c1a86c3c416af913a4d657b461d73f2297061801982463d7bd47f5a91cfe", + vendorField: "Test Transaction 1", + id: "ca29499181a5b8145caacb21c638b63de60a9e7cc3a4d4bef367e653c4242f61", + }, +}; + +describe(".toHaveValidSecondSignature", () => { + test("passes when given a valid transaction", () => { + expect(transaction).toHaveValidSecondSignature({ + publicKey: wallets[1].publicKey, + }); + }); + + test("fails when given an invalid transaction", () => { + transaction.secondSignature = "invalid"; + transaction.signSignature = "invalid"; + expect(expect(transaction).toHaveValidSecondSignature).toThrowError( + "Expected value to have a valid second signature", + ); + }); + + test("fails when it does not match", () => { + transaction.secondSignature = "invalid"; + transaction.signSignature = "invalid"; + expect(transaction).not.toHaveValidSecondSignature({ + publicKey: wallets[1].publicKey, + }); + }); +}); diff --git a/packages/core-jest-matchers/__tests__/transactions/valid.test.ts b/packages/core-jest-matchers/__tests__/transactions/valid.test.ts new file mode 100644 index 0000000000..158bce599c --- /dev/null +++ b/packages/core-jest-matchers/__tests__/transactions/valid.test.ts @@ -0,0 +1,29 @@ +import "../../src/transactions/valid"; + +const transaction = { + version: 1, + network: 23, + type: 0, + timestamp: 35672738, + senderPublicKey: "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + fee: 10000000, + vendorFieldHex: "5449443a2030", + amount: 200000000, + expiration: 0, + recipientId: "AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5", + signature: + "304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619", + vendorField: "TID: 0", + id: "a5e9e6039675563959a783fa672c0ffe65369168a1ecffa3c89bf82961d8dbad", +}; + +describe(".toBeValidTransaction", () => { + test("passes when given a valid transaction", () => { + expect(transaction).toBeValidTransaction(); + }); + + test("fails when given an invalid transaction", () => { + transaction.fee = "invalid" as any; + expect(expect(transaction).toBeValidTransaction).toThrowError("Expected value to be a valid transaction"); + }); +}); diff --git a/packages/core-jest-matchers/package.json b/packages/core-jest-matchers/package.json new file mode 100644 index 0000000000..eec87f0810 --- /dev/null +++ b/packages/core-jest-matchers/package.json @@ -0,0 +1,54 @@ +{ + "name": "@arkecosystem/core-jest-matchers", + "description": "Jest matchers for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust ", + "Erwann Gentric ", + "Joshua Noack " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/bip39": "^2.4.1", + "@types/lodash.get": "^4.4.4", + "@types/lodash.isequal": "^4.5.3", + "@types/lodash.sortby": "^4.7.4", + "bip39": "^2.5.0", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "lodash.sortby": "^4.7.0", + "superheroes": "^2.0.0", + "xstate": "^4.2.2" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } +} diff --git a/packages/core-jest-matchers/src/api/block.ts b/packages/core-jest-matchers/src/api/block.ts new file mode 100644 index 0000000000..652e4ebc55 --- /dev/null +++ b/packages/core-jest-matchers/src/api/block.ts @@ -0,0 +1,62 @@ +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeValidBlock(): R; + toBeValidArrayOfBlocks(): R; + } + } +} + +function isValidBlock(block) { + const allowedKeys = sortBy([ + "blockSignature", + "createdAt", + "generatorPublicKey", + "height", + "id", + "numberOfTransactions", + "payloadHash", + "payloadLength", + "previousBlock", + "reward", + "timestamp", + "totalAmount", + "totalFee", + "transactions", + "updatedAt", + "version", + ]); + const actualKeys = Object.keys(block).filter(key => allowedKeys.includes(key)); + + return isEqual(sortBy(actualKeys), allowedKeys); +} + +expect.extend({ + toBeValidBlock: (actual, expected) => { + return { + message: () => `Expected ${JSON.stringify(actual)} to be a valid block`, + pass: isValidBlock(actual), + }; + }, + toBeValidArrayOfBlocks: (actual, expected) => { + const message = () => `Expected ${JSON.stringify(actual)} to be a valid array of blocks`; + + if (!Array.isArray(actual)) { + return { message, pass: false }; + } + + for (const peer of actual) { + if (!isValidBlock(peer)) { + return { message, pass: false }; + } + } + + return { message, pass: true }; + }, +}); diff --git a/packages/core-jest-matchers/src/api/index.ts b/packages/core-jest-matchers/src/api/index.ts new file mode 100644 index 0000000000..1a4b6f752b --- /dev/null +++ b/packages/core-jest-matchers/src/api/index.ts @@ -0,0 +1,4 @@ +import "./block"; +import "./peer"; +import "./response"; +import "./transaction"; diff --git a/packages/core-jest-matchers/src/api/peer.ts b/packages/core-jest-matchers/src/api/peer.ts new file mode 100644 index 0000000000..c9a1f5d5f9 --- /dev/null +++ b/packages/core-jest-matchers/src/api/peer.ts @@ -0,0 +1,46 @@ +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeValidPeer(): R; + toBeValidArrayOfPeers(): R; + } + } +} + +function isValidPeer(peer) { + const allowedKeys = sortBy(["ip", "port"]); + const actualKeys = Object.keys(peer).filter(key => allowedKeys.includes(key)); + + return isEqual(sortBy(actualKeys), allowedKeys); +} + +expect.extend({ + toBeValidPeer: (actual, expected) => { + return { + message: () => `Expected ${JSON.stringify(actual)} to be a valid peer`, + pass: isValidPeer(actual), + }; + }, + + toBeValidArrayOfPeers: (actual, expected) => { + const message = () => `Expected ${JSON.stringify(actual)} to be a valid array of peers`; + + if (!Array.isArray(actual)) { + return { message, pass: false }; + } + + for (const peer of actual) { + if (!isValidPeer(peer)) { + return { message, pass: false }; + } + } + + return { message, pass: true }; + }, +}); diff --git a/packages/core-jest-matchers/src/api/response.ts b/packages/core-jest-matchers/src/api/response.ts new file mode 100644 index 0000000000..0e833a4644 --- /dev/null +++ b/packages/core-jest-matchers/src/api/response.ts @@ -0,0 +1,41 @@ +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeSuccessfulResponse(): R; + toBePaginated(): R; + } + } +} + +expect.extend({ + toBeSuccessfulResponse: (actual, expected) => { + return { + message: () => + `Expected ${JSON.stringify({ + data: actual.data, + status: actual.status, + headers: actual.headers, + })} to be a successful response`, + pass: actual.status === 200 && typeof actual.data === "object", + }; + }, + + toBePaginated: (actual, expected) => { + return { + message: () => + `Expected ${JSON.stringify({ + data: actual.data, + status: actual.status, + headers: actual.headers, + })} to be a paginated response`, + pass: + actual.data.meta && + ["pageCount", "totalCount", "next", "previous", "self", "first", "last"].every(property => + Object.keys(actual.data.meta).includes(property), + ), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/api/transaction.ts b/packages/core-jest-matchers/src/api/transaction.ts new file mode 100644 index 0000000000..f012640cc3 --- /dev/null +++ b/packages/core-jest-matchers/src/api/transaction.ts @@ -0,0 +1,38 @@ +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeApiTransaction(): R; + } + } +} + +expect.extend({ + toBeApiTransaction: (actual, expected) => { + // TODO based on type + const allowedKeys = sortBy([ + "id", + "blockid", + "type", + "timestamp", + "amount", + "fee", + "senderId", + "senderPublicKey", + "signature", + "asset", + "confirmations", + ]); + const actualKeys = Object.keys(actual).filter(key => allowedKeys.includes(key)); + + return { + message: () => `Expected ${JSON.stringify(actual)} to be a valid transaction`, + pass: isEqual(sortBy(actualKeys), allowedKeys), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/blockchain/dispatch.ts b/packages/core-jest-matchers/src/blockchain/dispatch.ts new file mode 100644 index 0000000000..6a9f4692e7 --- /dev/null +++ b/packages/core-jest-matchers/src/blockchain/dispatch.ts @@ -0,0 +1,34 @@ +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toDispatch(dispatcher: object, value: string): R; + } + } +} + +expect.extend({ + async toDispatch(received, dispatcher, expected) { + const mock = jest.fn(); + + dispatcher.dispatch = mock; + await received(); + + const calls = dispatcher.dispatch.mock.calls; + const pass = calls && calls[0] ? Object.is(calls[0][0], expected) : false; + + const messageStr = `Expected "${expected}" to ${this.isNot ? "not" : ""} be dispatched, received ${ + calls && calls[0] ? calls[0][0] : "" + }`; + const message = () => messageStr; + + return { + // FIXME isNot is necessary to write the right message + // @see https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers + message, + pass, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/blockchain/execute-on-entry.ts b/packages/core-jest-matchers/src/blockchain/execute-on-entry.ts new file mode 100644 index 0000000000..681bd92046 --- /dev/null +++ b/packages/core-jest-matchers/src/blockchain/execute-on-entry.ts @@ -0,0 +1,40 @@ +import get from "lodash/get"; +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toExecuteOnEntry(transition: object): R; + } + } +} + +expect.extend({ + toExecuteOnEntry: (machine, transition) => { + let path = transition.state; + + // For nested states, but only works 1 level deep + if (transition.state.indexOf(".") !== -1) { + const slugs = path.split("."); + path = `${slugs[0]}.states.${slugs[1]}`; + } + + const state = get(machine.states, path); + + const actions = transition.actions.map(action => `"${action}"`).join(", "); + + return { + // FIXME isNot is necessary to write the right message + // @see https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers + message: () => + `Expected machine to ${this.isNot ? "not " : ""} call actions ${actions} on state "${ + transition.state + }"`, + pass: isEqual(state.onEntry.map(action => action.type), transition.actions), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/blockchain/index.ts b/packages/core-jest-matchers/src/blockchain/index.ts new file mode 100644 index 0000000000..fcb5f5792d --- /dev/null +++ b/packages/core-jest-matchers/src/blockchain/index.ts @@ -0,0 +1,3 @@ +import "./dispatch"; +import "./execute-on-entry"; +import "./transition"; diff --git a/packages/core-jest-matchers/src/blockchain/transition.ts b/packages/core-jest-matchers/src/blockchain/transition.ts new file mode 100644 index 0000000000..ec4b6e1e6f --- /dev/null +++ b/packages/core-jest-matchers/src/blockchain/transition.ts @@ -0,0 +1,28 @@ +import { matchesState } from "xstate"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toTransition(transition: object): R; + } + } +} + +expect.extend({ + toTransition: (machine, transition) => { + const state = machine.transition(transition.from, transition.on); + + return { + // FIXME isNot is necessary to write the right message + // @see https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers + message: () => + `Expected machine to ${this.isNot ? "not" : ""} transition to "${transition.to}" from "${ + transition.from + }" on "${transition.on}"`, + pass: matchesState(transition.to, state.value), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/fields/address.ts b/packages/core-jest-matchers/src/fields/address.ts new file mode 100644 index 0000000000..fdc359e28a --- /dev/null +++ b/packages/core-jest-matchers/src/fields/address.ts @@ -0,0 +1,21 @@ +import { crypto } from "@arkecosystem/crypto"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeAddress(): R; + } + } +} + +expect.extend({ + toBeAddress: (received, argument) => { + return { + message: () => "Expected value to be a valid address", + pass: crypto.validateAddress(received, argument), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/fields/index.ts b/packages/core-jest-matchers/src/fields/index.ts new file mode 100644 index 0000000000..db72fbd37f --- /dev/null +++ b/packages/core-jest-matchers/src/fields/index.ts @@ -0,0 +1,2 @@ +import "./address"; +import "./public-key"; diff --git a/packages/core-jest-matchers/src/fields/public-key.ts b/packages/core-jest-matchers/src/fields/public-key.ts new file mode 100644 index 0000000000..08cddfa61d --- /dev/null +++ b/packages/core-jest-matchers/src/fields/public-key.ts @@ -0,0 +1,21 @@ +import { crypto } from "@arkecosystem/crypto"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBePublicKey(): R; + } + } +} + +expect.extend({ + toBePublicKey: received => { + return { + message: () => "Expected value to be a valid public key", + pass: crypto.validatePublicKey(received), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/index.ts b/packages/core-jest-matchers/src/index.ts new file mode 100644 index 0000000000..41af8fabc9 --- /dev/null +++ b/packages/core-jest-matchers/src/index.ts @@ -0,0 +1,11 @@ +import "jest-extended"; + +import "./api"; +import "./blockchain"; +import "./fields"; +import "./models"; +import "./transactions"; + +import "./fields"; +import "./models"; +import "./transactions"; diff --git a/packages/core-jest-matchers/src/models/delegate.ts b/packages/core-jest-matchers/src/models/delegate.ts new file mode 100644 index 0000000000..f23cc7292d --- /dev/null +++ b/packages/core-jest-matchers/src/models/delegate.ts @@ -0,0 +1,22 @@ +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeDelegate(): R; + } + } +} + +expect.extend({ + toBeDelegate: actual => { + return { + message: () => "Expected value to be a valid delegate", + pass: isEqual(sortBy(Object.keys(actual)), ["address", "publicKey", "username"]), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/models/index.ts b/packages/core-jest-matchers/src/models/index.ts new file mode 100644 index 0000000000..adf9ce78c3 --- /dev/null +++ b/packages/core-jest-matchers/src/models/index.ts @@ -0,0 +1,3 @@ +import "./delegate"; +import "./transaction"; +import "./wallet"; diff --git a/packages/core-jest-matchers/src/models/transaction.ts b/packages/core-jest-matchers/src/models/transaction.ts new file mode 100644 index 0000000000..fe0373bccb --- /dev/null +++ b/packages/core-jest-matchers/src/models/transaction.ts @@ -0,0 +1,26 @@ +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeTransaction(): R; + } + } +} + +expect.extend({ + toBeTransaction: actual => { + // TODO based on type + const allowedKeys = sortBy(["id", "type", "amount", "fee", "timestamp", "signature"]); + const actualKeys = Object.keys(actual).filter(key => allowedKeys.includes(key)); + + return { + message: () => "Expected value to be a valid transaction", + pass: isEqual(sortBy(actualKeys), allowedKeys), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/models/wallet.ts b/packages/core-jest-matchers/src/models/wallet.ts new file mode 100644 index 0000000000..1ac3f58ac1 --- /dev/null +++ b/packages/core-jest-matchers/src/models/wallet.ts @@ -0,0 +1,22 @@ +import isEqual from "lodash/isEqual"; +import sortBy from "lodash/sortBy"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeWallet(): R; + } + } +} + +expect.extend({ + toBeWallet: actual => { + return { + message: () => "Expected value to be a valid wallet", + pass: isEqual(sortBy(Object.keys(actual)), ["address", "publicKey"]), + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/index.ts b/packages/core-jest-matchers/src/transactions/index.ts new file mode 100644 index 0000000000..40a19bd73b --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/index.ts @@ -0,0 +1,3 @@ +import "./types"; +import "./valid"; +import "./valid-second-signature"; diff --git a/packages/core-jest-matchers/src/transactions/types/delegate-resignation.ts b/packages/core-jest-matchers/src/transactions/types/delegate-resignation.ts new file mode 100644 index 0000000000..4bcdd1be51 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/delegate-resignation.ts @@ -0,0 +1,22 @@ +import { constants } from "@arkecosystem/crypto"; +const { DelegateResignation } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeDelegateResignationType(): R; + } + } +} + +expect.extend({ + toBeDelegateResignationType: received => { + return { + message: () => "Expected value to be a valid DelegateResignation transaction.", + pass: received.type === DelegateResignation, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/delegate.ts b/packages/core-jest-matchers/src/transactions/types/delegate.ts new file mode 100644 index 0000000000..d4820934be --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/delegate.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { DelegateRegistration } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeDelegateType(): R; + } + } +} + +expect.extend({ + toBeDelegateType: received => { + return { + message: () => "Expected value to be a valid DELEGATE transaction.", + pass: received.type === DelegateRegistration, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/index.ts b/packages/core-jest-matchers/src/transactions/types/index.ts new file mode 100644 index 0000000000..d2460ca40b --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/index.ts @@ -0,0 +1,9 @@ +import "./delegate"; +import "./delegate-resignation"; +import "./ipfs"; +import "./multi-payment"; +import "./multi-signature"; +import "./second-signature"; +import "./timelock-transfer"; +import "./transfer"; +import "./vote"; diff --git a/packages/core-jest-matchers/src/transactions/types/ipfs.ts b/packages/core-jest-matchers/src/transactions/types/ipfs.ts new file mode 100644 index 0000000000..637bb42389 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/ipfs.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { Ipfs } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeIpfsType(): R; + } + } +} + +expect.extend({ + toBeIpfsType: received => { + return { + message: () => "Expected value to be a valid IPFS transaction.", + pass: received.type === Ipfs, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/multi-payment.ts b/packages/core-jest-matchers/src/transactions/types/multi-payment.ts new file mode 100644 index 0000000000..aa7d1aa1f3 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/multi-payment.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { MultiPayment } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeMultiPaymentType(): R; + } + } +} + +expect.extend({ + toBeMultiPaymentType: received => { + return { + message: () => "Expected value to be a valid MultiPayment transaction.", + pass: received.type === MultiPayment, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/multi-signature.ts b/packages/core-jest-matchers/src/transactions/types/multi-signature.ts new file mode 100644 index 0000000000..7697b0e883 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/multi-signature.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { MultiSignature } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeMultiSignatureType(): R; + } + } +} + +expect.extend({ + toBeMultiSignatureType: received => { + return { + message: () => "Expected value to be a valid MultiSignature transaction.", + pass: received.type === MultiSignature, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/second-signature.ts b/packages/core-jest-matchers/src/transactions/types/second-signature.ts new file mode 100644 index 0000000000..f118d6dd8c --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/second-signature.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { SecondSignature } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeSecondSignatureType(): R; + } + } +} + +expect.extend({ + toBeSecondSignatureType: received => { + return { + message: () => "Expected value to be a valid SecondSignature transaction.", + pass: received.type === SecondSignature, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/timelock-transfer.ts b/packages/core-jest-matchers/src/transactions/types/timelock-transfer.ts new file mode 100644 index 0000000000..140fd6137d --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/timelock-transfer.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { TimelockTransfer } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeTimelockTransferType(): R; + } + } +} + +expect.extend({ + toBeTimelockTransferType: received => { + return { + message: () => "Expected value to be a valid TimelockTransfer transaction.", + pass: received.type === TimelockTransfer, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/transfer.ts b/packages/core-jest-matchers/src/transactions/types/transfer.ts new file mode 100644 index 0000000000..5678bb97a9 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/transfer.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { Transfer } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeTransferType(): R; + } + } +} + +expect.extend({ + toBeTransferType: received => { + return { + message: () => "Expected value to be a valid Transfer transaction.", + pass: received.type === Transfer, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/types/vote.ts b/packages/core-jest-matchers/src/transactions/types/vote.ts new file mode 100644 index 0000000000..6ccb661270 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/types/vote.ts @@ -0,0 +1,23 @@ +import { constants } from "@arkecosystem/crypto"; + +const { Vote } = constants.TransactionTypes; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeVoteType(): R; + } + } +} + +expect.extend({ + toBeVoteType: received => { + return { + message: () => "Expected value to be a valid VOTE transaction.", + pass: received.type === Vote, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/valid-second-signature.ts b/packages/core-jest-matchers/src/transactions/valid-second-signature.ts new file mode 100644 index 0000000000..42936bc772 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/valid-second-signature.ts @@ -0,0 +1,27 @@ +import { crypto } from "@arkecosystem/crypto"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toHaveValidSecondSignature(value: object): R; + } + } +} + +expect.extend({ + toHaveValidSecondSignature: (actual, expected) => { + let verified; + + try { + verified = crypto.verifySecondSignature(actual, expected.publicKey); + } catch (e) {} // tslint:disable-line + + return { + message: () => "Expected value to have a valid second signature", + pass: !!verified, + }; + }, +}); diff --git a/packages/core-jest-matchers/src/transactions/valid.ts b/packages/core-jest-matchers/src/transactions/valid.ts new file mode 100644 index 0000000000..580a1cbd44 --- /dev/null +++ b/packages/core-jest-matchers/src/transactions/valid.ts @@ -0,0 +1,21 @@ +import { crypto } from "@arkecosystem/crypto"; + +export {}; + +declare global { + namespace jest { + // tslint:disable-next-line:interface-name + interface Matchers { + toBeValidTransaction(): R; + } + } +} + +expect.extend({ + toBeValidTransaction: (transaction, network) => { + return { + message: () => "Expected value to be a valid transaction", + pass: crypto.verify(transaction), + }; + }, +}); diff --git a/packages/core-jest-matchers/tsconfig.json b/packages/core-jest-matchers/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-jest-matchers/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-json-rpc/CHANGELOG.md b/packages/core-json-rpc/CHANGELOG.md deleted file mode 100644 index fe8128a6d3..0000000000 --- a/packages/core-json-rpc/CHANGELOG.md +++ /dev/null @@ -1,34 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.1 - 2018-12-06 - -### Fixed - -- Return the encoded WIF for BIP38 wallets instead of the encrypted WIF - -## 0.2.0 - 2018-12-03 - -### Changed - -- Moved all API calls from v1 to v2 -- Using in-memory peer list rather then fetching it via API -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Sign transaction _after_ filling in the recipientId and amount -- Fixed the failing test-suite -- BIP38 functionality - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-json-rpc/LICENSE b/packages/core-json-rpc/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-json-rpc/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-json-rpc/README.md b/packages/core-json-rpc/README.md index cb084b89e4..d61b9e34b7 100644 --- a/packages/core-json-rpc/README.md +++ b/packages/core-json-rpc/README.md @@ -14,9 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [François-Xavier Thoorens](https://github.com/fix) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-json-rpc/__tests__/__support__/request.js b/packages/core-json-rpc/__tests__/__support__/request.js deleted file mode 100644 index 284c41c970..0000000000 --- a/packages/core-json-rpc/__tests__/__support__/request.js +++ /dev/null @@ -1,18 +0,0 @@ -const axios = require('axios') -const uuid = require('uuid/v4') - -module.exports = async (method, params) => { - const id = uuid() - const response = await axios.post('http://localhost:8080/', { - jsonrpc: '2.0', - id, - method, - params, - }) - - await expect(response.status).toBe(200) - await expect(response.data.jsonrpc).toBe('2.0') - await expect(response.data.id).toBe(id) - - return response -} diff --git a/packages/core-json-rpc/__tests__/__support__/request.ts b/packages/core-json-rpc/__tests__/__support__/request.ts new file mode 100644 index 0000000000..a1bd1f6f37 --- /dev/null +++ b/packages/core-json-rpc/__tests__/__support__/request.ts @@ -0,0 +1,18 @@ +import axios from "axios"; +import uuid from "uuid/v4"; + +export async function sendRequest(method, params: any = {}) { + const id = uuid(); + const response = await axios.post("http://localhost:8080/", { + jsonrpc: "2.0", + id, + method, + params, + }); + + await expect(response.status).toBe(200); + await expect(response.data.jsonrpc).toBe("2.0"); + await expect(response.data.id).toBe(id); + + return response; +} diff --git a/packages/core-json-rpc/__tests__/__support__/setup.js b/packages/core-json-rpc/__tests__/__support__/setup.js deleted file mode 100644 index aefbf2fd59..0000000000 --- a/packages/core-json-rpc/__tests__/__support__/setup.js +++ /dev/null @@ -1,21 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(60000) - -exports.setUp = async () => { - process.env.ARK_JSON_RPC_ENABLED = true - - await appHelper.setUp({ - exclude: [ - '@arkecosystem/core-api', - '@arkecosystem/core-webhooks', - '@arkecosystem/core-graphql', - '@arkecosystem/core-forger', - ], - }) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-json-rpc/__tests__/__support__/setup.ts b/packages/core-json-rpc/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..4290b0cbc8 --- /dev/null +++ b/packages/core-json-rpc/__tests__/__support__/setup.ts @@ -0,0 +1,38 @@ +import { app } from "@arkecosystem/core-container"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; + +jest.setTimeout(60000); + +const options = { + enabled: true, + host: "0.0.0.0", + port: 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], +}; + +export async function setUp() { + // @ts-ignore + process.env.CORE_JSON_RPC_ENABLED = true; + + await setUpContainer({ + exclude: [ + "@arkecosystem/core-webhooks", + "@arkecosystem/core-graphql", + "@arkecosystem/core-forger", + "@arkecosystem/core-json-rpc", + ], + }); + + const { plugin } = require("../../src"); + await registerWithContainer(plugin, options); + + return app; +} + +export async function tearDown() { + await app.tearDown(); + + const { plugin } = require("../../src"); + await plugin.deregister(app, options); +} diff --git a/packages/core-json-rpc/__tests__/blocks.test.js b/packages/core-json-rpc/__tests__/blocks.test.js deleted file mode 100644 index 8f6d157ace..0000000000 --- a/packages/core-json-rpc/__tests__/blocks.test.js +++ /dev/null @@ -1,95 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const request = require('./__support__/request') - -const app = require('./__support__/setup') - -const axiosMock = new MockAdapter(axios) - -jest.mock('is-reachable', () => jest.fn(async peer => true)) - -let peerMock - -beforeAll(async () => { - await app.setUp() - - const Peer = require('@arkecosystem/core-p2p/lib/peer') - peerMock = new Peer('0.0.0.99', 4002) - Object.assign(peerMock, peerMock.headers, { status: 'OK' }) - - const monitor = require('@arkecosystem/core-container').resolvePlugin('p2p') - monitor.peers = {} - monitor.peers[peerMock.ip] = peerMock -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(async () => { - axiosMock.onPost(/.*:8080.*/).passThrough() -}) - -afterEach(async () => { - axiosMock.reset() // important: resets any existing mocking behavior -}) - -describe('Blocks', () => { - describe('POST blocks.latest', () => { - it('should get the latest block', async () => { - axiosMock - .onGet(/.*\/api\/blocks/) - .reply(() => [200, { data: [{ id: '123' }] }, peerMock.headers]) - - const response = await request('blocks.latest') - - expect(response.data.result.id).toBeString() - }) - }) - - describe('POST blocks.info', () => { - it('should get the block information', async () => { - axiosMock - .onGet(/.*\/api\/blocks\/123/) - .reply(() => [200, { data: { id: '123' } }, peerMock.headers]) - - const response = await request('blocks.info', { - id: '123', - }) - - expect(response.data.result.id).toBe('123') - }) - - it('should fail to get the block information', async () => { - const response = await request('blocks.info', { id: '123' }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe('Block 123 could not be found.') - }) - }) - - describe('POST blocks.transactions', () => { - it('should get the block transactions', async () => { - axiosMock - .onGet(/.*\/api\/blocks\/123\/transactions/) - .reply(() => [ - 200, - { meta: { totalCount: 1 }, data: [{ id: '123' }, { id: '123' }] }, - peerMock.headers, - ]) - - const response = await request('blocks.transactions', { - id: '123', - }) - - expect(response.data.result.data).toHaveLength(2) - }) - - it('should fail to get the block transactions', async () => { - const response = await request('blocks.transactions', { id: '123' }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe('Block 123 could not be found.') - }) - }) -}) diff --git a/packages/core-json-rpc/__tests__/blocks.test.ts b/packages/core-json-rpc/__tests__/blocks.test.ts new file mode 100644 index 0000000000..5b38a90015 --- /dev/null +++ b/packages/core-json-rpc/__tests__/blocks.test.ts @@ -0,0 +1,101 @@ +import "jest-extended"; + +import { app } from "@arkecosystem/core-container"; +import { Peer } from "@arkecosystem/core-p2p/src/peer"; +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { sendRequest } from "./__support__/request"; +import { setUp, tearDown } from "./__support__/setup"; + +const axiosMock = new MockAdapter(axios); + +jest.mock("is-reachable", () => jest.fn(async peer => true)); + +let peerMock; + +beforeAll(async () => { + await setUp(); + + peerMock = new Peer("1.0.0.99", 4002); + Object.assign(peerMock, peerMock.headers, { status: "OK" }); + + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + monitor.peers[peerMock.ip] = peerMock; +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(async () => { + axiosMock.onPost(/.*:8080.*/).passThrough(); +}); + +afterEach(async () => { + axiosMock.reset(); // important: resets any existing mocking behavior +}); + +describe("Blocks", () => { + describe("POST blocks.latest", () => { + it("should get the latest block", async () => { + axiosMock.onGet(/.*\/api\/blocks/).reply(() => [200, { data: [{ id: "123" }] }, peerMock.headers]); + + const response = await sendRequest("blocks.latest"); + + expect(response.data.result.id).toBeString(); + }); + + it("should not find the latest block", async () => { + axiosMock.onGet(/.*\/api\/blocks/).reply(() => [404, null, peerMock.headers]); + + const response = await sendRequest("blocks.latest"); + + expect(response.data.error.message).toBe("Latest block could not be found."); + }); + }); + + describe("POST blocks.info", () => { + it("should get the block information", async () => { + axiosMock.onGet(/.*\/api\/blocks\/123/).reply(() => [200, { data: { id: "123" } }, peerMock.headers]); + + const response = await sendRequest("blocks.info", { + id: "123", + }); + + expect(response.data.result.id).toBe("123"); + }); + + it("should fail to get the block information", async () => { + const response = await sendRequest("blocks.info", { id: "123" }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe("Block 123 could not be found."); + }); + }); + + describe("POST blocks.transactions", () => { + it("should get the block transactions", async () => { + axiosMock + .onGet(/.*\/api\/blocks\/123\/transactions/) + .reply(() => [ + 200, + { meta: { totalCount: 1 }, data: [{ id: "123" }, { id: "123" }] }, + peerMock.headers, + ]); + + const response = await sendRequest("blocks.transactions", { + id: "123", + }); + + expect(response.data.result.data).toHaveLength(2); + }); + + it("should fail to get the block transactions", async () => { + const response = await sendRequest("blocks.transactions", { id: "123" }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe("Block 123 could not be found."); + }); + }); +}); diff --git a/packages/core-json-rpc/__tests__/transactions.test.js b/packages/core-json-rpc/__tests__/transactions.test.js deleted file mode 100644 index 48eb2013b0..0000000000 --- a/packages/core-json-rpc/__tests__/transactions.test.js +++ /dev/null @@ -1,156 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') - -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const app = require('./__support__/setup') -const request = require('./__support__/request') - -const axiosMock = new MockAdapter(axios) - -jest.mock('is-reachable', () => jest.fn(async peer => true)) - -let peerMock - -beforeAll(async () => { - await app.setUp() - - const Peer = require('@arkecosystem/core-p2p/lib/peer') - peerMock = new Peer('0.0.0.99', 4002) - Object.assign(peerMock, peerMock.headers, { status: 'OK' }) - - const monitor = require('@arkecosystem/core-container').resolvePlugin('p2p') - monitor.peers = {} - monitor.peers[peerMock.ip] = peerMock -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(async () => { - axiosMock.onPost(/.*:8080.*/).passThrough() -}) - -afterEach(async () => { - axiosMock.reset() // important: resets any existing mocking behavior -}) - -describe('Transactions', () => { - describe('POST transactions.info', () => { - it('should get the transaction for the given ID', async () => { - axiosMock.onGet(/.*\/api\/transactions/).reply(() => [ - 200, - { - data: { - id: - 'e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8', - }, - }, - peerMock.headers, - ]) - - const response = await request('transactions.info', { - id: 'e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8', - }) - - expect(response.data.result.id).toBe( - 'e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8', - ) - }) - - it('should fail to get the transaction for the given ID', async () => { - const response = await request('transactions.info', { - id: 'e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8', - }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe( - 'Transaction e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8 could not be found.', - ) - }) - }) - - describe('POST transactions.create', () => { - it('should create a new transaction and verify', async () => { - const response = await request('transactions.create', { - amount: 100000000, - recipientId: 'APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn', - passphrase: 'this is a top secret passphrase', - }) - - expect(response.data.result.recipientId).toBe( - 'APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn', - ) - expect(crypto.verify(response.data.result)).toBeTrue() - }) - }) - - describe('POST transactions.broadcast', () => { - it('should broadcast the transaction', async () => { - const transaction = await request('transactions.create', { - amount: 100000000, - recipientId: 'APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn', - passphrase: 'this is a top secret passphrase', - }) - - axiosMock - .onPost(/.*\/api\/transactions/) - .reply(() => [200, { success: true }, peerMock.headers]) - - const response = await request('transactions.broadcast', { - id: transaction.data.result.id, - }) - - expect(crypto.verify(response.data.result)).toBeTrue() - }) - - it('should fail to broadcast the transaction', async () => { - const response = await request('transactions.broadcast', { - id: 'e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8', - }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe( - 'Transaction e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8 could not be found.', - ) - }) - }) - - describe('POST transactions.bip38.create', () => { - it('should create a new transaction', async () => { - const userId = require('crypto') - .randomBytes(32) - .toString('hex') - await request('wallets.bip38.create', { - bip38: 'this is a top secret passphrase', - userId, - }) - - const response = await request('transactions.bip38.create', { - bip38: 'this is a top secret passphrase', - userId, - amount: 1000000000, - recipientId: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - }) - - expect(response.data.result.recipientId).toBe( - 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - ) - expect(crypto.verify(response.data.result)).toBeTrue() - }) - - it('should fail to create a new transaction', async () => { - const response = await request('transactions.bip38.create', { - bip38: 'this is a top secret passphrase', - userId: '123456789', - amount: 1000000000, - recipientId: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe( - 'User 123456789 could not be found.', - ) - }) - }) -}) diff --git a/packages/core-json-rpc/__tests__/transactions.test.ts b/packages/core-json-rpc/__tests__/transactions.test.ts new file mode 100644 index 0000000000..f86e5b1bb7 --- /dev/null +++ b/packages/core-json-rpc/__tests__/transactions.test.ts @@ -0,0 +1,147 @@ +import "jest-extended"; + +import { app } from "@arkecosystem/core-container"; +import { Peer } from "@arkecosystem/core-p2p/dist/peer"; +import { crypto } from "@arkecosystem/crypto"; +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { sendRequest } from "./__support__/request"; +import { setUp, tearDown } from "./__support__/setup"; + +const axiosMock = new MockAdapter(axios); + +jest.mock("is-reachable", () => jest.fn(async peer => true)); + +let peerMock; + +beforeAll(async () => { + await setUp(); + + peerMock = new Peer("1.0.0.99", 4002); + Object.assign(peerMock, peerMock.headers, { status: "OK" }); + + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + monitor.peers[peerMock.ip] = peerMock; +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(async () => { + axiosMock.onPost(/.*:8080.*/).passThrough(); +}); + +afterEach(async () => { + axiosMock.reset(); // important: resets any existing mocking behavior +}); + +describe("Transactions", () => { + describe("POST transactions.info", () => { + it("should get the transaction for the given ID", async () => { + axiosMock.onGet(/.*\/api\/transactions/).reply(() => [ + 200, + { + data: { + id: "e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8", + }, + }, + peerMock.headers, + ]); + + const response = await sendRequest("transactions.info", { + id: "e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8", + }); + + expect(response.data.result.id).toBe("e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8"); + }); + + it("should fail to get the transaction for the given ID", async () => { + const response = await sendRequest("transactions.info", { + id: "e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8", + }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe( + "Transaction e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8 could not be found.", + ); + }); + }); + + describe("POST transactions.create", () => { + it("should create a new transaction and verify", async () => { + const response = await sendRequest("transactions.create", { + amount: 100000000, + recipientId: "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + passphrase: "this is a top secret passphrase", + }); + + expect(response.data.result.recipientId).toBe("APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn"); + expect(crypto.verify(response.data.result)).toBeTrue(); + }); + }); + + describe("POST transactions.broadcast", () => { + it("should broadcast the transaction", async () => { + const transaction = await sendRequest("transactions.create", { + amount: 100000000, + recipientId: "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + passphrase: "this is a top secret passphrase", + }); + + axiosMock.onPost(/.*\/api\/transactions/).reply(() => [200, { success: true }, peerMock.headers]); + + const response = await sendRequest("transactions.broadcast", { + id: transaction.data.result.id, + }); + + expect(crypto.verify(response.data.result)).toBeTrue(); + }); + + it("should fail to broadcast the transaction", async () => { + const response = await sendRequest("transactions.broadcast", { + id: "e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8", + }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe( + "Transaction e4311204acf8a86ba833e494f5292475c6e9e0913fc455a12601b4b6b55818d8 could not be found.", + ); + }); + }); + + describe("POST transactions.bip38.create", () => { + it("should create a new transaction", async () => { + const userId = require("crypto") + .randomBytes(32) + .toString("hex"); + await sendRequest("wallets.bip38.create", { + bip38: "this is a top secret passphrase", + userId, + }); + + const response = await sendRequest("transactions.bip38.create", { + bip38: "this is a top secret passphrase", + userId, + amount: 1000000000, + recipientId: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv", + }); + + expect(response.data.result.recipientId).toBe("AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv"); + expect(crypto.verify(response.data.result)).toBeTrue(); + }); + + it("should fail to create a new transaction", async () => { + const response = await sendRequest("transactions.bip38.create", { + bip38: "this is a top secret passphrase", + userId: "123456789", + amount: 1000000000, + recipientId: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv", + }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe("User 123456789 could not be found."); + }); + }); +}); diff --git a/packages/core-json-rpc/__tests__/wallets.test.js b/packages/core-json-rpc/__tests__/wallets.test.js deleted file mode 100644 index 117fb209af..0000000000 --- a/packages/core-json-rpc/__tests__/wallets.test.js +++ /dev/null @@ -1,196 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const request = require('./__support__/request') -const app = require('./__support__/setup') - -const axiosMock = new MockAdapter(axios) - -jest.mock('is-reachable', () => jest.fn(async peer => true)) - -let peerMock - -beforeAll(async () => { - await app.setUp() - - const Peer = require('@arkecosystem/core-p2p/lib/peer') - peerMock = new Peer('0.0.0.99', 4002) - Object.assign(peerMock, peerMock.headers, { status: 'OK' }) - - const monitor = require('@arkecosystem/core-container').resolvePlugin('p2p') - monitor.peers = {} - monitor.peers[peerMock.ip] = peerMock -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(async () => { - axiosMock - .onGet(/.*\/api\/loader\/autoconfigure/) - .reply(() => [200, { network: {} }, peerMock.headers]) - axiosMock - .onGet(/.*\/peer\/status/) - .reply(() => [200, { success: true, height: 5 }, peerMock.headers]) - axiosMock.onGet(/.*\/peer\/list/).reply(() => [ - 200, - { - success: true, - peers: [ - { - status: 'OK', - ip: peerMock.ip, - port: 4002, - height: 5, - delay: 8, - }, - ], - }, - peerMock.headers, - ]) - axiosMock.onPost(/.*:8080.*/).passThrough() -}) - -afterEach(async () => { - axiosMock.reset() // important: resets any existing mocking behavior -}) - -describe('Wallets', () => { - describe('POST wallets.info', () => { - it('should get information about the given wallet', async () => { - axiosMock - .onGet(/.*\/api\/wallets\/AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv/) - .reply(() => [ - 200, - { data: { address: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv' } }, - peerMock.headers, - ]) - - const response = await request('wallets.info', { - address: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - }) - - expect(response.data.result.address).toBe( - 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - ) - }) - - it('should fail to get information about the given wallet', async () => { - axiosMock - .onGet(/.*\/api\/wallets\/AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv/) - .reply(() => [ - 404, - { - error: { - code: 404, - message: - 'Wallet AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv could not be found.', - }, - }, - peerMock.headers, - ]) - - const response = await request('wallets.info', { - address: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe( - 'Wallet AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv could not be found.', - ) - }) - }) - - describe('POST wallets.transactions', () => { - it('should get the transactions for the given wallet', async () => { - axiosMock - .onGet(/.*\/api\/transactions/) - .reply(() => [ - 200, - { meta: { totalCount: 2 }, data: [{ id: '123' }, { id: '1234' }] }, - peerMock.headers, - ]) - - const response = await request('wallets.transactions', { - address: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - }) - - expect(response.data.result.count).toBe(2) - expect(response.data.result.data).toHaveLength(2) - }) - - it('should fail to get transactions for the given wallet', async () => { - const response = await request('wallets.transactions', { - address: 'AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv', - }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe( - 'Wallet AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv could not be found.', - ) - }) - }) - - describe('POST wallets.create', () => { - it('should create a new wallet', async () => { - const response = await request('wallets.create', { - passphrase: 'this is a top secret passphrase', - }) - - expect(response.data.result.address).toBe( - 'AGeYmgbg2LgGxRW2vNNJvQ88PknEJsYizC', - ) - expect(response.data.result.publicKey).toBe( - '034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192', - ) - }) - }) - - describe('POST wallets.bip38.*', () => { - let bip38wif - const userId = require('crypto') - .randomBytes(32) - .toString('hex') - - describe('create', () => { - it('should create a new wallet', async () => { - const response = await request('wallets.bip38.create', { - bip38: 'this is a top secret passphrase', - userId, - }) - - expect(response.data.result).toHaveProperty('address') - expect(response.data.result).toHaveProperty('publicKey') - expect(response.data.result).toHaveProperty('wif') - - bip38wif = response.data.result.wif - }) - }) - - describe('info', () => { - it('should find the wallet for the given userId', async () => { - const response = await request('wallets.bip38.info', { - bip38: 'this is a top secret passphrase', - userId, - }) - - expect(response.data.result).toHaveProperty('address') - expect(response.data.result).toHaveProperty('publicKey') - expect(response.data.result).toHaveProperty('wif') - expect(response.data.result.wif).toBe(bip38wif) - }) - - it('should fail to find the wallet for the given userId', async () => { - const response = await request('wallets.bip38.info', { - bip38: 'invalid', - userId: '123456789', - }) - - expect(response.data.error.code).toBe(404) - expect(response.data.error.message).toBe( - 'User 123456789 could not be found.', - ) - }) - }) - }) -}) diff --git a/packages/core-json-rpc/__tests__/wallets.test.ts b/packages/core-json-rpc/__tests__/wallets.test.ts new file mode 100644 index 0000000000..67b43faad5 --- /dev/null +++ b/packages/core-json-rpc/__tests__/wallets.test.ts @@ -0,0 +1,178 @@ +import "jest-extended"; + +import { app } from "@arkecosystem/core-container"; +import { Peer } from "@arkecosystem/core-p2p/dist/peer"; +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { sendRequest } from "./__support__/request"; +import { setUp, tearDown } from "./__support__/setup"; + +const axiosMock = new MockAdapter(axios); + +jest.mock("is-reachable", () => jest.fn(async peer => true)); + +let peerMock; + +beforeAll(async () => { + await setUp(); + + peerMock = new Peer("1.0.0.99", 4002); + Object.assign(peerMock, peerMock.headers, { status: "OK" }); + + const monitor = app.resolvePlugin("p2p"); + monitor.peers = {}; + monitor.peers[peerMock.ip] = peerMock; +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(async () => { + axiosMock.onGet(/.*\/api\/loader\/autoconfigure/).reply(() => [200, { network: {} }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/status/).reply(() => [200, { success: true, height: 5 }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/list/).reply(() => [ + 200, + { + success: true, + peers: [ + { + status: "OK", + ip: peerMock.ip, + port: 4002, + height: 5, + delay: 8, + }, + ], + }, + peerMock.headers, + ]); + axiosMock.onPost(/.*:8080.*/).passThrough(); +}); + +afterEach(async () => { + axiosMock.reset(); // important: resets any existing mocking behavior +}); + +describe("Wallets", () => { + describe("POST wallets.info", () => { + it("should get information about the given wallet", async () => { + axiosMock + .onGet(/.*\/api\/wallets\/AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv/) + .reply(() => [200, { data: { address: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv" } }, peerMock.headers]); + + const response = await sendRequest("wallets.info", { + address: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv", + }); + + expect(response.data.result.address).toBe("AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv"); + }); + + it("should fail to get information about the given wallet", async () => { + axiosMock.onGet(/.*\/api\/wallets\/AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv/).reply(() => [ + 404, + { + error: { + code: 404, + message: "Wallet AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv could not be found.", + }, + }, + peerMock.headers, + ]); + + const response = await sendRequest("wallets.info", { + address: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv", + }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe("Wallet AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv could not be found."); + }); + }); + + describe("POST wallets.transactions", () => { + it("should get the transactions for the given wallet", async () => { + axiosMock + .onGet(/.*\/api\/transactions/) + .reply(() => [ + 200, + { meta: { totalCount: 2 }, data: [{ id: "123" }, { id: "1234" }] }, + peerMock.headers, + ]); + + const response = await sendRequest("wallets.transactions", { + address: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv", + }); + + expect(response.data.result.count).toBe(2); + expect(response.data.result.data).toHaveLength(2); + }); + + it("should fail to get transactions for the given wallet", async () => { + const response = await sendRequest("wallets.transactions", { + address: "AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv", + }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe("Wallet AUDud8tvyVZa67p3QY7XPRUTjRGnWQQ9Xv could not be found."); + }); + }); + + describe("POST wallets.create", () => { + it("should create a new wallet", async () => { + const response = await sendRequest("wallets.create", { + passphrase: "this is a top secret passphrase", + }); + + expect(response.data.result.address).toBe("AGeYmgbg2LgGxRW2vNNJvQ88PknEJsYizC"); + expect(response.data.result.publicKey).toBe( + "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + ); + }); + }); + + describe("POST wallets.bip38.*", () => { + let bip38wif; + const userId = require("crypto") + .randomBytes(32) + .toString("hex"); + + describe("create", () => { + it("should create a new wallet", async () => { + const response = await sendRequest("wallets.bip38.create", { + bip38: "this is a top secret passphrase", + userId, + }); + + expect(response.data.result).toHaveProperty("address"); + expect(response.data.result).toHaveProperty("publicKey"); + expect(response.data.result).toHaveProperty("wif"); + + bip38wif = response.data.result.wif; + }); + }); + + describe("info", () => { + it("should find the wallet for the given userId", async () => { + const response = await sendRequest("wallets.bip38.info", { + bip38: "this is a top secret passphrase", + userId, + }); + + expect(response.data.result).toHaveProperty("address"); + expect(response.data.result).toHaveProperty("publicKey"); + expect(response.data.result).toHaveProperty("wif"); + expect(response.data.result.wif).toBe(bip38wif); + }); + + it("should fail to find the wallet for the given userId", async () => { + const response = await sendRequest("wallets.bip38.info", { + bip38: "invalid", + userId: "123456789", + }); + + expect(response.data.error.code).toBe(404); + expect(response.data.error.message).toBe("User 123456789 could not be found."); + }); + }); + }); +}); diff --git a/packages/core-json-rpc/jest.config.js b/packages/core-json-rpc/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-json-rpc/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-json-rpc/lib/defaults.js b/packages/core-json-rpc/lib/defaults.js deleted file mode 100644 index c80fdad1f6..0000000000 --- a/packages/core-json-rpc/lib/defaults.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - database: { - uri: - process.env.ARK_JSON_RPC_DATABASE - || `sqlite://${process.env.ARK_PATH_DATA}/database/json-rpc.sqlite`, - options: {}, - }, -} diff --git a/packages/core-json-rpc/lib/index.js b/packages/core-json-rpc/lib/index.js deleted file mode 100644 index 96b85e112f..0000000000 --- a/packages/core-json-rpc/lib/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const database = require('./server/services/database') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'json-rpc', - async register(container, options) { - const logger = container.resolvePlugin('logger') - - if (!options.enabled) { - logger.info('JSON-RPC Server is disabled :grey_exclamation:') - - return - } - - database.init(options.database) - - return require('./server')(options) - }, - async deregister(container, options) { - if (options.enabled) { - container.resolvePlugin('logger').info('Stopping JSON-RPC Server') - - return container.resolvePlugin('json-rpc').stop() - } - }, -} diff --git a/packages/core-json-rpc/lib/server/handler.js b/packages/core-json-rpc/lib/server/handler.js deleted file mode 100644 index f68d062d1b..0000000000 --- a/packages/core-json-rpc/lib/server/handler.js +++ /dev/null @@ -1,11 +0,0 @@ -const processor = require('./services/processor') - -module.exports = { - method: 'POST', - path: '/', - async handler(request, h) { - return Array.isArray(request.payload) - ? processor.collection(request.server, request.payload) - : processor.resource(request.server, request.payload) - }, -} diff --git a/packages/core-json-rpc/lib/server/index.js b/packages/core-json-rpc/lib/server/index.js deleted file mode 100755 index f84e12adfb..0000000000 --- a/packages/core-json-rpc/lib/server/index.js +++ /dev/null @@ -1,54 +0,0 @@ -const { - createServer, - mountServer, - plugins, -} = require('@arkecosystem/core-http-utils') -const logger = require('@arkecosystem/core-container').resolvePlugin('logger') - -function registerMethods(server, group) { - Object.values(require(`./methods/${group}`)).forEach(method => { - server.app.schemas[method.name] = method.schema - - delete method.schema - - server.method(method) - }) -} - -/** - * Create a new hapi.js server. - * @param {Object} options - * @return {Hapi.Server} - */ -module.exports = async options => { - if (options.allowRemote) { - logger.warn( - 'JSON-RPC server allows remote connections, this is a potential security risk :warning:', - ) - } - - const server = await createServer({ - host: options.host, - port: options.port, - }) - - server.app.schemas = {} - - if (!options.allowRemote) { - await server.register({ - plugin: plugins.whitelist, - options: { - whitelist: options.whitelist, - name: 'JSON-RPC', - }, - }) - } - - registerMethods(server, 'wallets') - registerMethods(server, 'blocks') - registerMethods(server, 'transactions') - - server.route(require('./handler')) - - return mountServer('JSON-RPC', server) -} diff --git a/packages/core-json-rpc/lib/server/methods/blocks/index.js b/packages/core-json-rpc/lib/server/methods/blocks/index.js deleted file mode 100644 index d150e074cf..0000000000 --- a/packages/core-json-rpc/lib/server/methods/blocks/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = [ - require('./latest'), - require('./info'), - require('./transactions'), -] diff --git a/packages/core-json-rpc/lib/server/methods/blocks/info.js b/packages/core-json-rpc/lib/server/methods/blocks/info.js deleted file mode 100644 index bb696702f9..0000000000 --- a/packages/core-json-rpc/lib/server/methods/blocks/info.js +++ /dev/null @@ -1,19 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const network = require('../../services/network') - -module.exports = { - name: 'blocks.info', - async method(params) { - const response = await network.sendRequest(`blocks/${params.id}`) - - return response - ? response.data - : Boom.notFound(`Block ${params.id} could not be found.`) - }, - schema: { - id: Joi.number() - .unsafe() - .required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/blocks/latest.js b/packages/core-json-rpc/lib/server/methods/blocks/latest.js deleted file mode 100644 index 7375e84620..0000000000 --- a/packages/core-json-rpc/lib/server/methods/blocks/latest.js +++ /dev/null @@ -1,15 +0,0 @@ -const Boom = require('boom') -const network = require('../../services/network') - -module.exports = { - name: 'blocks.latest', - async method(params) { - const response = await network.sendRequest( - 'blocks?orderBy=height:desc&limit=1', - ) - - return response - ? response.data[0] - : Boom.notFound(`Latest block could not be found.`) - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/blocks/transactions.js b/packages/core-json-rpc/lib/server/methods/blocks/transactions.js deleted file mode 100644 index 75a53c21e9..0000000000 --- a/packages/core-json-rpc/lib/server/methods/blocks/transactions.js +++ /dev/null @@ -1,33 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const network = require('../../services/network') - -module.exports = { - name: 'blocks.transactions', - async method(params) { - const response = await network.sendRequest( - `blocks/${params.id}/transactions`, - { - offset: params.offset, - orderBy: 'timestamp:desc', - }, - ) - - if (!response) { - return Boom.notFound(`Block ${params.id} could not be found.`) - } - - return response - ? { - count: response.meta.totalCount, - data: response.data, - } - : {} - }, - schema: { - id: Joi.number() - .unsafe() - .required(), - offset: Joi.number().default(0), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/index.js b/packages/core-json-rpc/lib/server/methods/index.js deleted file mode 100644 index 3e4c7c25cd..0000000000 --- a/packages/core-json-rpc/lib/server/methods/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - ...require('./wallets'), - ...require('./blocks'), - ...require('./transactions'), -} diff --git a/packages/core-json-rpc/lib/server/methods/transactions/bip38/create.js b/packages/core-json-rpc/lib/server/methods/transactions/bip38/create.js deleted file mode 100644 index 48bd33b5f1..0000000000 --- a/packages/core-json-rpc/lib/server/methods/transactions/bip38/create.js +++ /dev/null @@ -1,37 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const { transactionBuilder } = require('@arkecosystem/crypto') -const database = require('../../../services/database') -const getBIP38Wallet = require('../../../utils/bip38-keys') - -module.exports = { - name: 'transactions.bip38.create', - async method(params) { - const wallet = await getBIP38Wallet(params.userId, params.bip38) - - if (!wallet) { - return Boom.notFound(`User ${params.userId} could not be found.`) - } - - const transaction = transactionBuilder - .transfer() - .recipientId(params.recipientId) - .amount(params.amount) - .signWithWif(wallet.wif) - .getStruct() - - await database.set(transaction.id, transaction) - - return transaction - }, - schema: { - amount: Joi.number().required(), - recipientId: Joi.string() - .length(34) - .required(), - bip38: Joi.string().required(), - userId: Joi.string() - .hex() - .required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/transactions/broadcast.js b/packages/core-json-rpc/lib/server/methods/transactions/broadcast.js deleted file mode 100644 index 004f5edc3d..0000000000 --- a/packages/core-json-rpc/lib/server/methods/transactions/broadcast.js +++ /dev/null @@ -1,27 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const { crypto } = require('@arkecosystem/crypto') -const network = require('../../services/network') -const database = require('../../services/database') - -module.exports = { - name: 'transactions.broadcast', - async method(params) { - const transaction = await database.get(params.id) - - if (!transaction) { - return Boom.notFound(`Transaction ${params.id} could not be found.`) - } - - if (!crypto.verify(transaction)) { - return Boom.badData() - } - - await network.broadcast(transaction) - - return transaction - }, - schema: { - id: Joi.string().length(64), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/transactions/create.js b/packages/core-json-rpc/lib/server/methods/transactions/create.js deleted file mode 100644 index 9e52bb14df..0000000000 --- a/packages/core-json-rpc/lib/server/methods/transactions/create.js +++ /dev/null @@ -1,24 +0,0 @@ -const Joi = require('joi') -const { transactionBuilder } = require('@arkecosystem/crypto') -const database = require('../../services/database') - -module.exports = { - name: 'transactions.create', - async method(params) { - const transaction = transactionBuilder - .transfer() - .recipientId(params.recipientId) - .amount(params.amount) - .sign(params.passphrase) - .getStruct() - - await database.set(transaction.id, transaction) - - return transaction - }, - schema: { - amount: Joi.number().required(), - recipientId: Joi.string().required(), - passphrase: Joi.string().required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/transactions/index.js b/packages/core-json-rpc/lib/server/methods/transactions/index.js deleted file mode 100644 index 9affc9f40d..0000000000 --- a/packages/core-json-rpc/lib/server/methods/transactions/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = [ - require('./bip38/create'), - require('./broadcast'), - require('./create'), - require('./info'), -] diff --git a/packages/core-json-rpc/lib/server/methods/transactions/info.js b/packages/core-json-rpc/lib/server/methods/transactions/info.js deleted file mode 100644 index 9594615ce4..0000000000 --- a/packages/core-json-rpc/lib/server/methods/transactions/info.js +++ /dev/null @@ -1,19 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const network = require('../../services/network') - -module.exports = { - name: 'transactions.info', - async method(params) { - const response = await network.sendRequest(`transactions/${params.id}`) - - return response - ? response.data - : Boom.notFound(`Transaction ${params.id} could not be found.`) - }, - schema: { - id: Joi.string() - .length(64) - .required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/wallets/bip38/create.js b/packages/core-json-rpc/lib/server/methods/wallets/bip38/create.js deleted file mode 100644 index d599937afa..0000000000 --- a/packages/core-json-rpc/lib/server/methods/wallets/bip38/create.js +++ /dev/null @@ -1,48 +0,0 @@ -const Joi = require('joi') -const { crypto, utils } = require('@arkecosystem/crypto') -const bip39 = require('bip39') -const bip38 = require('bip38') -const database = require('../../../services/database') -const getBIP38Wallet = require('../../../utils/bip38-keys') -const decryptWIF = require('../../../utils/decrypt-wif') - -module.exports = { - name: 'wallets.bip38.create', - async method(params) { - try { - const { keys, wif } = await getBIP38Wallet(params.userId, params.bip38) - - return { - publicKey: keys.publicKey, - address: crypto.getAddress(keys.publicKey), - wif, - } - } catch (error) { - const { publicKey, privateKey } = crypto.getKeys(bip39.generateMnemonic()) - - const encryptedWIF = bip38.encrypt( - Buffer.from(privateKey, 'hex'), - true, - params.bip38 + params.userId, - ) - await database.set( - utils.sha256(Buffer.from(params.userId)).toString('hex'), - encryptedWIF, - ) - - const { wif } = decryptWIF(encryptedWIF, params.userId, params.bip38) - - return { - publicKey, - address: crypto.getAddress(publicKey), - wif, - } - } - }, - schema: { - bip38: Joi.string().required(), - userId: Joi.string() - .hex() - .required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/wallets/bip38/show.js b/packages/core-json-rpc/lib/server/methods/wallets/bip38/show.js deleted file mode 100644 index dabfbb0092..0000000000 --- a/packages/core-json-rpc/lib/server/methods/wallets/bip38/show.js +++ /dev/null @@ -1,33 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const bip38 = require('bip38') -const { crypto, utils } = require('@arkecosystem/crypto') -const database = require('../../../services/database') -const decryptWIF = require('../../../utils/decrypt-wif') - -module.exports = { - name: 'wallets.bip38.info', - async method(params) { - const encryptedWIF = await database.get( - utils.sha256(Buffer.from(params.userId)).toString('hex'), - ) - - if (!encryptedWIF) { - return Boom.notFound(`User ${params.userId} could not be found.`) - } - - const { keys, wif } = decryptWIF(encryptedWIF, params.userId, params.bip38) - - return { - publicKey: keys.publicKey, - address: crypto.getAddress(keys.publicKey), - wif, - } - }, - schema: { - bip38: Joi.string().required(), - userId: Joi.string() - .hex() - .required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/wallets/create.js b/packages/core-json-rpc/lib/server/methods/wallets/create.js deleted file mode 100644 index c16b13a74e..0000000000 --- a/packages/core-json-rpc/lib/server/methods/wallets/create.js +++ /dev/null @@ -1,17 +0,0 @@ -const Joi = require('joi') -const { crypto } = require('@arkecosystem/crypto') - -module.exports = { - name: 'wallets.create', - async method(params) { - const { publicKey } = crypto.getKeys(params.passphrase) - - return { - publicKey, - address: crypto.getAddress(publicKey), - } - }, - schema: { - passphrase: Joi.string().required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/wallets/index.js b/packages/core-json-rpc/lib/server/methods/wallets/index.js deleted file mode 100644 index 3e124bf0c5..0000000000 --- a/packages/core-json-rpc/lib/server/methods/wallets/index.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = [ - require('./bip38/create'), - require('./bip38/show'), - require('./create'), - require('./info'), - require('./transactions'), -] diff --git a/packages/core-json-rpc/lib/server/methods/wallets/info.js b/packages/core-json-rpc/lib/server/methods/wallets/info.js deleted file mode 100644 index 35d14b8a3b..0000000000 --- a/packages/core-json-rpc/lib/server/methods/wallets/info.js +++ /dev/null @@ -1,19 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const network = require('../../services/network') - -module.exports = { - name: 'wallets.info', - async method(params) { - const response = await network.sendRequest(`wallets/${params.address}`) - - return response - ? response.data - : Boom.notFound(`Wallet ${params.address} could not be found.`) - }, - schema: { - address: Joi.string() - .length(34) - .required(), - }, -} diff --git a/packages/core-json-rpc/lib/server/methods/wallets/transactions.js b/packages/core-json-rpc/lib/server/methods/wallets/transactions.js deleted file mode 100644 index bfa27d1258..0000000000 --- a/packages/core-json-rpc/lib/server/methods/wallets/transactions.js +++ /dev/null @@ -1,29 +0,0 @@ -const Boom = require('boom') -const Joi = require('joi') -const network = require('../../services/network') - -module.exports = { - name: 'wallets.transactions', - async method(params) { - const response = await network.sendRequest('transactions', { - offset: params.offset, - orderBy: 'timestamp:desc', - ownerId: params.address, - }) - - if (!response) { - return Boom.notFound(`Wallet ${params.address} could not be found.`) - } - - return { - count: response.meta.totalCount, - data: response.data, - } - }, - schema: { - address: Joi.string() - .length(34) - .required(), - offset: Joi.number().default(0), - }, -} diff --git a/packages/core-json-rpc/lib/server/services/database.js b/packages/core-json-rpc/lib/server/services/database.js deleted file mode 100644 index 7025732cf5..0000000000 --- a/packages/core-json-rpc/lib/server/services/database.js +++ /dev/null @@ -1,25 +0,0 @@ -const Keyv = require('keyv') - -class Database { - init(options) { - this.database = new Keyv(options) - } - - async get(id) { - return this.database.get(id) - } - - async set(id, value) { - return this.database.set(id, value) - } - - async delete(id) { - return this.database.delete(id) - } - - async clear() { - return this.database.clear() - } -} - -module.exports = new Database() diff --git a/packages/core-json-rpc/lib/server/services/network.js b/packages/core-json-rpc/lib/server/services/network.js deleted file mode 100644 index cbca05e944..0000000000 --- a/packages/core-json-rpc/lib/server/services/network.js +++ /dev/null @@ -1,124 +0,0 @@ -const axios = require('axios') -const { configManager } = require('@arkecosystem/crypto') -const isReachable = require('is-reachable') -const sample = require('lodash/sample') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const p2p = app.resolvePlugin('p2p') -const config = app.resolvePlugin('config') - -class Network { - constructor() { - this.network = config.network - - this.__loadRemotePeers() - - configManager.setConfig(config.network) - - this.client = axios.create({ - headers: { - Accept: 'application/vnd.ark.core-api.v2+json', - 'Content-Type': 'application/json', - }, - timeout: 3000, - }) - } - - setServer() { - this.server = this.__getRandomPeer() - } - - async sendRequest(url, params = {}) { - if (!this.server) { - this.setServer() - } - - const peer = await this.__selectResponsivePeer(this.server) - const uri = `http://${peer.ip}:${peer.port}/api/${url}` - - try { - logger.info(`Sending request on "${this.network.name}" to "${uri}"`) - - const response = await this.client.get(uri, { params }) - - return response.data - } catch (error) { - logger.error(error.message) - } - } - - async broadcast(transaction) { - return this.client.post( - `http://${this.server.ip}:${this.server.port}/api/transactions`, - { - transactions: [transaction], - }, - ) - } - - async connect() { - if (this.server) { - // logger.info(`Server is already configured as "${this.server.ip}:${this.server.port}"`) - return - } - - this.setServer() - - try { - const peerPort = app.resolveOptions('p2p').port - const response = await axios.get( - `http://${this.server.ip}:${peerPort}/config`, - ) - - const plugin = response.data.data.plugins['@arkecosystem/core-api'] - - if (!plugin.enabled) { - const index = this.peers.findIndex(peer => peer.ip === this.server.ip) - this.peers.splice(index, 1) - - if (!this.peers.length) { - this.__loadRemotePeers() - } - - return this.connect() - } - - this.server.port = plugin.port - } catch (error) { - return this.connect() - } - } - - __getRandomPeer() { - this.__loadRemotePeers() - - return sample(this.peers) - } - - __loadRemotePeers() { - this.peers = - this.network.name === 'testnet' - ? [{ ip: '127.0.0.1', port: app.resolveOptions('api').port }] - : p2p.getPeers() - - if (!this.peers.length) { - logger.error('No peers found. Shutting down...') - process.exit() - } - } - - async __selectResponsivePeer(peer) { - const reachable = await isReachable(`${peer.ip}:${peer.port}`) - - if (!reachable) { - logger.warn(`${peer} is unresponsive. Choosing new peer.`) - - return this.__selectResponsivePeer(this.__getRandomPeer()) - } - - return peer - } -} - -module.exports = new Network() diff --git a/packages/core-json-rpc/lib/server/services/processor.js b/packages/core-json-rpc/lib/server/services/processor.js deleted file mode 100644 index 5a4592b778..0000000000 --- a/packages/core-json-rpc/lib/server/services/processor.js +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint no-shadow: "off" */ -/* eslint no-await-in-loop: "off" */ - -const Joi = require('joi') -const get = require('lodash/get') -const network = require('./network') - -class Processor { - async resource(server, payload) { - const { error } = Joi.validate(payload || {}, { - jsonrpc: Joi.string() - .valid('2.0') - .required(), - method: Joi.string().required(), - id: Joi.required(), - params: Joi.object(), - }) - - if (error) { - return this.__createErrorResponse( - payload ? payload.id : null, - -32600, - error, - ) - } - - const { method, params, id } = payload - - try { - const targetMethod = get(server.methods, method) - - if (!targetMethod) { - return this.__createErrorResponse( - id, - -32601, - 'The method does not exist / is not available.', - ) - } - - const schema = server.app.schemas[method] - - if (schema) { - const { error } = Joi.validate(params, schema) - - if (error) { - return this.__createErrorResponse(id, -32602, error) - } - } - - await network.connect() - - const result = await targetMethod(params) - - return result.isBoom - ? this.__createErrorResponse( - id, - result.output.statusCode, - result.output.payload, - ) - : this.__createSuccessResponse(id, result) - } catch (error) { - return this.__createErrorResponse(id, -32603, error) - } - } - - async collection(server, payload) { - const results = [] - - for (let i = 0; i < payload.length; i++) { - const result = await this.resource(server, payload[i]) - - results.push(result) - } - - return results - } - - __createSuccessResponse(id, result) { - return { - jsonrpc: '2.0', - id, - result, - } - } - - __createErrorResponse(id, code, error) { - return { - jsonrpc: '2.0', - id, - error: { - code, - message: error.message, - data: error.stack, - }, - } - } -} - -module.exports = new Processor() diff --git a/packages/core-json-rpc/lib/server/utils/bip38-keys.js b/packages/core-json-rpc/lib/server/utils/bip38-keys.js deleted file mode 100644 index c93f81b192..0000000000 --- a/packages/core-json-rpc/lib/server/utils/bip38-keys.js +++ /dev/null @@ -1,19 +0,0 @@ -const { configManager, crypto, utils } = require('@arkecosystem/crypto') -const bip38 = require('bip38') -const wif = require('wif') -const database = require('../services/database') -const decryptWIF = require('./decrypt-wif') - -module.exports = async (userId, bip38password) => { - try { - const encryptedWif = await database.get( - utils.sha256(Buffer.from(userId)).toString('hex'), - ) - - if (encryptedWif) { - return decryptWIF(encryptedWif, userId, bip38password) - } - } catch (error) { - throw Error('Could not find a matching WIF') - } -} diff --git a/packages/core-json-rpc/lib/server/utils/decrypt-wif.js b/packages/core-json-rpc/lib/server/utils/decrypt-wif.js deleted file mode 100644 index 83271fe7f4..0000000000 --- a/packages/core-json-rpc/lib/server/utils/decrypt-wif.js +++ /dev/null @@ -1,18 +0,0 @@ -const { configManager, crypto } = require('@arkecosystem/crypto') -const bip38 = require('bip38') -const wif = require('wif') - -module.exports = (encryptedWif, userId, bip38password) => { - const decrypted = bip38.decrypt( - encryptedWif.toString('hex'), - bip38password + userId, - ) - - const encodedWIF = wif.encode( - configManager.get('wif'), - decrypted.privateKey, - decrypted.compressed, - ) - - return { keys: crypto.getKeysFromWIF(encodedWIF), wif: encodedWIF } -} diff --git a/packages/core-json-rpc/package.json b/packages/core-json-rpc/package.json index 74b29cbf6f..9eea77bded 100644 --- a/packages/core-json-rpc/package.json +++ b/packages/core-json-rpc/package.json @@ -1,46 +1,69 @@ { - "name": "@arkecosystem/core-json-rpc", - "description": "A JSON-RPC 2.0 Specification compliant server to interact with the Ark Blockchain.", - "version": "0.2.1", - "contributors": [ - "François-Xavier Thoorens ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "@keyv/sqlite": "^2.0.0", - "axios": "^0.18.0", - "bip38": "^2.0.2", - "bip39": "^2.5.0", - "boom": "^7.3.0", - "is-reachable": "^3.0.0", - "joi": "^14.3.0", - "keyv": "^3.1.0", - "lodash.get": "^4.4.2", - "uuid": "^3.3.2", - "wif": "^2.0.6" - }, - "devDependencies": { - "@arkecosystem/core-p2p": "~0.2", - "@arkecosystem/core-test-utils": "~0.2", - "axios-mock-adapter": "^1.15.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-json-rpc", + "description": "A JSON-RPC 2.0 Specification compliant server to interact with the Ark Blockchain.", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@keyv/sqlite": "^2.0.0", + "@types/bip39": "^2.4.1", + "@types/boom": "^7.2.1", + "@types/joi": "^14.0.0", + "@types/keyv": "^3.0.1", + "@types/lodash.get": "^4.4.4", + "@types/uuid": "^3.4.4", + "@types/wif": "^2.0.1", + "axios": "^0.18.0", + "bip39": "^2.5.0", + "boom": "^7.3.0", + "is-reachable": "^3.0.0", + "joi": "^14.3.0", + "keyv": "^3.1.0", + "lodash.get": "^4.4.2", + "uuid": "^3.3.2", + "wif": "^2.0.6" + }, + "devDependencies": { + "@arkecosystem/core-p2p": "^2.1.0", + "@arkecosystem/core-test-utils": "^2.1.0", + "@types/is-reachable": "^3.0.0", + "@types/keyv__sqlite": "^2.0.0", + "axios-mock-adapter": "^1.15.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-json-rpc/src/defaults.ts b/packages/core-json-rpc/src/defaults.ts new file mode 100644 index 0000000000..e2f3eb84b8 --- /dev/null +++ b/packages/core-json-rpc/src/defaults.ts @@ -0,0 +1,11 @@ +export const defaults = { + enabled: process.env.CORE_JSON_RPC_ENABLED, + host: process.env.CORE_JSON_RPC_HOST || "0.0.0.0", + port: process.env.CORE_JSON_RPC_PORT || 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + database: { + uri: process.env.CORE_JSON_RPC_DATABASE || `sqlite://${process.env.CORE_PATH_DATA}/json-rpc.sqlite`, + options: {}, + }, +}; diff --git a/packages/core-json-rpc/src/index.ts b/packages/core-json-rpc/src/index.ts new file mode 100644 index 0000000000..384b06471b --- /dev/null +++ b/packages/core-json-rpc/src/index.ts @@ -0,0 +1,33 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { startServer } from "./server"; +import { database } from "./server/services/database"; +import { network } from "./server/services/network"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "json-rpc", + async register(container: Container.IContainer, options) { + const logger = container.resolvePlugin("logger"); + + if (!options.enabled) { + logger.info("JSON-RPC Server is disabled :grey_exclamation:"); + + return; + } + + database.init(options.database); + + await network.init(); + + return startServer(options); + }, + async deregister(container: Container.IContainer, options) { + if (options.enabled) { + container.resolvePlugin("logger").info("Stopping JSON-RPC Server"); + + return container.resolvePlugin("json-rpc").stop(); + } + }, +}; diff --git a/packages/core-json-rpc/src/server/index.ts b/packages/core-json-rpc/src/server/index.ts new file mode 100755 index 0000000000..fc1a9b5715 --- /dev/null +++ b/packages/core-json-rpc/src/server/index.ts @@ -0,0 +1,47 @@ +import { app } from "@arkecosystem/core-container"; +import { createServer, mountServer, plugins } from "@arkecosystem/core-http-utils"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { registerMethods } from "./methods"; +import { Processor } from "./services/processor"; + +export async function startServer(options) { + if (options.allowRemote) { + app.resolvePlugin("logger").warn( + "JSON-RPC server allows remote connections, this is a potential security risk :warning:", + ); + } + + const server = await createServer({ + host: options.host, + port: options.port, + }); + + // @ts-ignore + server.app.schemas = {}; + + if (!options.allowRemote) { + await server.register({ + plugin: plugins.whitelist, + options: { + whitelist: options.whitelist, + name: "JSON-RPC", + }, + }); + } + + registerMethods(server); + + server.route({ + method: "POST", + path: "/", + async handler(request, h) { + const processor = new Processor(); + + return Array.isArray(request.payload) + ? processor.collection(request.server, request.payload) + : processor.resource(request.server, request.payload); + }, + }); + + return mountServer("JSON-RPC", server); +} diff --git a/packages/core-json-rpc/src/server/methods/blocks/info.ts b/packages/core-json-rpc/src/server/methods/blocks/info.ts new file mode 100644 index 0000000000..87ff9e53ac --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/blocks/info.ts @@ -0,0 +1,18 @@ +import Boom from "boom"; +import Joi from "joi"; +import { network } from "../../services/network"; + +export const blockInfo = { + name: "blocks.info", + async method(params) { + const response = await network.sendRequest(`blocks/${params.id}`); + + return response ? response.data : Boom.notFound(`Block ${params.id} could not be found.`); + }, + schema: { + id: Joi.number() + // @ts-ignore + .unsafe() + .required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/blocks/latest.ts b/packages/core-json-rpc/src/server/methods/blocks/latest.ts new file mode 100644 index 0000000000..7e5fdfd332 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/blocks/latest.ts @@ -0,0 +1,11 @@ +import Boom from "boom"; +import { network } from "../../services/network"; + +export const blockLatest = { + name: "blocks.latest", + async method(params) { + const response = await network.sendRequest("blocks?orderBy=height:desc&limit=1"); + + return response ? response.data[0] : Boom.notFound(`Latest block could not be found.`); + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/blocks/transactions.ts b/packages/core-json-rpc/src/server/methods/blocks/transactions.ts new file mode 100644 index 0000000000..3bd0aae721 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/blocks/transactions.ts @@ -0,0 +1,27 @@ +import Boom from "boom"; +import Joi from "joi"; +import { network } from "../../services/network"; + +export const blockTransactions = { + name: "blocks.transactions", + async method(params) { + const response = await network.sendRequest(`blocks/${params.id}/transactions`, { + offset: params.offset, + orderBy: "timestamp:desc", + }); + + return response + ? { + count: response.meta.totalCount, + data: response.data, + } + : Boom.notFound(`Block ${params.id} could not be found.`); + }, + schema: { + id: Joi.number() + // @ts-ignore + .unsafe() + .required(), + offset: Joi.number().default(0), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/index.ts b/packages/core-json-rpc/src/server/methods/index.ts new file mode 100644 index 0000000000..536380626e --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/index.ts @@ -0,0 +1,40 @@ +import Hapi from "hapi"; + +import { blockInfo } from "./blocks/info"; +import { blockLatest } from "./blocks/latest"; +import { blockTransactions } from "./blocks/transactions"; + +import { walletBIP38Create } from "./wallets/bip38/create"; +import { walletBIP38 } from "./wallets/bip38/show"; +import { walletCreate } from "./wallets/create"; +import { walletInfo } from "./wallets/info"; +import { walletTransactions } from "./wallets/transactions"; + +import { transactionBIP38Create } from "./transactions/bip38/create"; +import { transactionBroadcast } from "./transactions/broadcast"; +import { transactionCreate } from "./transactions/create"; +import { transactionInfo } from "./transactions/info"; + +export function registerMethods(server: Hapi.Server) { + const registerMethod = method => { + // @ts-ignore + server.app.schemas[method.name] = method.schema; + + delete method.schema; + + server.method(method); + }; + + registerMethod(blockLatest); + registerMethod(blockInfo); + registerMethod(blockTransactions); + registerMethod(walletBIP38Create); + registerMethod(walletBIP38); + registerMethod(walletCreate); + registerMethod(walletInfo); + registerMethod(walletTransactions); + registerMethod(transactionBIP38Create); + registerMethod(transactionBroadcast); + registerMethod(transactionCreate); + registerMethod(transactionInfo); +} diff --git a/packages/core-json-rpc/src/server/methods/transactions/bip38/create.ts b/packages/core-json-rpc/src/server/methods/transactions/bip38/create.ts new file mode 100644 index 0000000000..279314f6e7 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/transactions/bip38/create.ts @@ -0,0 +1,37 @@ +import { transactionBuilder } from "@arkecosystem/crypto"; +import Boom from "boom"; +import Joi from "joi"; +import { database } from "../../../services/database"; +import { getBIP38Wallet } from "../../../utils/bip38-keys"; + +export const transactionBIP38Create = { + name: "transactions.bip38.create", + async method(params) { + const wallet = await getBIP38Wallet(params.userId, params.bip38); + + if (!wallet) { + return Boom.notFound(`User ${params.userId} could not be found.`); + } + + const transaction = transactionBuilder + .transfer() + .recipientId(params.recipientId) + .amount(params.amount) + .signWithWif(wallet.wif) + .getStruct(); + + await database.set(transaction.id, transaction); + + return transaction; + }, + schema: { + amount: Joi.number().required(), + recipientId: Joi.string() + .length(34) + .required(), + bip38: Joi.string().required(), + userId: Joi.string() + .hex() + .required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/transactions/broadcast.ts b/packages/core-json-rpc/src/server/methods/transactions/broadcast.ts new file mode 100644 index 0000000000..470f7d62cf --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/transactions/broadcast.ts @@ -0,0 +1,27 @@ +import { crypto } from "@arkecosystem/crypto"; +import Boom from "boom"; +import Joi from "joi"; +import { database } from "../../services/database"; +import { network } from "../../services/network"; + +export const transactionBroadcast = { + name: "transactions.broadcast", + async method(params) { + const transaction = await database.get(params.id); + + if (!transaction) { + return Boom.notFound(`Transaction ${params.id} could not be found.`); + } + + if (!crypto.verify(transaction)) { + return Boom.badData(); + } + + await network.broadcast(transaction); + + return transaction; + }, + schema: { + id: Joi.string().length(64), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/transactions/create.ts b/packages/core-json-rpc/src/server/methods/transactions/create.ts new file mode 100644 index 0000000000..ac8e7986b3 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/transactions/create.ts @@ -0,0 +1,24 @@ +import { transactionBuilder } from "@arkecosystem/crypto"; +import Joi from "joi"; +import { database } from "../../services/database"; + +export const transactionCreate = { + name: "transactions.create", + async method(params) { + const transaction = transactionBuilder + .transfer() + .recipientId(params.recipientId) + .amount(params.amount) + .sign(params.passphrase) + .getStruct(); + + await database.set(transaction.id, transaction); + + return transaction; + }, + schema: { + amount: Joi.number().required(), + recipientId: Joi.string().required(), + passphrase: Joi.string().required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/transactions/info.ts b/packages/core-json-rpc/src/server/methods/transactions/info.ts new file mode 100644 index 0000000000..3a4cbe4446 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/transactions/info.ts @@ -0,0 +1,17 @@ +import Boom from "boom"; +import Joi from "joi"; +import { network } from "../../services/network"; + +export const transactionInfo = { + name: "transactions.info", + async method(params) { + const response = await network.sendRequest(`transactions/${params.id}`); + + return response ? response.data : Boom.notFound(`Transaction ${params.id} could not be found.`); + }, + schema: { + id: Joi.string() + .length(64) + .required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/wallets/bip38/create.ts b/packages/core-json-rpc/src/server/methods/wallets/bip38/create.ts new file mode 100644 index 0000000000..cb29c6c1f0 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/wallets/bip38/create.ts @@ -0,0 +1,40 @@ +import { bip38, crypto, HashAlgorithms } from "@arkecosystem/crypto"; +import bip39 from "bip39"; +import Joi from "joi"; +import { database } from "../../../services/database"; +import { getBIP38Wallet } from "../../../utils/bip38-keys"; +import { decryptWIF } from "../../../utils/decrypt-wif"; + +export const walletBIP38Create = { + name: "wallets.bip38.create", + async method(params) { + try { + const { keys, wif } = await getBIP38Wallet(params.userId, params.bip38); + + return { + publicKey: keys.publicKey, + address: crypto.getAddress(keys.publicKey), + wif, + }; + } catch (error) { + const { publicKey, privateKey } = crypto.getKeys(bip39.generateMnemonic()); + + const encryptedWIF = bip38.encrypt(Buffer.from(privateKey, "hex"), true, params.bip38 + params.userId); + await database.set(HashAlgorithms.sha256(Buffer.from(params.userId)).toString("hex"), encryptedWIF); + + const { wif } = decryptWIF(encryptedWIF, params.userId, params.bip38); + + return { + publicKey, + address: crypto.getAddress(publicKey), + wif, + }; + } + }, + schema: { + bip38: Joi.string().required(), + userId: Joi.string() + .hex() + .required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/wallets/bip38/show.ts b/packages/core-json-rpc/src/server/methods/wallets/bip38/show.ts new file mode 100644 index 0000000000..192711a3e9 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/wallets/bip38/show.ts @@ -0,0 +1,30 @@ +import { crypto, HashAlgorithms } from "@arkecosystem/crypto"; +import Boom from "boom"; +import Joi from "joi"; +import { database } from "../../../services/database"; +import { decryptWIF } from "../../../utils/decrypt-wif"; + +export const walletBIP38 = { + name: "wallets.bip38.info", + async method(params) { + const encryptedWIF = await database.get(HashAlgorithms.sha256(Buffer.from(params.userId)).toString("hex")); + + if (!encryptedWIF) { + return Boom.notFound(`User ${params.userId} could not be found.`); + } + + const { keys, wif } = decryptWIF(encryptedWIF, params.userId, params.bip38); + + return { + publicKey: keys.publicKey, + address: crypto.getAddress(keys.publicKey), + wif, + }; + }, + schema: { + bip38: Joi.string().required(), + userId: Joi.string() + .hex() + .required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/wallets/create.ts b/packages/core-json-rpc/src/server/methods/wallets/create.ts new file mode 100644 index 0000000000..e72ff695f0 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/wallets/create.ts @@ -0,0 +1,17 @@ +import { crypto } from "@arkecosystem/crypto"; +import Joi from "joi"; + +export const walletCreate = { + name: "wallets.create", + async method(params) { + const { publicKey } = crypto.getKeys(params.passphrase); + + return { + publicKey, + address: crypto.getAddress(publicKey), + }; + }, + schema: { + passphrase: Joi.string().required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/wallets/info.ts b/packages/core-json-rpc/src/server/methods/wallets/info.ts new file mode 100644 index 0000000000..96dd4ca712 --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/wallets/info.ts @@ -0,0 +1,17 @@ +import Boom from "boom"; +import Joi from "joi"; +import { network } from "../../services/network"; + +export const walletInfo = { + name: "wallets.info", + async method(params) { + const response = await network.sendRequest(`wallets/${params.address}`); + + return response ? response.data : Boom.notFound(`Wallet ${params.address} could not be found.`); + }, + schema: { + address: Joi.string() + .length(34) + .required(), + }, +}; diff --git a/packages/core-json-rpc/src/server/methods/wallets/transactions.ts b/packages/core-json-rpc/src/server/methods/wallets/transactions.ts new file mode 100644 index 0000000000..ddfee4965b --- /dev/null +++ b/packages/core-json-rpc/src/server/methods/wallets/transactions.ts @@ -0,0 +1,29 @@ +import Boom from "boom"; +import Joi from "joi"; +import { network } from "../../services/network"; + +export const walletTransactions = { + name: "wallets.transactions", + async method(params) { + const response = await network.sendRequest("transactions", { + offset: params.offset, + orderBy: "timestamp:desc", + ownerId: params.address, + }); + + if (!response) { + return Boom.notFound(`Wallet ${params.address} could not be found.`); + } + + return { + count: response.meta.totalCount, + data: response.data, + }; + }, + schema: { + address: Joi.string() + .length(34) + .required(), + offset: Joi.number().default(0), + }, +}; diff --git a/packages/core-json-rpc/src/server/services/database.ts b/packages/core-json-rpc/src/server/services/database.ts new file mode 100644 index 0000000000..aac4224fb9 --- /dev/null +++ b/packages/core-json-rpc/src/server/services/database.ts @@ -0,0 +1,27 @@ +import Keyv from "keyv"; + +class Database { + public database: any; + + public init(options) { + this.database = new Keyv(options); + } + + public async get(id) { + return this.database.get(id); + } + + public async set(id, value) { + return this.database.set(id, value); + } + + public async delete(id) { + return this.database.delete(id); + } + + public async clear() { + return this.database.clear(); + } +} + +export const database = new Database(); diff --git a/packages/core-json-rpc/src/server/services/network.ts b/packages/core-json-rpc/src/server/services/network.ts new file mode 100644 index 0000000000..d8789900f5 --- /dev/null +++ b/packages/core-json-rpc/src/server/services/network.ts @@ -0,0 +1,126 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger, P2P } from "@arkecosystem/core-interfaces"; +import { configManager } from "@arkecosystem/crypto"; +import axios from "axios"; +import isReachable from "is-reachable"; +import sample from "lodash/sample"; + +class Network { + public logger: Logger.ILogger; + public p2p: P2P.IMonitor; + public config: any; + public network: any; + public client: any; + public peers: any; + public server: any; + + public async init() { + this.logger = app.resolvePlugin("logger"); + this.config = app.getConfig(); + this.p2p = app.resolvePlugin("p2p"); + + this.network = configManager.all(); + + this.loadRemotePeers(); + + this.client = axios.create({ + headers: { + Accept: "application/vnd.core-api.v2+json", + "Content-Type": "application/json", + }, + timeout: 3000, + }); + } + + public setServer() { + this.server = this.getRandomPeer(); + } + + public async sendRequest(url, params = {}) { + if (!this.server) { + this.setServer(); + } + + const peer = await this.selectResponsivePeer(this.server); + const uri = `http://${peer.ip}:${peer.port}/api/${url}`; + + try { + this.logger.info(`Sending request on "${this.network.name}" to "${uri}"`); + + const response = await this.client.get(uri, { params }); + + return response.data; + } catch (error) { + this.logger.error(error.message); + } + } + + public async broadcast(transaction) { + return this.client.post(`http://${this.server.ip}:${this.server.port}/api/transactions`, { + transactions: [transaction], + }); + } + + public async connect(): Promise { + if (this.server) { + // this.logger.info(`Server is already configured as "${this.server.ip}:${this.server.port}"`) + return true; + } + + this.setServer(); + + try { + const peerPort = app.resolveOptions("p2p").port; + const response = await axios.get(`http://${this.server.ip}:${peerPort}/config`); + + const plugin = response.data.data.plugins["@arkecosystem/core-api"]; + + if (!plugin.enabled) { + const index = this.peers.findIndex(peer => peer.ip === this.server.ip); + this.peers.splice(index, 1); + + if (!this.peers.length) { + this.loadRemotePeers(); + } + + return this.connect(); + } + + this.server.port = plugin.port; + } catch (error) { + return this.connect(); + } + } + + private getRandomPeer() { + this.loadRemotePeers(); + + return sample(this.peers); + } + + private loadRemotePeers() { + this.peers = + this.network.name === "testnet" + ? [{ ip: "127.0.0.1", port: app.resolveOptions("api").port }] + : this.p2p.getPeers(); + + if (!this.peers.length) { + this.logger.error("No peers found. Shutting down..."); + process.exit(); + } + } + + private async selectResponsivePeer(peer) { + const reachable = await isReachable(`${peer.ip}:${peer.port}`); + + if (!reachable) { + this.logger.warn(`${peer} is unresponsive. Choosing new peer.`); + + return this.selectResponsivePeer(this.getRandomPeer()); + } + + return peer; + } +} + +export const network = new Network(); diff --git a/packages/core-json-rpc/src/server/services/processor.ts b/packages/core-json-rpc/src/server/services/processor.ts new file mode 100644 index 0000000000..75eda130b0 --- /dev/null +++ b/packages/core-json-rpc/src/server/services/processor.ts @@ -0,0 +1,83 @@ +import Joi from "joi"; +import get from "lodash/get"; +import { network } from "./network"; + +export class Processor { + public async resource(server, payload) { + const { error } = Joi.validate(payload || {}, { + jsonrpc: Joi.string() + .valid("2.0") + .required(), + method: Joi.string().required(), + id: Joi.required(), + params: Joi.object(), + }); + + if (error) { + return this.createErrorResponse(payload ? payload.id : null, -32600, error); + } + + const { method, params, id } = payload; + + try { + const targetMethod = get(server.methods, method); + + if (!targetMethod) { + return this.createErrorResponse(id, -32601, "The method does not exist / is not available."); + } + + const schema = server.app.schemas[method]; + + if (schema) { + // tslint:disable-next-line:no-shadowed-variable + const { error } = Joi.validate(params, schema); + + if (error) { + return this.createErrorResponse(id, -32602, error); + } + } + + await network.connect(); + + const result = await targetMethod(params); + + return result.isBoom + ? this.createErrorResponse(id, result.output.statusCode, result.output.payload) + : this.createSuccessResponse(id, result); + } catch (error) { + return this.createErrorResponse(id, -32603, error); + } + } + + public async collection(server, payload) { + const results = []; + + for (const item of payload) { + const result = await this.resource(server, item); + + results.push(result); + } + + return results; + } + + private createSuccessResponse(id, result) { + return { + jsonrpc: "2.0", + id, + result, + }; + } + + private createErrorResponse(id, code, error) { + return { + jsonrpc: "2.0", + id, + error: { + code, + message: error.message, + data: error.stack, + }, + }; + } +} diff --git a/packages/core-json-rpc/src/server/utils/bip38-keys.ts b/packages/core-json-rpc/src/server/utils/bip38-keys.ts new file mode 100644 index 0000000000..97d9028dfe --- /dev/null +++ b/packages/core-json-rpc/src/server/utils/bip38-keys.ts @@ -0,0 +1,17 @@ +import { configManager, crypto, HashAlgorithms } from "@arkecosystem/crypto"; +import { database } from "../services/database"; +import { decryptWIF } from "./decrypt-wif"; + +export async function getBIP38Wallet(userId, bip38password): Promise { + try { + const encryptedWif = await database.get(HashAlgorithms.sha256(Buffer.from(userId)).toString("hex")); + + if (encryptedWif) { + return decryptWIF(encryptedWif, userId, bip38password); + } + } catch (error) { + throw Error("Could not find a matching WIF"); + // TODO: Unreachable code. What was the intention here? To have it return a boolean or throw an Error? + return false; + } +} diff --git a/packages/core-json-rpc/src/server/utils/decrypt-wif.ts b/packages/core-json-rpc/src/server/utils/decrypt-wif.ts new file mode 100644 index 0000000000..a74344297d --- /dev/null +++ b/packages/core-json-rpc/src/server/utils/decrypt-wif.ts @@ -0,0 +1,10 @@ +import { bip38, configManager, crypto } from "@arkecosystem/crypto"; +import wif from "wif"; + +export const decryptWIF = (encryptedWif, userId, bip38password) => { + const decrypted = bip38.decrypt(encryptedWif.toString("hex"), bip38password + userId); + + const encodedWIF = wif.encode(configManager.get("wif"), decrypted.privateKey, decrypted.compressed); + + return { keys: crypto.getKeysFromWIF(encodedWIF), wif: encodedWIF }; +}; diff --git a/packages/core-json-rpc/tsconfig.json b/packages/core-json-rpc/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-json-rpc/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-logger-winston/CHANGELOG.md b/packages/core-logger-winston/CHANGELOG.md deleted file mode 100644 index d351b8c30a..0000000000 --- a/packages/core-logger-winston/CHANGELOG.md +++ /dev/null @@ -1,29 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Suppress output for silent shutdown - -### Changed - -- Upgraded `winston` to `3.0.0` -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Calculate correct logger padding - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-logger-winston/LICENSE b/packages/core-logger-winston/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-logger-winston/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-logger-winston/README.md b/packages/core-logger-winston/README.md index 5852f2b818..eb4b8770e8 100644 --- a/packages/core-logger-winston/README.md +++ b/packages/core-logger-winston/README.md @@ -14,9 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [François-Xavier Thoorens](https://github.com/fix) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-logger-winston/__tests__/logger.test.js b/packages/core-logger-winston/__tests__/logger.test.js deleted file mode 100644 index 0011e3c251..0000000000 --- a/packages/core-logger-winston/__tests__/logger.test.js +++ /dev/null @@ -1,134 +0,0 @@ -const capcon = require('capture-console') -const WinstonDriver = require('../lib/driver') - -let logger -let message - -beforeAll(() => { - const driver = new WinstonDriver({ - transports: [ - { - constructor: 'Console', - }, - ], - }) - - logger = driver.make() - - capcon.startCapture(process.stdout, stdout => { - message += stdout - }) -}) - -describe('Logger', () => { - it('should be an object', () => { - expect(logger).toBeObject() - }) - - describe('error', () => { - it('should be a function', () => { - expect(logger.error).toBeFunction() - }) - - it('should log a message', () => { - logger.info('error_message') - - expect(message).toMatch(/error/) - expect(message).toMatch(/error_message/) - message = null - }) - }) - - describe('warn', () => { - it('should be a function', () => { - expect(logger.warn).toBeFunction() - }) - - it('should log a message', () => { - logger.info('warning_message') - - expect(message).toMatch(/warn/) - expect(message).toMatch(/warning_message/) - message = null - }) - }) - - describe('info', () => { - it('should be a function', () => { - expect(logger.info).toBeFunction() - }) - - it('should log a message', () => { - logger.info('info_message') - - expect(message).toMatch(/info/) - expect(message).toMatch(/info_message/) - message = null - }) - }) - - describe('debug', () => { - it('should be a function', () => { - expect(logger.debug).toBeFunction() - }) - - it('should log a message', () => { - logger.info('debug_message') - - expect(message).toMatch(/debug/) - expect(message).toMatch(/debug_message/) - message = null - }) - }) - - describe('printTracker', () => { - it('should be a function', () => { - expect(logger.printTracker).toBeFunction() - }) - - it('should print the tracker', () => { - logger.printTracker('test_title', 50, 100, 'done') - - expect(message).toMatch(/test_title/) - expect(message).toMatch(/=========================/) - expect(message).toMatch(/50/) - expect(message).toMatch(/done/) - message = null - }) - }) - - describe('stopTracker', () => { - it('should be a function', () => { - expect(logger.stopTracker).toBeFunction() - }) - - it('should stop the tracker', () => { - logger.stopTracker('test_title', 50, 100) - - expect(message).toMatch(/test_title/) - expect(message).toMatch(/=========================/) - expect(message).toMatch(/50/) - message = null - }) - }) - - describe('suppressConsoleOutput', () => { - it('should be a function', () => { - expect(logger.suppressConsoleOutput).toBeFunction() - }) - - it('should suppress console output', () => { - logger.suppressConsoleOutput(true) - - logger.info('silent_message') - expect(message).toBeNull() - - logger.suppressConsoleOutput(false) - - logger.info('non_silent_message') - expect(message).toMatch(/non_silent_message/) - - message = null - }) - }) -}) diff --git a/packages/core-logger-winston/__tests__/logger.test.ts b/packages/core-logger-winston/__tests__/logger.test.ts new file mode 100644 index 0000000000..ae1e64e0ab --- /dev/null +++ b/packages/core-logger-winston/__tests__/logger.test.ts @@ -0,0 +1,100 @@ +import { AbstractLogger } from "@arkecosystem/core-logger"; +import * as capcon from "capture-console"; +import "jest-extended"; +import * as winston from "winston"; +import { WinstonLogger } from "../src"; + +let logger: AbstractLogger; +let message; + +beforeAll(() => { + const driver = new WinstonLogger({ + transports: [ + { + constructor: "Console", + package: "winston/lib/winston/transports/console", + options: { + level: "debug", + }, + }, + { + constructor: "File", + options: { filename: "tmp.log", level: "silly" }, + }, + ], + }); + + logger = driver.make(); + + capcon.startCapture(process.stdout, stdout => { + message += stdout; + }); +}); + +describe("Logger", () => { + describe("error", () => { + it("should log a message", () => { + logger.error("error_message"); + + expect(message).toMatch(/error/); + expect(message).toMatch(/error_message/); + message = null; + }); + }); + + describe("warn", () => { + it("should log a message", () => { + logger.warn("warning_message"); + + expect(message).toMatch(/warn/); + expect(message).toMatch(/warning_message/); + message = null; + }); + }); + + describe("info", () => { + it("should log a message", () => { + logger.info("info_message"); + + expect(message).toMatch(/info/); + expect(message).toMatch(/info_message/); + message = null; + }); + }); + + describe("debug", () => { + it("should log a message", () => { + logger.debug("debug_message"); + + expect(message).toMatch(/debug/); + expect(message).toMatch(/debug_message/); + message = null; + }); + }); + + describe("verbose", () => { + it("should log a message", () => { + logger.verbose("verbose_message"); + + expect(message).toMatch(/verbose/); + expect(message).toMatch(/verbose_message/); + message = null; + }); + }); + + describe("suppressConsoleOutput", () => { + it("should suppress console output", () => { + logger.suppressConsoleOutput(true); + + logger.info("silent_message"); + expect(message).toBeNull(); + + logger.suppressConsoleOutput(false); + + logger.info("non_silent_message"); + expect(message).toMatch(/non_silent_message/); + + message = null; + }); + }); +}); diff --git a/packages/core-logger-winston/jest.config.js b/packages/core-logger-winston/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-logger-winston/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-logger-winston/lib/defaults.js b/packages/core-logger-winston/lib/defaults.js deleted file mode 100644 index 5162118f25..0000000000 --- a/packages/core-logger-winston/lib/defaults.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - transports: { - console: { - constructor: 'Console', - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - format: require('./formatter')(true), - stderrLevels: ['error', 'warn'], - }, - }, - dailyRotate: { - package: 'winston-daily-rotate-file', - constructor: 'DailyRotateFile', - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - format: require('./formatter')(false), - filename: - process.env.ARK_LOG_FILE - || `${process.env.ARK_PATH_DATA}/logs/core/${ - process.env.ARK_NETWORK_NAME - }/%DATE%.log`, - datePattern: 'YYYY-MM-DD', - zippedArchive: true, - maxSize: '100m', - maxFiles: '10', - }, - }, - }, -} diff --git a/packages/core-logger-winston/lib/driver.js b/packages/core-logger-winston/lib/driver.js deleted file mode 100644 index b323430324..0000000000 --- a/packages/core-logger-winston/lib/driver.js +++ /dev/null @@ -1,128 +0,0 @@ -const winston = require('winston') -const { LoggerInterface } = require('@arkecosystem/core-logger') -require('colors') - -let tracker = null - -module.exports = class Logger extends LoggerInterface { - /** - * Make the logger instance. - * @return {Winston.Logger} - */ - make() { - this.driver = winston.createLogger() - - this.__registerTransports() - - // this.__registerFilters() - - this.driver.printTracker = this.printTracker - this.driver.stopTracker = this.stopTracker - this.driver.suppressConsoleOutput = this.suppressConsoleOutput - - return this.driver - } - - /** - * Print the progress tracker. - * @param {String} title - * @param {Number} current - * @param {Number} max - * @param {String} postTitle - * @param {Number} figures - * @return {void} - */ - printTracker(title, current, max, postTitle, figures = 0) { - const progress = (100 * current) / max - - let line = '\u{1b}[0G ' - line += title.blue - line += ' [' - line += '='.repeat(Math.floor(progress / 2)).green - line += `${' '.repeat(Math.ceil(50 - progress / 2))}] ` - line += `${progress.toFixed(figures)}% ` - - if (postTitle) { - line += `${postTitle} ` - } - - process.stdout.write(line) - - tracker = line - } - - /** - * Stop the progress tracker. - * @param {String} title - * @param {Number} current - * @param {Number} max - * @return {void} - */ - stopTracker(title, current, max) { - let progress = (100 * current) / max - - if (progress > 100) { - progress = 100 - } - - let line = '\u{1b}[0G ' - line += title.blue - line += ' [' - line += '='.repeat(progress / 2).green - line += `${' '.repeat(50 - progress / 2)}] ` - line += `${progress.toFixed(0)}% ` - - if (current === max) { - line += '✔️' - } - - line += ' \n' - process.stdout.write(line) - tracker = null - } - - /** - * Suppress console output. - * @param {Boolean} - * @return {void} - */ - suppressConsoleOutput(suppress) { - const consoleTransport = this.transports.find(t => t.name === 'console') - if (consoleTransport) { - consoleTransport.silent = suppress - } - } - - /** - * Register all transports. - * @return {void} - */ - __registerTransports() { - for (const transport of Object.values(this.options.transports)) { - if (transport.package) { - require(transport.package) - } - - this.driver.add( - new winston.transports[transport.constructor](transport.options), - ) - } - } - - /** - * Register all filters. - * @return {void} - */ - __registerFilters() { - this.driver.filters.push((level, message, meta) => { - if (tracker) { - process.stdout.write( - '\u{1b}[0G \u{1b}[0G', - ) - tracker = null - } - - return message - }) - } -} diff --git a/packages/core-logger-winston/lib/formatter.js b/packages/core-logger-winston/lib/formatter.js deleted file mode 100644 index 38cd7c94ea..0000000000 --- a/packages/core-logger-winston/lib/formatter.js +++ /dev/null @@ -1,45 +0,0 @@ -const { format } = require('winston') -const chalk = require('chalk') -const dayjs = require('dayjs-ext') -const emoji = require('node-emoji') - -const { colorize, combine, timestamp, printf } = format - -module.exports = (colorOutput = true) => - combine( - colorize(), - timestamp(), - printf(info => { - const infoLevel = info[Symbol.for('level')] - - let level = infoLevel.toUpperCase() - let message = emoji.emojify(info.message) || JSON.stringify(info.meta) - - if (colorOutput) { - level = { - error: chalk.bold.red(level), - warn: chalk.bold.yellow(level), - info: chalk.bold.blue(level), - verbose: chalk.bold.cyan(level), - debug: chalk.bold.white(level), - silly: chalk.bold.magenta(level), - }[infoLevel] - - message = { - error: chalk.bold.bgRed(message), - warn: chalk.bold.black.bgYellow(message), - info: message, - verbose: chalk.bold.cyan(message), - debug: chalk.black.bgWhite(message), - silly: chalk.bold.black.bgWhite(message), - }[infoLevel] - } - - const dateTime = dayjs(info.timestamp).format('YYYY-MM-DD HH:mm:ss') - - const dateTimeAndLevel = `[${dateTime}][${level}]:` - const lineSpacer = ' '.repeat(Math.abs(dateTimeAndLevel.length - 50) + 1) - - return `[${dateTime}][${level}]${lineSpacer}: ${message}` - }), - ) diff --git a/packages/core-logger-winston/lib/index.js b/packages/core-logger-winston/lib/index.js deleted file mode 100644 index 33fccd6819..0000000000 --- a/packages/core-logger-winston/lib/index.js +++ /dev/null @@ -1,24 +0,0 @@ -const WinstonDriver = require('./driver') - -/** - * The struct used by the plugin container. - * @type {WinstonDriver} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'logger', - extends: '@arkecosystem/core-logger', - async register(container, options) { - const logManager = container.resolvePlugin('logManager') - await logManager.makeDriver(new WinstonDriver(options)) - - return logManager.driver() - }, -} - -/** - * Expose the winston formatter for configuration. - * @type {Function} - */ -exports.formatter = require('./formatter') diff --git a/packages/core-logger-winston/package.json b/packages/core-logger-winston/package.json index 616fa0d606..8ff7b4e9a0 100644 --- a/packages/core-logger-winston/package.json +++ b/packages/core-logger-winston/package.json @@ -1,37 +1,57 @@ { - "name": "@arkecosystem/core-logger-winston", - "description": "Winston Logger for Ark Core", - "version": "0.2.0", - "contributors": [ - "François-Xavier Thoorens ", - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-logger": "~0.2", - "chalk": "^2.4.1", - "colors": "^1.3.2", - "dayjs-ext": "^2.2.0", - "node-emoji": "^1.8.1", - "winston": "^3.1.0", - "winston-daily-rotate-file": "^3.5.1" - }, - "devDependencies": { - "capture-console": "^1.0.1" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-logger-winston", + "description": "Winston Logger for Ark Core", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-logger": "^2.1.0", + "chalk": "^2.4.1", + "colors": "^1.3.3", + "dayjs-ext": "^2.2.0", + "lodash.isempty": "^4.4.0", + "node-emoji": "^1.8.1", + "winston": "^3.1.0", + "winston-daily-rotate-file": "^3.5.1" + }, + "devDependencies": { + "@types/capture-console": "^1.0.0", + "@types/lodash.isempty": "^4.4.4", + "@types/node-emoji": "^1.8.0", + "capture-console": "^1.0.1" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-logger-winston/src/defaults.ts b/packages/core-logger-winston/src/defaults.ts new file mode 100644 index 0000000000..f8854266cf --- /dev/null +++ b/packages/core-logger-winston/src/defaults.ts @@ -0,0 +1,27 @@ +import { formatter } from "./formatter"; + +export const defaults = { + transports: { + console: { + constructor: "Console", + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + format: formatter(true), + stderrLevels: ["error", "warn"], + }, + }, + dailyRotate: { + package: "winston-daily-rotate-file", + constructor: "DailyRotateFile", + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + format: formatter(false), + filename: process.env.CORE_LOG_FILE || `${process.env.CORE_PATH_LOG}/%DATE%.log`, + datePattern: "YYYY-MM-DD", + zippedArchive: true, + maxSize: "100m", + maxFiles: "10", + }, + }, + }, +}; diff --git a/packages/core-logger-winston/src/driver.ts b/packages/core-logger-winston/src/driver.ts new file mode 100644 index 0000000000..724770cb13 --- /dev/null +++ b/packages/core-logger-winston/src/driver.ts @@ -0,0 +1,119 @@ +import { AbstractLogger } from "@arkecosystem/core-logger"; +import "colors"; +import isEmpty from "lodash/isEmpty"; +import { inspect } from "util"; +import * as winston from "winston"; + +export class WinstonLogger extends AbstractLogger { + public logger: any; + + constructor(readonly options) { + super(options); + } + + /** + * Make the logger instance. + */ + public make() { + this.logger = winston.createLogger(); + + this.registerTransports(); + + return this; + } + + /** + * Log an error message. + * @param {*} message + * @return {void} + */ + public error(message: any): void { + this.createLog("error", message); + } + + /** + * Log a warning message. + * @param {*} message + * @return {void} + */ + public warn(message: any): void { + this.createLog("warn", message); + } + + /** + * Log an info message. + * @param {*} message + * @return {void} + */ + public info(message: any): void { + this.createLog("info", message); + } + + /** + * Log a debug message. + * @param {*} message + * @return {void} + */ + public debug(message: any): void { + this.createLog("debug", message); + } + + /** + * Log a verbose message. + * @param {*} message + * @return {void} + */ + public verbose(message: any): void { + this.createLog("verbose", message); + } + + /** + * Suppress console output. + * @param {Boolean} + * @return {void} + */ + public suppressConsoleOutput(suppress: boolean): void { + const consoleTransport = this.logger.transports.find(t => t.name === "console"); + + if (consoleTransport) { + consoleTransport.silent = suppress; + } + } + + /** + * Register all transports. + * @return {void} + */ + private registerTransports(): void { + for (const transport of Object.values(this.options.transports)) { + // @ts-ignore + if (transport.package) { + // @ts-ignore + require(transport.package); + } + + this.logger.add( + // @ts-ignore + new winston.transports[transport.constructor](transport.options), + ); + } + } + + /** + * Log a message with the given method. + * @param {String} method + * @param {*} message + * @return {void} + */ + private createLog(method: string, message: any): void { + if (isEmpty(message)) { + return; + } + + if (typeof message !== "string") { + message = inspect(message, { depth: 1 }); + } + + this.logger[method](message); + } +} diff --git a/packages/core-logger-winston/src/formatter.ts b/packages/core-logger-winston/src/formatter.ts new file mode 100644 index 0000000000..9249d5e04c --- /dev/null +++ b/packages/core-logger-winston/src/formatter.ts @@ -0,0 +1,45 @@ +import chalk from "chalk"; +import dayjs from "dayjs-ext"; +import emoji from "node-emoji"; +import { format } from "winston"; + +const { colorize, combine, timestamp, printf } = format; + +const formatter = (colorOutput: boolean = true) => + combine( + colorize(), + timestamp(), + printf(info => { + // @ts-ignore + const infoLevel = info[Symbol.for("level")]; + + let level = infoLevel.toUpperCase(); + let message = emoji.emojify(info.message) || JSON.stringify(info.meta); + + if (colorOutput) { + level = { + error: chalk.bold.red(level), + warn: chalk.bold.yellow(level), + info: chalk.bold.blue(level), + verbose: chalk.bold.cyan(level), + debug: chalk.bold.white(level), + silly: chalk.bold.magenta(level), + }[infoLevel]; + + message = { + error: chalk.bold.bgRed(message), + warn: chalk.bold.black.bgYellow(message), + info: message, + verbose: chalk.bold.cyan(message), + debug: chalk.black.bgWhite(message), + silly: chalk.bold.black.bgWhite(message), + }[infoLevel]; + } + + const dateTime = dayjs(info.timestamp).format("YYYY-MM-DD HH:mm:ss"); + + return `[${dateTime}][${level}]: ${message}`; + }), + ); + +export { formatter }; diff --git a/packages/core-logger-winston/src/index.ts b/packages/core-logger-winston/src/index.ts new file mode 100644 index 0000000000..4f9a1a05f8 --- /dev/null +++ b/packages/core-logger-winston/src/index.ts @@ -0,0 +1,3 @@ +export * from "./driver"; +export * from "./formatter"; +export * from "./plugin"; diff --git a/packages/core-logger-winston/src/plugin.ts b/packages/core-logger-winston/src/plugin.ts new file mode 100644 index 0000000000..8388ab84fb --- /dev/null +++ b/packages/core-logger-winston/src/plugin.ts @@ -0,0 +1,33 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import { LogManager } from "@arkecosystem/core-logger"; +import { defaults } from "./defaults"; +import { WinstonLogger } from "./driver"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "logger", + extends: "@arkecosystem/core-logger", + async register(container: Container.IContainer, options) { + const logManager: LogManager = container.resolvePlugin("logManager"); + await logManager.makeDriver(new WinstonLogger(options)); + + const driver = logManager.driver(); + driver.debug(`Data Directory => ${process.env.CORE_PATH_DATA}`); + driver.debug(`Config Directory => ${process.env.CORE_PATH_CONFIG}`); + + if (process.env.CORE_PATH_CACHE) { + driver.debug(`Cache Directory => ${process.env.CORE_PATH_CACHE}`); + } + + if (process.env.CORE_PATH_LOG) { + driver.debug(`Log Directory => ${process.env.CORE_PATH_LOG}`); + } + + if (process.env.CORE_PATH_TEMP) { + driver.debug(`Temp Directory => ${process.env.CORE_PATH_TEMP}`); + } + + return driver; + }, +}; diff --git a/packages/core-logger-winston/tsconfig.json b/packages/core-logger-winston/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-logger-winston/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-logger/CHANGELOG.md b/packages/core-logger/CHANGELOG.md deleted file mode 100644 index fd0a4f14d3..0000000000 --- a/packages/core-logger/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Changed - -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-logger/LICENSE b/packages/core-logger/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-logger/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-logger/README.md b/packages/core-logger/README.md index 7461bb653d..05e30ceccd 100644 --- a/packages/core-logger/README.md +++ b/packages/core-logger/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-logger/__tests__/__stubs__/logger.ts b/packages/core-logger/__tests__/__stubs__/logger.ts new file mode 100644 index 0000000000..0d9730c65c --- /dev/null +++ b/packages/core-logger/__tests__/__stubs__/logger.ts @@ -0,0 +1,31 @@ +import { AbstractLogger } from "../../src"; + +export class Logger extends AbstractLogger { + public make(): any { + return this; + } + + public error(message: string): void { + // + } + + public warn(message: string): void { + // + } + + public info(message: string): void { + // + } + + public debug(message: string): void { + // + } + + public verbose(message: string): void { + // + } + + public suppressConsoleOutput(suppress: boolean): void { + // + } +} diff --git a/packages/core-logger/__tests__/interface.test.js b/packages/core-logger/__tests__/interface.test.js deleted file mode 100644 index 66a662e4ad..0000000000 --- a/packages/core-logger/__tests__/interface.test.js +++ /dev/null @@ -1,57 +0,0 @@ -const LoggerInterface = require('../lib/interface') - -const logger = new LoggerInterface() - -describe('Logger Interface', () => { - it('should be an object', () => { - expect(logger).toBeObject() - }) - - describe('driver', () => { - it('should be a function', () => { - expect(logger.driver).toBeFunction() - }) - }) - - describe('error', () => { - it('should be a function', () => { - expect(logger.error).toBeFunction() - }) - }) - - describe('warning', () => { - it('should be a function', () => { - expect(logger.warn).toBeFunction() - }) - }) - - describe('info', () => { - it('should be a function', () => { - expect(logger.info).toBeFunction() - }) - }) - - describe('debug', () => { - it('should be a function', () => { - expect(logger.debug).toBeFunction() - }) - }) - - describe('printTracker', () => { - it('should be a function', () => { - expect(logger.printTracker).toBeFunction() - }) - }) - - describe('stopTracker', () => { - it('should be a function', () => { - expect(logger.stopTracker).toBeFunction() - }) - }) - - describe('suppressConsoleOutput', () => { - it('should be a function', () => { - expect(logger.suppressConsoleOutput).toBeFunction() - }) - }) -}) diff --git a/packages/core-logger/__tests__/manager.test.js b/packages/core-logger/__tests__/manager.test.js deleted file mode 100644 index 7e9325e091..0000000000 --- a/packages/core-logger/__tests__/manager.test.js +++ /dev/null @@ -1,32 +0,0 @@ -const loggerManager = require('../lib/manager') - -class FakeDriver { - make() { - return this - } -} - -describe('Config Manager', () => { - it('should be an object', () => { - expect(loggerManager).toBeObject() - expect(loggerManager.drivers).toBeDefined() - }) - - describe('driver', () => { - it('should be a function', () => { - expect(loggerManager.driver).toBeFunction() - }) - - it('should return the driver', async () => { - await loggerManager.makeDriver(new FakeDriver()) - - expect(loggerManager.driver()).toBeInstanceOf(FakeDriver) - }) - }) - - describe('makeDriver', () => { - it('should be a function', () => { - expect(loggerManager.makeDriver).toBeFunction() - }) - }) -}) diff --git a/packages/core-logger/__tests__/manager.test.ts b/packages/core-logger/__tests__/manager.test.ts new file mode 100644 index 0000000000..eca1bec3ca --- /dev/null +++ b/packages/core-logger/__tests__/manager.test.ts @@ -0,0 +1,15 @@ +import "jest-extended"; +import { AbstractLogger, LogManager } from "../src"; +import { Logger } from "./__stubs__/logger"; + +const manager = new LogManager(); + +describe("Config Manager", () => { + describe("driver", () => { + it("should return the driver", async () => { + await manager.makeDriver(new Logger({})); + + expect(manager.driver()).toBeInstanceOf(AbstractLogger); + }); + }); +}); diff --git a/packages/core-logger/jest.config.js b/packages/core-logger/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-logger/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-logger/lib/index.js b/packages/core-logger/lib/index.js deleted file mode 100644 index 45eea19843..0000000000 --- a/packages/core-logger/lib/index.js +++ /dev/null @@ -1,19 +0,0 @@ -const logManager = require('./manager') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - alias: 'logManager', - async register() { - return logManager - }, -} - -/** - * The interface used by concrete implementations. - * @type {LoggerInterface} - */ -exports.LoggerInterface = require('./interface') diff --git a/packages/core-logger/lib/interface.js b/packages/core-logger/lib/interface.js deleted file mode 100644 index ec61a1be9f..0000000000 --- a/packages/core-logger/lib/interface.js +++ /dev/null @@ -1,86 +0,0 @@ -module.exports = class LoggerInterface { - /** - * Create a new logger instance. - * @param {Object} options - */ - constructor(options) { - this.options = options - } - - /** - * Get a driver instance. - * @return {LoggerInterface} - */ - driver() { - return this.driver - } - - /** - * Log an error message. - * @param {*} message - * @return {void} - */ - error(message) { - throw new Error('Method [error] not implemented!') - } - - /** - * Log a warning message. - * @param {*} message - * @return {void} - */ - warn(message) { - throw new Error('Method [warn] not implemented!') - } - - /** - * Log an info message. - * @param {*} message - * @return {void} - */ - info(message) { - throw new Error('Method [info] not implemented!') - } - - /** - * Log a debug message. - * @param {*} message - * @return {void} - */ - debug(message) { - throw new Error('Method [debug] not implemented!') - } - - /** - * Print the progress tracker. - * @param {String} title - * @param {Number} current - * @param {Number} max - * @param {String} postTitle - * @param {Number} figures - * @return {void} - */ - printTracker(title, current, max, postTitle, figures = 0) { - throw new Error('Method [printTracker] not implemented!') - } - - /** - * Stop the progress tracker. - * @param {String} title - * @param {Number} current - * @param {Number} max - * @return {void} - */ - stopTracker(title, current, max) { - throw new Error('Method [stopTracker] not implemented!') - } - - /** - * Suppress console output. - * @param {Boolean} - * @return {void} - */ - suppressConsoleOutput(suppress) { - throw new Error('Method [suppressConsoleOutput] not implemented!') - } -} diff --git a/packages/core-logger/lib/manager.js b/packages/core-logger/lib/manager.js deleted file mode 100644 index 485a72abfc..0000000000 --- a/packages/core-logger/lib/manager.js +++ /dev/null @@ -1,30 +0,0 @@ -class LogManager { - /** - * Create a new log manager instance. - * @constructor - */ - constructor() { - this.drivers = {} - } - - /** - * Get a logger instance. - * @param {String} name - * @return {LoggerInterface} - */ - driver(name = 'default') { - return this.drivers[name] - } - - /** - * Make the logger instance. - * @param {LoggerInterface} driver - * @param {String} name - * @return {void} - */ - async makeDriver(driver, name = 'default') { - this.drivers[name] = await driver.make() - } -} - -module.exports = new LogManager() diff --git a/packages/core-logger/package.json b/packages/core-logger/package.json index 94dd9a2e92..3a383aaf7c 100644 --- a/packages/core-logger/package.json +++ b/packages/core-logger/package.json @@ -1,24 +1,42 @@ { - "name": "@arkecosystem/core-logger", - "description": "Logger Manager for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-logger", + "description": "Logger Manager for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-logger/src/index.ts b/packages/core-logger/src/index.ts new file mode 100644 index 0000000000..5ea9d983cb --- /dev/null +++ b/packages/core-logger/src/index.ts @@ -0,0 +1,3 @@ +export * from "./manager"; +export * from "./logger"; +export * from "./plugin"; diff --git a/packages/core-logger/src/logger.ts b/packages/core-logger/src/logger.ts new file mode 100644 index 0000000000..884c10be91 --- /dev/null +++ b/packages/core-logger/src/logger.ts @@ -0,0 +1,57 @@ +import { Logger } from "@arkecosystem/core-interfaces"; + +export abstract class AbstractLogger implements Logger.ILogger { + /** + * Create a new logger instance. + * @param {Object} options + */ + constructor(protected options: any) {} + + /** + * Make the logger instance. + * @return {Object} + */ + public abstract make(): Logger.ILogger; + + /** + * Log an error message. + * @param {*} message + * @return {void} + */ + public abstract error(message: any): void; + + /** + * Log a warning message. + * @param {*} message + * @return {void} + */ + public abstract warn(message: any): void; + + /** + * Log an info message. + * @param {*} message + * @return {void} + */ + public abstract info(message: any): void; + + /** + * Log a debug message. + * @param {*} message + * @return {void} + */ + public abstract debug(message: any): void; + + /** + * Log a verbose message. + * @param {*} message + * @return {void} + */ + public abstract verbose(message: any): void; + + /** + * Suppress console output. + * @param {Boolean} + * @return {void} + */ + public abstract suppressConsoleOutput(suppress: boolean): void; +} diff --git a/packages/core-logger/src/manager.ts b/packages/core-logger/src/manager.ts new file mode 100644 index 0000000000..b9d59e4383 --- /dev/null +++ b/packages/core-logger/src/manager.ts @@ -0,0 +1,31 @@ +import { Logger } from "@arkecosystem/core-interfaces"; + +export class LogManager { + private drivers: Map; + + /** + * Create a new manager instance. + */ + constructor() { + this.drivers = new Map(); + } + + /** + * Get a logger instance. + * @param {String} name + * @return {AbstractLogger} + */ + public driver(name: string = "default"): Logger.ILogger { + return this.drivers.get(name); + } + + /** + * Make the logger instance. + * @param {AbstractLogger} driver + * @param {String} name + * @return {void} + */ + public async makeDriver(driver: Logger.ILogger, name: string = "default"): Promise { + this.drivers.set(name, await driver.make()); + } +} diff --git a/packages/core-logger/src/plugin.ts b/packages/core-logger/src/plugin.ts new file mode 100644 index 0000000000..c7d7eb8ebd --- /dev/null +++ b/packages/core-logger/src/plugin.ts @@ -0,0 +1,9 @@ +import { LogManager } from "./manager"; + +export const plugin = { + pkg: require("../package.json"), + alias: "logManager", + async register() { + return new LogManager(); + }, +}; diff --git a/packages/core-logger/tsconfig.json b/packages/core-logger/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-logger/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-p2p/CHANGELOG.md b/packages/core-p2p/CHANGELOG.md deleted file mode 100644 index fe90adcdb4..0000000000 --- a/packages/core-p2p/CHANGELOG.md +++ /dev/null @@ -1,84 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.11 - 2018-12-17 - -### Fixed - -- Prevent the list of peers to become too short - -## 0.2.1 - 2018-12-11 - -### Fixed - -- Ensure no local peers are enlisted -- Ensure the IP of the TCP connection is used - -## 0.2.0 - 2018-12-03 - -### Added - -- Support for next forger calculations -- Relay support for wake-up from forger (to sync before forging) -- Additional tests implemented -- Remote API authentication -- Return a 503 status code while the node is syncing/busy instead of crashing -- Updated peer heights on a regular basis -- Validate P2P headers -- Rule based peer banning to provide greater control -- Event emitting via API through the relay -- Configuration API -- Minimum peer version -- Peer whitelisting & blacklisting -- Common block checks -- Peer banning after forks -- Reject forgers as peers -- Recovery after a fork -- Enabled rate-limiting -- Enable/Disable peer discovery -- Dump the peer list on shutdown and load it on next start -- Log how many peers were found with what versions -- Log how many peers were found with what git commits _(development networks only)_ - -### Changed - -- Network state calculation (new internal/networkState) taking PBFT into account -- Peer optimisations (blacklisting, whitelisting, coldstart) options for peers and forger -- Overall reduced the complexity of how the P2P API is structured -- Allow config to be retrieved without P2P headers -- Dropped node.js 9 as minimum requirement in favour of node.js 10 -- Exclude transactions from broadcasting if they are in the transaction pool -- Reduced timeouts for HTTP requests -- Allow 20/rps instead of 1000/rpm -- Limit the number of peers a transaction is broadcasted to -- Broadcast transactions in chunks based on `maxTransactionsPerRequest` -- Improved ping behaviour by remembering ping times - -### Removed - -- Remove threading for block downloads - -### Fixed - -- Handle "no common block" banning -- Various cases of bad error handling -- Various inconsistencies between the v1 P2P API and current implementation -- Return ports as integers -- Handle CORS requests to the P2P API -- Return the last block if no height is provided to a method -- Race condition that would result in duplicate transactions in the transaction pool -- Accept v1 peers -- Avoid errors when banning peers before the state storage is not ready yet -- Grab transactions based on the transactions per block - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-p2p/LICENSE b/packages/core-p2p/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-p2p/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-p2p/README.md b/packages/core-p2p/README.md index 0929a34301..b8819d9bae 100644 --- a/packages/core-p2p/README.md +++ b/packages/core-p2p/README.md @@ -14,11 +14,13 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [All Contributors](../../../../contributors) +- [Alex Barnsley](https://github.com/alexbarnsley) +- [Brian Faust](https://github.com/faustbrian) +- [Erwann Gentric](https://github.com/air1one) +- [François-Xavier Thoorens](https://github.com/fix) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-p2p/__mocks__/sntp.js b/packages/core-p2p/__mocks__/sntp.js deleted file mode 100644 index b6cb8435ef..0000000000 --- a/packages/core-p2p/__mocks__/sntp.js +++ /dev/null @@ -1,12 +0,0 @@ -const Sntp = jest.genMockFromModule('sntp') - -const sntpTime = Sntp.time -Sntp.time = options => { - if (options.host === 'notime.unknown.not') { - // we actually want to call the real Sntp time() because we want it to fail - return sntpTime(options) - } - return { t: 111 } -} - -module.exports = Sntp diff --git a/packages/core-p2p/__mocks__/sntp.ts b/packages/core-p2p/__mocks__/sntp.ts new file mode 100644 index 0000000000..6dc687fbc8 --- /dev/null +++ b/packages/core-p2p/__mocks__/sntp.ts @@ -0,0 +1,12 @@ +const Sntp: any = jest.genMockFromModule("sntp"); + +const sntpTime = Sntp.time; +Sntp.time = options => { + if (options.host === "notime.unknown.not") { + // we actually want to call the real Sntp time() because we want it to fail + return sntpTime(options); + } + return { t: 111 }; +}; + +export = Sntp; diff --git a/packages/core-p2p/__tests__/__support__/setup.js b/packages/core-p2p/__tests__/__support__/setup.js deleted file mode 100644 index 08cb29c352..0000000000 --- a/packages/core-p2p/__tests__/__support__/setup.js +++ /dev/null @@ -1,14 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(60000) - -exports.setUp = async () => { - await appHelper.setUp({ - exit: '@arkecosystem/core-blockchain', - }) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-p2p/__tests__/__support__/setup.ts b/packages/core-p2p/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..2c4e2bcfd6 --- /dev/null +++ b/packages/core-p2p/__tests__/__support__/setup.ts @@ -0,0 +1,29 @@ +import { app } from "@arkecosystem/core-container"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; + +jest.setTimeout(60000); + +const options = { + host: "0.0.0.0", + port: 4000, + minimumNetworkReach: 5, + coldStart: 5, +}; + +export const setUp = async () => { + await setUpContainer({ + exit: "@arkecosystem/core-p2p", + exclude: ["@arkecosystem/core-p2p"], + }); + + // register p2p plugin + await registerWithContainer(require("../../src/plugin").plugin, options); + await registerWithContainer(require("@arkecosystem/core-blockchain").plugin, {}); +}; + +export const tearDown = async () => { + await require("@arkecosystem/core-blockchain").plugin.deregister(app, {}); + await require("../../src/plugin").plugin.deregister(app, options); + + await app.tearDown(); +}; diff --git a/packages/core-p2p/__tests__/__support__/utils.js b/packages/core-p2p/__tests__/__support__/utils.js deleted file mode 100644 index 83f3840f39..0000000000 --- a/packages/core-p2p/__tests__/__support__/utils.js +++ /dev/null @@ -1,33 +0,0 @@ -const apiHelpers = require('@arkecosystem/core-test-utils/lib/helpers/api') - -class Helpers { - constructor() { - this.headers = { - nethash: - 'd9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192', - port: 4000, - version: '2.0.0', - } - } - - async GET(endpoint, params = {}) { - return this.request('GET', endpoint, params) - } - - async POST(endpoint, params) { - return this.request('POST', endpoint, params) - } - - async request(method, path, params = {}) { - const url = `http://localhost:4002/${path}` - const server = require('@arkecosystem/core-container').resolvePlugin('p2p') - .server - - return apiHelpers.request(server, method, url, this.headers, params) - } -} - -/** - * @type {Helpers} - */ -module.exports = new Helpers() diff --git a/packages/core-p2p/__tests__/__support__/utils.ts b/packages/core-p2p/__tests__/__support__/utils.ts new file mode 100644 index 0000000000..39f0f9e1a4 --- /dev/null +++ b/packages/core-p2p/__tests__/__support__/utils.ts @@ -0,0 +1,33 @@ +import { app } from "@arkecosystem/core-container"; +import { ApiHelpers } from "@arkecosystem/core-test-utils/src/helpers/api"; + +class Helpers { + public headers: any; + constructor() { + this.headers = { + nethash: "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + port: 4000, + version: "2.0.0", + }; + } + + public async GET(endpoint, params = {}) { + return this.request("GET", endpoint, params); + } + + public async POST(endpoint, params) { + return this.request("POST", endpoint, params); + } + + public async request(method, path, params = {}) { + const url = `http://localhost:4002/${path}`; + const server = app.resolvePlugin("p2p").server; + + return ApiHelpers.request(server, method, url, this.headers, params); + } +} + +/** + * @type {Helpers} + */ +export const utils = new Helpers(); diff --git a/packages/core-p2p/__tests__/court/guard.test.js b/packages/core-p2p/__tests__/court/guard.test.js deleted file mode 100644 index 063a4f9508..0000000000 --- a/packages/core-p2p/__tests__/court/guard.test.js +++ /dev/null @@ -1,240 +0,0 @@ -/* eslint max-len: "off" */ - -const dayjs = require('dayjs-ext') -const app = require('../__support__/setup') - -const ARK_ENV = process.env.ARK_ENV - -const defaults = require('../../lib/defaults') -const offences = require('../../lib/court/offences') - -let container -let guard -let Peer -let peerMock - -beforeAll(async () => { - await app.setUp() - container = require('@arkecosystem/core-container') - - guard = require('../../lib/court/guard') - Peer = require('../../lib/peer') -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(async () => { - guard.monitor.config = defaults - guard.monitor.peers = {} - - // this peer is here to be ready for future use in tests (not added to initial peers) - peerMock = new Peer('0.0.0.99', 4002) - Object.assign(peerMock, peerMock.headers) -}) - -describe('Guard', () => { - it('should be an object', () => { - expect(guard).toBeObject() - }) - - describe('isSuspended', () => { - it('should be a function', () => { - expect(guard.isSuspended).toBeFunction() - }) - - it('should return true', async () => { - process.env.ARK_ENV = false - await guard.monitor.acceptNewPeer(peerMock) - process.env.ARK_ENV = ARK_ENV - - expect(guard.isSuspended(peerMock)).toBe(true) - }) - - it('should return false because passed', async () => { - process.env.ARK_ENV = false - await guard.monitor.acceptNewPeer(peerMock) - guard.suspensions[peerMock.ip].until = dayjs().subtract(1, 'minutes') - process.env.ARK_ENV = ARK_ENV - - expect(guard.isSuspended(peerMock)).toBe(false) - }) - - it('should return false because not suspended', () => { - expect(guard.isSuspended(peerMock)).toBe(false) - }) - }) - - describe('isRepeatOffender', () => { - it('should be a function', () => { - expect(guard.isRepeatOffender).toBeFunction() - }) - - it('should be true if the threshold is met', () => { - const peer = { offences: [] } - - for (let i = 0; i < 10; i++) { - peer.offences.push({ weight: 10 }) - } - - expect(guard.isRepeatOffender(peer)).toBeFalse() - }) - - it('should be false if the threshold is not met', () => { - const peer = { offences: [] } - - for (let i = 0; i < 15; i++) { - peer.offences.push({ weight: 10 }) - } - - expect(guard.isRepeatOffender(peer)).toBeTrue() - }) - }) - - describe('__determineOffence', () => { - const convertToMinutes = actual => - Math.ceil(actual.diff(dayjs()) / 1000) / 60 - - const dummy = { - nethash: - 'd9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192', - version: '2.0.0', - status: 200, - state: {}, - } - - it('should be a function', () => { - expect(guard.__determineOffence).toBeFunction() - }) - - it('should return a 1 day suspension for "Blacklisted"', () => { - const config = container.resolvePlugin('config') - config.peers.blackList = ['dummy-ip-addr'] - - const { until, reason } = guard.__determineOffence({ - nethash: - 'd9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192', - ip: 'dummy-ip-addr', - }) - - expect(convertToMinutes(until)).toBe(720) - expect(reason).toBe('Blacklisted') - }) - - it('should return a 5 minutes suspension for "No Common Blocks"', () => { - const { until, reason } = guard.__determineOffence({ - ...dummy, - ...{ - commonBlocks: false, - }, - }) - - expect(convertToMinutes(until)).toBe(5) - expect(reason).toBe('No Common Blocks') - }) - - it('should return a 6 hours suspension for "Invalid Version"', () => { - const { until, reason } = guard.__determineOffence({ - nethash: - 'd9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192', - version: '1.0.0', - status: 200, - delay: 1000, - }) - - expect(convertToMinutes(until)).toBe(360) - expect(reason).toBe('Invalid Version') - }) - - it('should return a 10 minutes suspension for "Node is not at height"', () => { - guard.monitor.getNetworkHeight = jest.fn(() => 154) - - const { until, reason } = guard.__determineOffence({ - ...dummy, - state: { - height: 1, - }, - }) - - expect(convertToMinutes(until)).toBe(10) - expect(reason).toBe('Node is not at height') - }) - - it('should return a 5 minutes suspension for "Invalid Response Status"', () => { - const { until, reason } = guard.__determineOffence({ - ...dummy, - ...{ status: 201 }, - }) - - expect(convertToMinutes(until)).toBe(5) - expect(reason).toBe('Invalid Response Status') - }) - - it('should return a 2 minutes suspension for "Timeout"', () => { - const { until, reason } = guard.__determineOffence({ - ...dummy, - ...{ delay: -1 }, - }) - - expect(convertToMinutes(until)).toBe(2) - expect(reason).toBe('Timeout') - }) - - it('should return a 1 minutes suspension for "High Latency"', () => { - const { until, reason } = guard.__determineOffence({ - ...dummy, - ...{ delay: 3000 }, - }) - - expect(convertToMinutes(until)).toBe(1) - expect(reason).toBe('High Latency') - }) - - it('should return a 30 seconds suspension for "Blockchain not ready"', () => { - const { until, reason } = guard.__determineOffence({ - ...dummy, - ...{ status: 503 }, - }) - - expect(convertToMinutes(until)).toBe(0.5) - expect(reason).toBe('Blockchain not ready') - }) - - it('should return a 60 seconds suspension for "Rate limit exceeded"', () => { - const { until, reason } = guard.__determineOffence({ - ...dummy, - ...{ status: 429 }, - }) - - expect(convertToMinutes(until)).toBe(1) - expect(reason).toBe('Rate limit exceeded') - }) - - it('should return a 30 minutes suspension for "Unknown"', () => { - const { until, reason } = guard.__determineOffence(dummy) - - expect(convertToMinutes(until)).toBe(30) - expect(reason).toBe('Unknown') - }) - }) - - describe('__determinePunishment', () => { - it('should be a function', () => { - expect(guard.__determinePunishment).toBeFunction() - }) - - it('should be true if the threshold is met', () => { - const actual = guard.__determinePunishment({}, offences.REPEAT_OFFENDER) - - expect(actual).toHaveProperty('until') - expect(actual.until).toBeObject() - - expect(actual).toHaveProperty('reason') - expect(actual.reason).toBeString() - - expect(actual).toHaveProperty('weight') - expect(actual.weight).toBeNumber() - }) - }) -}) diff --git a/packages/core-p2p/__tests__/court/guard.test.ts b/packages/core-p2p/__tests__/court/guard.test.ts new file mode 100644 index 0000000000..da5b158208 --- /dev/null +++ b/packages/core-p2p/__tests__/court/guard.test.ts @@ -0,0 +1,258 @@ +import { app } from "@arkecosystem/core-container"; +import dayjs from "dayjs-ext"; +import { offences } from "../../src/court/offences"; +import { defaults } from "../../src/defaults"; +import { Peer } from "../../src/peer"; +import { setUp, tearDown } from "../__support__/setup"; + +const CORE_ENV = process.env.CORE_ENV; +let guard; +let peerMock; + +beforeAll(async () => { + await setUp(); + + app.getConfig().set("milestoneHash", "dummy-milestone"); + + guard = require("../../src/court/guard").guard; +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(async () => { + guard.monitor.config = defaults; + guard.monitor.peers = {}; + + // this peer is here to be ready for future use in tests (not added to initial peers) + peerMock = new Peer("1.0.0.99", 4002); + peerMock.milestoneHash = "dummy-milestone"; + Object.assign(peerMock, peerMock.headers); +}); + +describe("Guard", () => { + describe("isSuspended", () => { + it("should return true", async () => { + process.env.CORE_ENV = "false"; + await guard.monitor.acceptNewPeer(peerMock); + process.env.CORE_ENV = CORE_ENV; + + expect(guard.isSuspended(peerMock)).toBe(true); + }); + + it("should return false because passed", async () => { + process.env.CORE_ENV = "false"; + await guard.monitor.acceptNewPeer(peerMock); + guard.suspensions[peerMock.ip].until = dayjs().subtract(1, "minute"); + process.env.CORE_ENV = CORE_ENV; + + expect(guard.isSuspended(peerMock)).toBe(false); + }); + + it("should return false because not suspended", () => { + expect(guard.isSuspended(peerMock)).toBe(false); + }); + }); + + describe("isRepeatOffender", () => { + it("should be true if the threshold is met", () => { + const peer = { offences: [] }; + + for (let i = 0; i < 10; i++) { + peer.offences.push({ weight: 10 }); + } + + expect(guard.isRepeatOffender(peer)).toBeFalse(); + }); + + it("should be false if the threshold is not met", () => { + const peer = { offences: [] }; + + for (let i = 0; i < 15; i++) { + peer.offences.push({ weight: 10 }); + } + + expect(guard.isRepeatOffender(peer)).toBeTrue(); + }); + }); + + describe("isValidVersion", () => { + it("should be a valid version", () => { + const get = guard.config.get; + guard.config.get = jest.fn(() => ">=2.0.0"); + + expect(guard.isValidVersion({ version: "2.0.0" })).toBeTrue(); + expect(guard.isValidVersion({ version: "2.1.39" })).toBeTrue(); + expect(guard.isValidVersion({ version: "3.0.0" })).toBeTrue(); + + guard.config.get = get; + }); + + it("should be an invalid version", () => { + const get = guard.config.get; + guard.config.get = jest.fn(() => ">=2.0.0"); + + expect(guard.isValidVersion({ version: "1.0.0" })).toBeFalse(); + expect(guard.isValidVersion({ version: "1.0" })).toBeFalse(); + expect(guard.isValidVersion({ version: "---aaa" })).toBeFalse(); + expect(guard.isValidVersion({ version: "2490" })).toBeFalse(); + expect(guard.isValidVersion({ version: 2 })).toBeFalse(); + expect(guard.isValidVersion({ version: -10.2 })).toBeFalse(); + expect(guard.isValidVersion({ version: {} })).toBeFalse(); + expect(guard.isValidVersion({ version: true })).toBeFalse(); + expect(guard.isValidVersion({ version: () => "1" })).toBeFalse(); + expect(guard.isValidVersion({ version: "2.0.0.0" })).toBeFalse(); + + guard.config.get = get; + }); + }); + + describe("__determineOffence", () => { + const convertToMinutes = actual => Math.ceil(actual.diff(dayjs()) / 1000) / 60; + + const dummy = { + nethash: "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + milestoneHash: "dummy-milestone", + version: "2.1.0", + status: 200, + state: {}, + }; + + it('should return a 1 year suspension for "Blacklisted"', () => { + guard.config.set("blacklist", ["dummy-ip-addr"]); + + const { until, reason } = guard.__determineOffence({ + nethash: "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + milestoneHash: "dummy-milestone", + ip: "dummy-ip-addr", + }); + + expect(reason).toBe("Blacklisted"); + expect(convertToMinutes(until)).toBe(525600); + + guard.config.set("blacklist", []); + }); + + it('should return a 5 minutes suspension for "No Common Blocks"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + ...{ + commonBlocks: false, + }, + }); + + expect(reason).toBe("No Common Blocks"); + expect(convertToMinutes(until)).toBe(5); + }); + + it('should return a 5 minute suspension for "Invalid Version"', () => { + const { until, reason } = guard.__determineOffence({ + nethash: "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + milestoneHash: "dummy-milestone", + version: "1.0.0", + status: 200, + delay: 1000, + }); + + expect(reason).toBe("Invalid Version"); + expect(convertToMinutes(until)).toBe(5); + }); + + it('should return a 5 minute suspension for "Invalid Milestones"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + milestoneHash: "wrong-milestone", + }); + + expect(reason).toBe("Invalid Milestones"); + expect(convertToMinutes(until)).toBe(5); + }); + + it('should return a 10 minutes suspension for "Node is not at height"', () => { + guard.monitor.getNetworkHeight = jest.fn(() => 154); + + const { until, reason } = guard.__determineOffence({ + ...dummy, + state: { + height: 1, + }, + }); + + expect(reason).toBe("Node is not at height"); + expect(convertToMinutes(until)).toBe(10); + }); + + it('should return a 5 minutes suspension for "Invalid Response Status"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + ...{ status: 201 }, + }); + + expect(reason).toBe("Invalid Response Status"); + expect(convertToMinutes(until)).toBe(5); + }); + + it('should return a 2 minutes suspension for "Timeout"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + ...{ delay: -1 }, + }); + + expect(reason).toBe("Timeout"); + expect(convertToMinutes(until)).toBe(2); + }); + + it('should return a 1 minutes suspension for "High Latency"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + ...{ delay: 3000 }, + }); + + expect(reason).toBe("High Latency"); + expect(convertToMinutes(until)).toBe(1); + }); + + it('should return a 30 seconds suspension for "Blockchain not ready"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + ...{ status: 503 }, + }); + + expect(reason).toBe("Blockchain not ready"); + expect(convertToMinutes(until)).toBe(0.5); + }); + + it('should return a 60 seconds suspension for "Rate limit exceeded"', () => { + const { until, reason } = guard.__determineOffence({ + ...dummy, + ...{ status: 429 }, + }); + + expect(reason).toBe("Rate limit exceeded"); + expect(convertToMinutes(until)).toBe(1); + }); + + it('should return a 10 minutes suspension for "Unknown"', () => { + const { until, reason } = guard.__determineOffence(dummy); + + expect(reason).toBe("Unknown"); + expect(convertToMinutes(until)).toBe(10); + }); + }); + + describe("__determinePunishment", () => { + it("should be true if the threshold is met", () => { + const actual = guard.__determinePunishment({}, offences.REPEAT_OFFENDER); + + expect(actual).toHaveProperty("until"); + expect(actual.until).toBeObject(); + + expect(actual).toHaveProperty("reason"); + expect(actual.reason).toBeString(); + + expect(actual).toHaveProperty("weight"); + expect(actual.weight).toBeNumber(); + }); + }); +}); diff --git a/packages/core-p2p/__tests__/monitor.test.js b/packages/core-p2p/__tests__/monitor.test.js deleted file mode 100644 index c221963354..0000000000 --- a/packages/core-p2p/__tests__/monitor.test.js +++ /dev/null @@ -1,263 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') - -const axiosMock = new MockAdapter(axios) - -const app = require('./__support__/setup') - -const defaults = require('../lib/defaults') - -let monitor -let Peer -let peerMock - -beforeAll(async () => { - await app.setUp() - - monitor = require('../lib/monitor') - Peer = require('../lib/peer') -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(async () => { - monitor.config = defaults - - const initialPeersMock = {} - ;['0.0.0.0', '0.0.0.1', '0.0.0.2', '0.0.0.3', '0.0.0.4'].forEach(ip => { - const initialPeer = new Peer(ip, 4000) - initialPeersMock[ip] = Object.assign(initialPeer, initialPeer.headers, { - ban: 0, - }) - }) - monitor.peers = initialPeersMock - - peerMock = new Peer('0.0.0.99', 4000) // this peer is just here to be picked up by tests below (not added to initial peers) - Object.assign(peerMock, peerMock.headers, { status: 200 }) - peerMock.nethash = - 'd9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192' - - axiosMock.reset() // important: resets any existing mocking behavior -}) - -describe('Monitor', () => { - it('should be an object', () => { - expect(monitor).toBeObject() - }) - - describe('updateNetworkStatus', () => { - it('should be a function', () => { - expect(monitor.updateNetworkStatus).toBeFunction() - }) - }) - - describe('updateNetworkStatusIfNotEnoughPeers', () => { - it('should be a function', () => { - expect(monitor.updateNetworkStatusIfNotEnoughPeers).toBeFunction() - }) - }) - - describe('cleanPeers', () => { - it('should be a function', () => { - expect(monitor.cleanPeers).toBeFunction() - }) - - it('should be ok', async () => { - const previousLength = Object.keys(monitor.peers).length - - await monitor.cleanPeers(true) - - expect(Object.keys(monitor.peers).length).toBeLessThan(previousLength) - }) - }) - - describe('acceptNewPeer', () => { - it('should be a function', () => { - expect(monitor.acceptNewPeer).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(`${peerMock.url}/peer/status`) - .reply(() => [200, { success: true }, peerMock.headers]) - process.env.ARK_ENV = false - - await monitor.acceptNewPeer(peerMock) - - expect(monitor.peers[peerMock.ip]).toBeObject() - - process.env.ARK_ENV = 'test' - }) - }) - - describe('getPeers', () => { - it('should be a function', () => { - expect(monitor.getPeers).toBeFunction() - }) - - it('should be ok', async () => { - const peers = monitor.getPeers() - - expect(peers).toBeArray() - expect(peers.length).toBe(5) // 5 from peers.json - }) - }) - - describe('getRandomPeer', () => { - it('should be a function', () => { - expect(monitor.getRandomPeer).toBeFunction() - }) - - it('should be ok', async () => { - const peer = monitor.getRandomPeer() - - expect(peer).toBeObject() - expect(peer).toHaveProperty('ip') - expect(peer).toHaveProperty('port') - }) - }) - - describe('getRandomDownloadBlocksPeer', () => { - it('should be a function', () => { - expect(monitor.getRandomDownloadBlocksPeer).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(/.*\/peer\/blocks\/common/) - .reply(() => [200, { success: true, common: true }, peerMock.headers]) - const peer = await monitor.getRandomDownloadBlocksPeer() - - expect(peer).toBeObject() - expect(peer).toHaveProperty('ip') - expect(peer).toHaveProperty('port') - }) - }) - - describe('discoverPeers', () => { - it('should be a function', () => { - expect(monitor.discoverPeers).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(/.*\/peer\/status/) - .reply(() => [200, { success: true }, peerMock.headers]) - axiosMock - .onGet(/.*\/peer\/list/) - .reply(() => [ - 200, - { peers: [peerMock.toBroadcastInfo()] }, - peerMock.headers, - ]) - - await monitor.discoverPeers() - const peers = monitor.getPeers() - - expect(peers).toBeArray() - expect(Object.keys(peers).length).toBe(6) // 5 from initial peers + 1 from peerMock - expect(peers.find(e => e.ip === peerMock.ip)).toBeDefined() - }) - }) - - describe('hasPeers', () => { - it('should be a function', () => { - expect(monitor.hasPeers).toBeFunction() - }) - }) - - describe('getNetworkHeight', () => { - it('should be a function', () => { - expect(monitor.getNetworkHeight).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(/.*\/peer\/status/) - .reply(() => [200, { success: true, height: 2 }, peerMock.headers]) - axiosMock - .onGet(/.*\/peer\/list/) - .reply(() => [200, { peers: [] }, peerMock.headers]) - await monitor.discoverPeers() - - const height = await monitor.getNetworkHeight() - expect(height).toBe(2) - }) - - // TODO test with peers with different heights (use replyOnce) and check that median is OK - }) - - describe('getPBFTForgingStatus', () => { - it('should be a function', () => { - expect(monitor.getPBFTForgingStatus).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(/.*\/peer\/status/) - .reply(() => [200, { success: true, height: 2 }, peerMock.headers]) - axiosMock - .onGet(/.*\/peer\/list/) - .reply(() => [200, { peers: [] }, peerMock.headers]) - - await monitor.discoverPeers() - const pbftForgingStatus = monitor.getPBFTForgingStatus() - - expect(pbftForgingStatus).toBeNumber() - // TODO test mocking peers currentSlot & forgingAllowed - }) - }) - - describe('downloadBlocks', () => { - it('should be a function', () => { - expect(monitor.downloadBlocks).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(/.*\/peer\/blocks\/common/) - .reply(() => [200, { success: true, common: true }, peerMock.headers]) - axiosMock - .onGet(/.*\/peer\/status/) - .reply(() => [200, { success: true, height: 2 }, peerMock.headers]) - axiosMock - .onGet(/.*\/peer\/blocks/) - .reply(() => [ - 200, - { blocks: [{ id: 1 }, { id: 2 }] }, - peerMock.headers, - ]) - - const blocks = await monitor.downloadBlocks(1) - - expect(blocks).toBeArray() - expect(blocks.length).toBe(2) - }) - }) - - describe('broadcastBlock', () => { - it('should be a function', () => { - expect(monitor.broadcastBlock).toBeFunction() - }) - }) - - describe('broadcastTransactions', () => { - it('should be a function', () => { - expect(monitor.broadcastTransactions).toBeFunction() - }) - }) - - describe('__checkDNSConnectivity', () => { - it('should be a function', () => { - expect(monitor.__checkDNSConnectivity).toBeFunction() - }) - }) - - describe('__checkNTPConnectivity', () => { - it('should be a function', () => { - expect(monitor.__checkNTPConnectivity).toBeFunction() - }) - }) -}) diff --git a/packages/core-p2p/__tests__/monitor.test.ts b/packages/core-p2p/__tests__/monitor.test.ts new file mode 100644 index 0000000000..00fdab4c22 --- /dev/null +++ b/packages/core-p2p/__tests__/monitor.test.ts @@ -0,0 +1,156 @@ +/* tslint:disable:max-line-length */ +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { defaults } from "../src/defaults"; +import { Peer } from "../src/peer"; +import { setUp, tearDown } from "./__support__/setup"; + +const axiosMock = new MockAdapter(axios); + +let peerMock: Peer; +let monitor; + +beforeAll(async () => { + await setUp(); + monitor = require("../src/monitor").monitor; +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(async () => { + monitor.config = defaults; + + const initialPeersMock = {}; + ["1.0.0.0", "1.0.0.1", "1.0.0.2", "1.0.0.3", "1.0.0.4"].forEach(ip => { + const initialPeer = new Peer(ip, 4000); + initialPeersMock[ip] = Object.assign(initialPeer, initialPeer.headers, { + ban: 0, + }); + }); + + monitor.peers = initialPeersMock; + + peerMock = new Peer("1.0.0.99", 4000); // this peer is just here to be picked up by tests below (not added to initial peers) + Object.assign(peerMock, peerMock.headers, { status: 200 }); + peerMock.nethash = "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192"; + + axiosMock.reset(); // important: resets any existing mocking behavior +}); + +describe("Monitor", () => { + describe("cleanPeers", () => { + it("should be ok", async () => { + const previousLength = Object.keys(monitor.peers).length; + + await monitor.cleanPeers(true); + + expect(Object.keys(monitor.peers).length).toBeLessThan(previousLength); + }); + }); + + describe("acceptNewPeer", () => { + it("should be ok", async () => { + axiosMock.onGet(`${peerMock.url}/peer/status`).reply(() => [200, { success: true }, peerMock.headers]); + process.env.CORE_ENV = "false"; + + await monitor.acceptNewPeer(peerMock); + + expect(monitor.peers[peerMock.ip]).toBeObject(); + + process.env.CORE_ENV = "test"; + }); + }); + + describe("getPeers", () => { + it("should be ok", async () => { + const peers = monitor.getPeers(); + + expect(peers).toBeArray(); + expect(peers.length).toBe(5); // 5 from peers.json + }); + }); + + describe("getRandomPeer", () => { + it("should be ok", async () => { + const peer = monitor.getRandomPeer(); + + expect(peer).toBeObject(); + expect(peer).toHaveProperty("ip"); + expect(peer).toHaveProperty("port"); + }); + }); + + describe("getRandomDownloadBlocksPeer", () => { + it("should be ok", async () => { + axiosMock + .onGet(/.*\/peer\/blocks\/common/) + .reply(() => [200, { success: true, common: true }, peerMock.headers]); + const peer = await monitor.getRandomDownloadBlocksPeer(); + + expect(peer).toBeObject(); + expect(peer).toHaveProperty("ip"); + expect(peer).toHaveProperty("port"); + }); + }); + + describe("discoverPeers", () => { + it("should be ok", async () => { + axiosMock.onGet(/.*\/peer\/status/).reply(() => [200, { success: true }, peerMock.headers]); + axiosMock + .onGet(/.*\/peer\/list/) + .reply(() => [200, { peers: [peerMock.toBroadcastInfo()] }, peerMock.headers]); + + await monitor.discoverPeers(); + const peers = monitor.getPeers(); + + expect(peers).toBeArray(); + expect(Object.keys(peers).length).toBe(6); // 5 from initial peers + 1 from peerMock + expect(peers.find(e => e.ip === peerMock.ip)).toBeDefined(); + }); + }); + + describe("getNetworkHeight", () => { + it("should be ok", async () => { + axiosMock.onGet(/.*\/peer\/status/).reply(() => [200, { success: true, height: 2 }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/list/).reply(() => [200, { peers: [] }, peerMock.headers]); + await monitor.discoverPeers(); + + const height = await monitor.getNetworkHeight(); + expect(height).toBe(2); + }); + + // TODO test with peers with different heights (use replyOnce) and check that median is OK + }); + + describe("getPBFTForgingStatus", () => { + it("should be ok", async () => { + axiosMock.onGet(/.*\/peer\/status/).reply(() => [200, { success: true, height: 2 }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/list/).reply(() => [200, { peers: [] }, peerMock.headers]); + + await monitor.discoverPeers(); + const pbftForgingStatus = monitor.getPBFTForgingStatus(); + + expect(pbftForgingStatus).toBeNumber(); + // TODO test mocking peers currentSlot & forgingAllowed + }); + }); + + describe("downloadBlocks", () => { + it("should be ok", async () => { + axiosMock + .onGet(/.*\/peer\/blocks\/common/) + .reply(() => [200, { success: true, common: true }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/status/).reply(() => [200, { success: true, height: 2 }, peerMock.headers]); + axiosMock + .onGet(/.*\/peer\/blocks/) + .reply(() => [200, { blocks: [{ id: 1 }, { id: 2 }] }, peerMock.headers]); + + const blocks = await monitor.downloadBlocks(1); + + expect(blocks).toBeArray(); + expect(blocks.length).toBe(2); + }); + }); +}); diff --git a/packages/core-p2p/__tests__/peer.test.js b/packages/core-p2p/__tests__/peer.test.js deleted file mode 100644 index d9b949cbf8..0000000000 --- a/packages/core-p2p/__tests__/peer.test.js +++ /dev/null @@ -1,283 +0,0 @@ -/* eslint no-empty: "off" */ - -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') - -const axiosMock = new MockAdapter(axios) -const { Block, Transaction } = require('@arkecosystem/crypto').models -const app = require('./__support__/setup') - -let genesisBlock -let genesisTransaction - -let Peer -let peerMock - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - genesisTransaction = new Transaction(genesisBlock.transactions[0]) - - Peer = require('../lib/peer') -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(() => { - peerMock = new Peer('0.0.0.99', 4002) - Object.assign(peerMock, peerMock.headers) - - axiosMock.reset() // important: resets any existing mocking behavior -}) - -describe('Peer', () => { - it('should be an object', () => { - expect(peerMock).toBeObject() - }) - - describe('toBroadcastInfo', () => { - it('should be a function', () => { - expect(peerMock.toBroadcastInfo).toBeFunction() - }) - - it('should be ok', async () => { - const struct = peerMock.toBroadcastInfo() - - expect(struct).toBeObject() - expect(struct).toHaveProperty('ip') - expect(struct).toHaveProperty('port') - expect(struct).toHaveProperty('version') - expect(struct).toHaveProperty('os') - expect(struct).toHaveProperty('status') - expect(struct).toHaveProperty('height') - expect(struct).toHaveProperty('delay') - }) - }) - - describe('postBlock', () => { - it('should be a function', () => { - expect(peerMock.postBlock).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onPost(`${peerMock.url}/peer/blocks`) - .reply(200, { success: true }, peerMock.headers) - - const response = await peerMock.postBlock(genesisBlock.toJson()) - - expect(response).toBeObject() - expect(response).toHaveProperty('success') - expect(response.success).toBeTrue() - }) - }) - - describe.skip('postTransactions', () => { - it('should be a function', () => { - expect(peerMock.postTransactions).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onPost(`${peerMock.url}/peer/transactions`) - .reply(200, { success: true }, peerMock.headers) - - const response = await peerMock.postTransactions([ - genesisTransaction.toJson(), - ]) - - expect(response).toBeObject() - expect(response).toHaveProperty('success') - expect(response.success).toBeTrue() - }) - }) - - describe('downloadBlocks', () => { - // https://github.com/facebook/jest/issues/3601 - const errorCapturer = fn => - fn - .then(res => () => res) - .catch(err => () => { - throw err - }) - - it('should be a function', () => { - expect(peerMock.downloadBlocks).toBeFunction() - }) - - it('should return the blocks with status 200', async () => { - const blocks = [{}] - axiosMock - .onGet(`${peerMock.url}/peer/blocks`) - .reply(200, { blocks }, peerMock.headers) - const result = await peerMock.downloadBlocks(1) - - expect(result).toEqual(blocks) - }) - - it('should not return the blocks with status 500', async () => { - axiosMock - .onGet(`${peerMock.url}/peer/blocks`) - .reply(500, { data: {} }, peerMock.headers) - - expect(await errorCapturer(peerMock.downloadBlocks(1))).toThrow( - /request.*500/i, - ) - }) - }) - - describe('ping', () => { - it('should be a function', () => { - expect(peerMock.ping).toBeFunction() - }) - - it('should be ok', async () => { - axiosMock - .onGet(`${peerMock.url}/peer/status`) - .reply(() => [200, { success: true }, peerMock.headers]) - - const response = await peerMock.ping(5000) - - expect(response).toBeObject() - expect(response).toHaveProperty('success') - expect(response.success).toBeTrue() - }) - - it('should not be ok', async () => { - axiosMock - .onGet(`${peerMock.url}/peer/status`) - .reply(() => [500, {}, peerMock.headers]) - return expect(peerMock.ping(1)).rejects.toThrowError('is unresponsive') - }) - - it.each([200, 500, 503])( - 'should update peer status from http response %i', - async status => { - axiosMock - .onGet(`${peerMock.url}/peer/status`) - .replyOnce(() => [status, {}, peerMock.headers]) - try { - await peerMock.ping(1000) - } catch (e) {} - expect(peerMock.status).toBe(status) - }, - ) - }) - - describe('recentlyPinged', () => { - it('should be a function', () => { - expect(peerMock.recentlyPinged).toBeFunction() - }) - - it('should be recently pinged', async () => { - peerMock.lastPinged = null - - expect(peerMock.recentlyPinged()).toBeFalse() - - axiosMock - .onGet(`${peerMock.url}/peer/status`) - .reply(() => [200, { success: true }, peerMock.headers]) - - const response = await peerMock.ping(5000) - - expect(response).toBeObject() - expect(response).toHaveProperty('success') - expect(response.success).toBeTrue() - expect(peerMock.recentlyPinged()).toBeTrue() - }) - }) - - describe('getPeers', () => { - it('should be a function', () => { - expect(peerMock.getPeers).toBeFunction() - }) - - it('should be ok', async () => { - const peersMock = [{ ip: '1.1.1.1' }] - axiosMock - .onGet(`${peerMock.url}/peer/status`) - .reply(() => [200, { success: true }, peerMock.headers]) - axiosMock - .onGet(`${peerMock.url}/peer/list`) - .reply(() => [200, { peers: peersMock }, peerMock.headers]) - - const peers = await peerMock.getPeers() - - expect(peers).toEqual(peersMock) - }) - }) - - describe('height', () => { - it('should update the height after download', async () => { - const blocks = [{}] - const headers = Object.assign({}, peerMock.headers, { height: 1 }) - - axiosMock - .onGet(`${peerMock.url}/peer/blocks`) - .reply(200, { blocks }, headers) - - expect(peerMock.state.height).toBeFalsy() - await peerMock.downloadBlocks(1) - expect(peerMock.state.height).toBe(1) - }) - - it('should update the height after post block', async () => { - const blocks = [{}] - const headers = Object.assign({}, peerMock.headers, { height: 1 }) - - axiosMock - .onPost(`${peerMock.url}/peer/blocks`) - .reply(200, { blocks }, headers) - - expect(peerMock.state.height).toBeFalsy() - await peerMock.postBlock(genesisBlock.toJson()) - expect(peerMock.state.height).toBe(1) - }) - - it('should update the height after post transaction', async () => { - const transactions = [{}] - const headers = Object.assign({}, peerMock.headers, { height: 1 }) - - axiosMock - .onPost(`${peerMock.url}/peer/transactions`) - .reply(200, { transactions }, headers) - - expect(peerMock.state.height).toBeFalsy() - await peerMock.postTransactions([genesisTransaction.toJson()]) - expect(peerMock.state.height).toBe(1) - }) - }) - - describe('__get', () => { - it('should be a function', () => { - expect(peerMock.__get).toBeFunction() - }) - }) - - describe('__parseHeaders', () => { - it('should be a function', () => { - expect(peerMock.__parseHeaders).toBeFunction() - }) - - it('should be ok', async () => { - const headers = { - nethash: 'nethash', - os: 'os', - version: 'version', - } - - await peerMock.__parseHeaders({ headers }) - - expect(peerMock.nethash).toBe(headers.nethash) - expect(peerMock.os).toBe(headers.os) - expect(peerMock.version).toBe(headers.version) - }) - }) -}) diff --git a/packages/core-p2p/__tests__/peer.test.ts b/packages/core-p2p/__tests__/peer.test.ts new file mode 100644 index 0000000000..eccc689add --- /dev/null +++ b/packages/core-p2p/__tests__/peer.test.ts @@ -0,0 +1,204 @@ +import { models } from "@arkecosystem/crypto"; +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { Peer } from "../src/peer"; +import { setUp, tearDown } from "./__support__/setup"; + +const axiosMock = new MockAdapter(axios); +const { Block, Transaction } = models; + +let genesisBlock; +let genesisTransaction; + +let peerMock: Peer; + +beforeAll(async () => { + await setUp(); + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(require("@arkecosystem/core-test-utils/src/config/testnet/genesisBlock.json")); + genesisTransaction = new Transaction(genesisBlock.transactions[0]); +}); + +afterAll(async () => { + await tearDown(); +}); + +beforeEach(() => { + peerMock = new Peer("1.0.0.99", 4002); + Object.assign(peerMock, peerMock.headers); + + axiosMock.reset(); // important: resets any existing mocking behavior +}); + +describe("Peer", () => { + describe("toBroadcastInfo", () => { + it("should be ok", async () => { + const struct = peerMock.toBroadcastInfo(); + + expect(struct).toBeObject(); + expect(struct).toHaveProperty("ip"); + expect(struct).toHaveProperty("port"); + expect(struct).toHaveProperty("version"); + expect(struct).toHaveProperty("os"); + expect(struct).toHaveProperty("status"); + expect(struct).toHaveProperty("height"); + expect(struct).toHaveProperty("delay"); + }); + }); + + describe("postBlock", () => { + it("should be ok", async () => { + axiosMock.onPost(`${peerMock.url}/peer/blocks`).reply(200, { success: true }, peerMock.headers); + + const response = await peerMock.postBlock(genesisBlock.toJson()); + + expect(response).toBeObject(); + expect(response).toHaveProperty("success"); + expect(response.success).toBeTrue(); + }); + }); + + describe.skip("postTransactions", () => { + it("should be ok", async () => { + axiosMock.onPost(`${peerMock.url}/peer/transactions`).reply(200, { success: true }, peerMock.headers); + + const response = await peerMock.postTransactions([genesisTransaction.toJson()]); + + expect(response).toBeObject(); + expect(response).toHaveProperty("success"); + expect(response.success).toBeTrue(); + }); + }); + + describe("downloadBlocks", () => { + // https://github.com/facebook/jest/issues/3601 + const errorCapturer = fn => + fn + .then(res => () => res) + .catch(err => () => { + throw err; + }); + + it("should return the blocks with status 200", async () => { + const blocks = [{}]; + axiosMock.onGet(`${peerMock.url}/peer/blocks`).reply(200, { blocks }, peerMock.headers); + const result = await peerMock.downloadBlocks(1); + + expect(result).toEqual(blocks); + }); + + it("should not return the blocks with status 500", async () => { + axiosMock.onGet(`${peerMock.url}/peer/blocks`).reply(500, { data: {} }, peerMock.headers); + + expect(await errorCapturer(peerMock.downloadBlocks(1))).toThrow(/request.*500/i); + }); + }); + + describe("ping", () => { + it("should be ok", async () => { + axiosMock.onGet(`${peerMock.url}/peer/status`).reply(() => [200, { success: true }, peerMock.headers]); + + const response = await peerMock.ping(5000); + + expect(response).toBeObject(); + expect(response).toHaveProperty("success"); + expect(response.success).toBeTrue(); + }); + + it("should not be ok", async () => { + axiosMock.onGet(`${peerMock.url}/peer/status`).reply(() => [500, {}, peerMock.headers]); + return expect(peerMock.ping(1)).rejects.toThrowError("is unresponsive"); + }); + + it.each([200, 500, 503])("should update peer status from http response %i", async status => { + axiosMock.onGet(`${peerMock.url}/peer/status`).replyOnce(() => [status, {}, peerMock.headers]); + try { + await peerMock.ping(1000); + // tslint:disable-next-line:no-empty + } catch (e) {} + expect(peerMock.status).toBe(status); + }); + }); + + describe("recentlyPinged", () => { + it("should be recently pinged", async () => { + peerMock.lastPinged = null; + + expect(peerMock.recentlyPinged()).toBeFalse(); + + axiosMock.onGet(`${peerMock.url}/peer/status`).reply(() => [200, { success: true }, peerMock.headers]); + + const response = await peerMock.ping(5000); + + expect(response).toBeObject(); + expect(response).toHaveProperty("success"); + expect(response.success).toBeTrue(); + expect(peerMock.recentlyPinged()).toBeTrue(); + }); + }); + + describe("getPeers", () => { + it("should be ok", async () => { + const peersMock = [{ ip: "1.1.1.1" }]; + axiosMock.onGet(`${peerMock.url}/peer/status`).reply(() => [200, { success: true }, peerMock.headers]); + axiosMock.onGet(`${peerMock.url}/peer/list`).reply(() => [200, { peers: peersMock }, peerMock.headers]); + + const peers = await peerMock.getPeers(); + + expect(peers).toEqual(peersMock); + }); + }); + + describe("height", () => { + it("should update the height after download", async () => { + const blocks = [{}]; + const headers = Object.assign({}, peerMock.headers, { height: 1 }); + + axiosMock.onGet(`${peerMock.url}/peer/blocks`).reply(200, { blocks }, headers); + + expect(peerMock.state.height).toBeFalsy(); + await peerMock.downloadBlocks(1); + expect(peerMock.state.height).toBe(1); + }); + + it("should update the height after post block", async () => { + const blocks = [{}]; + const headers = Object.assign({}, peerMock.headers, { height: 1 }); + + axiosMock.onPost(`${peerMock.url}/peer/blocks`).reply(200, { blocks }, headers); + + expect(peerMock.state.height).toBeFalsy(); + await peerMock.postBlock(genesisBlock.toJson()); + expect(peerMock.state.height).toBe(1); + }); + + it("should update the height after post transaction", async () => { + const transactions = [{}]; + const headers = Object.assign({}, peerMock.headers, { height: 1 }); + + axiosMock.onPost(`${peerMock.url}/peer/transactions`).reply(200, { transactions }, headers); + + expect(peerMock.state.height).toBeFalsy(); + await peerMock.postTransactions([genesisTransaction.toJson()]); + expect(peerMock.state.height).toBe(1); + }); + }); + + describe("__parseHeaders", () => { + it("should be ok", async () => { + const headers = { + nethash: "nethash", + os: "os", + version: "version", + }; + + await peerMock.__parseHeaders({ headers }); + + expect(peerMock.nethash).toBe(headers.nethash); + expect(peerMock.os).toBe(headers.os); + expect(peerMock.version).toBe(headers.version); + }); + }); +}); diff --git a/packages/core-p2p/__tests__/server/1.test.js b/packages/core-p2p/__tests__/server/1.test.js deleted file mode 100644 index 248e864f39..0000000000 --- a/packages/core-p2p/__tests__/server/1.test.js +++ /dev/null @@ -1,187 +0,0 @@ -const { Block, Transaction } = require('@arkecosystem/crypto').models -const genTransfer = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const app = require('../__support__/setup') -const utils = require('../__support__/utils') - -let genesisBlock - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('API - Version 1', () => { - describe('GET /peer/list', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/list') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - - expect(response.data).toHaveProperty('peers') - expect(response.data.peers).toBeArray() - }) - }) - - describe('GET /peer/blocks', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/blocks', { lastBlockHeight: 1 }) - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - - expect(response.data).toHaveProperty('blocks') - expect(response.data.blocks).toBeArray() - }) - - it('should retrieve lastBlock if no "lastBlockHeight" specified', async () => { - const response = await utils.GET('peer/blocks') - - expect(response.status).toBe(200) - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - - expect(response.data).toHaveProperty('blocks') - expect(response.data.blocks).toBeArray() - expect(response.data.blocks).toHaveLength(1) - }) - }) - - describe('GET /peer/transactionsFromIds', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/transactionsFromIds', { - ids: 'e40ce11cab82736da1cc91191716f3c1f446ca7b6a9f4f93b7120ef105ba06e8', - }) - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - - expect(response.data).toHaveProperty('transactions') - expect(response.data.transactions).toBeArray() - }) - }) - - describe('GET /peer/height', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/height') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - - expect(response.data).toHaveProperty('height') - expect(response.data.height).toBeNumber() - - expect(response.data).toHaveProperty('id') - expect(response.data.id).toBeString() - }) - }) - - describe('GET /peer/transactions', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/transactions') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - - expect(response.data).toHaveProperty('transactions') - expect(response.data.transactions).toBeArray() - }) - }) - - describe('GET /peer/blocks/common', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/blocks/common', { - ids: '17184958558311101492', - }) - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - expect(response.data).toHaveProperty('common') - expect(response.data.common).toBeObject() - expect(response.data.common.height).toBe(1) - expect(response.data.common.id).toBe('17184958558311101492') - - expect(response.data).toHaveProperty('lastBlockHeight') - expect(response.data.lastBlockHeight).toBeNumber() - }) - }) - - describe('GET /peer/status', () => { - it('should be ok', async () => { - const response = await utils.GET('peer/status') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - }) - }) - - describe('POST /peer/blocks', () => { - it('should be ok', async () => { - const response = await utils.POST('peer/blocks', { - block: genesisBlock.toJson(), - }) - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - }) - }) - - describe('POST /peer/transactions', () => { - it('should be ok', async () => { - const transactions = genTransfer('testnet') - const response = await utils.POST('peer/transactions', { - transactions, - }) - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('success') - expect(response.data.success).toBeTrue() - }) - }) -}) diff --git a/packages/core-p2p/__tests__/server/1.test.ts b/packages/core-p2p/__tests__/server/1.test.ts new file mode 100644 index 0000000000..68174b62e6 --- /dev/null +++ b/packages/core-p2p/__tests__/server/1.test.ts @@ -0,0 +1,176 @@ +import { generateTransfers } from "@arkecosystem/core-test-utils/src/generators/transactions/transfer"; +import { models } from "@arkecosystem/crypto"; +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +const { Block, Transaction } = models; + +let genesisBlock; + +beforeAll(async () => { + await setUp(); + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(require("@arkecosystem/core-test-utils/src/config/testnet/genesisBlock.json")); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("API - Version 1", () => { + describe("GET /peer/list", () => { + it("should be ok", async () => { + const response = await utils.GET("peer/list"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + + expect(response.data).toHaveProperty("peers"); + expect(response.data.peers).toBeArray(); + }); + }); + + describe("GET /peer/blocks", () => { + it("should be ok", async () => { + const response = await utils.GET("peer/blocks", { lastBlockHeight: 1 }); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + + expect(response.data).toHaveProperty("blocks"); + expect(response.data.blocks).toBeArray(); + }); + + it('should retrieve lastBlock if no "lastBlockHeight" specified', async () => { + const response = await utils.GET("peer/blocks"); + + expect(response.status).toBe(200); + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + + expect(response.data).toHaveProperty("blocks"); + expect(response.data.blocks).toBeArray(); + expect(response.data.blocks).toHaveLength(1); + }); + }); + + describe("GET /peer/height", () => { + it("should be ok", async () => { + const response = await utils.GET("peer/height"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + + expect(response.data).toHaveProperty("height"); + expect(response.data.height).toBeNumber(); + + expect(response.data).toHaveProperty("id"); + expect(response.data.id).toBeString(); + }); + }); + + describe("GET /peer/transactions", () => { + it("should be ok", async () => { + const response = await utils.GET("peer/transactions"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + + expect(response.data).toHaveProperty("transactions"); + expect(response.data.transactions).toBeArray(); + }); + }); + + describe("GET /peer/blocks/common", () => { + it("should be ok", async () => { + const response = await utils.GET("peer/blocks/common", { + ids: "17184958558311101492", + }); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + expect(response.data).toHaveProperty("common"); + expect(response.data.common).toBeObject(); + expect(response.data.common.height).toBe(1); + expect(response.data.common.id).toBe("17184958558311101492"); + + expect(response.data).toHaveProperty("lastBlockHeight"); + expect(response.data.lastBlockHeight).toBeNumber(); + }); + }); + + describe("GET /peer/status", () => { + it("should be ok", async () => { + const response = await utils.GET("peer/status"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + }); + }); + + describe("POST /peer/blocks", () => { + it("should be ok", async () => { + const response = await utils.POST("peer/blocks", { + block: genesisBlock.toJson(), + }); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("success"); + expect(response.data.success).toBeTrue(); + }); + }); + + describe("POST /peer/transactions", () => { + it("should succeed with an existing wallet", async () => { + const transactions = generateTransfers( + "testnet", + "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", + null, + 40, + ); + const response = await utils.POST("peer/transactions", { transactions }); + + expect(response.data).toBeObject(); + expect(response.data.success).toBeTrue(); + }); + + it("should fail with a cold wallet", async () => { + const transactions = generateTransfers("testnet", "wallet does not exist"); + const response = await utils.POST("peer/transactions", { transactions }); + + expect(response.data).toBeObject(); + expect(response.data.success).toBeFalse(); + }); + }); +}); diff --git a/packages/core-p2p/__tests__/server/internal.test.js b/packages/core-p2p/__tests__/server/internal.test.js deleted file mode 100644 index b6ef16cf38..0000000000 --- a/packages/core-p2p/__tests__/server/internal.test.js +++ /dev/null @@ -1,130 +0,0 @@ -const { Block, Transaction } = require('@arkecosystem/crypto').models -const genTransfer = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const blockFixture = require('../../../core-debugger-cli/__tests__/__fixtures__/block.json') -const app = require('../__support__/setup') -const utils = require('../__support__/utils') - -let genesisBlock -let genesisTransaction - -beforeAll(async () => { - await app.setUp() - - // Create the genesis block after the setup has finished or else it uses a potentially - // wrong network config. - genesisBlock = new Block( - require('@arkecosystem/core-test-utils/config/testnet/genesisBlock.json'), - ) - genesisTransaction = new Transaction(genesisBlock.transactions[0]) -}) - -beforeEach(() => { - utils.headers['x-auth'] = 'forger' -}) - -afterAll(async () => { - delete utils.headers['x-auth'] - await app.tearDown() -}) - -describe('API - Internal', () => { - describe('GET /rounds/current', () => { - it('should be ok', async () => { - const response = await utils.GET('internal/rounds/current') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('data') - }) - - it('should return 403 without x-auth', async () => { - delete utils.headers['x-auth'] - const response = await utils.GET('internal/rounds/current') - - expect(response.status).toBe(403) - }) - }) - - describe('POST /blocks', () => { - it('should be ok', async () => { - const block = new Block(blockFixture.data) - const response = await utils.POST('internal/blocks', { - block: block.toJson(), - }) - expect(response.status).toBe(204) - }) - - it('should return 403 without x-auth', async () => { - delete utils.headers['x-auth'] - const response = await utils.POST('internal/blocks', { - block: genesisBlock.toJson(), - }) - - expect(response.status).toBe(403) - }) - }) - - describe('POST /transactions/verify', () => { - it('should be ok', async () => { - const transaction = genTransfer('testnet')[0] - const response = await utils.POST('internal/transactions/verify', { - transaction: Transaction.serialize(transaction).toString('hex'), - }) - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('data') - }) - - it('should return 403 without x-auth', async () => { - delete utils.headers['x-auth'] - const response = await utils.POST('internal/transactions/verify', { - transaction: genesisTransaction, - }) - - expect(response.status).toBe(403) - }) - }) - - describe('GET /transactions/forging', () => { - it('should be ok', async () => { - const response = await utils.GET('internal/transactions/forging') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('data') - }) - - it('should return 403 without x-auth', async () => { - delete utils.headers['x-auth'] - const response = await utils.GET('internal/transactions/forging') - - expect(response.status).toBe(403) - }) - }) - - describe('GET /network/state', () => { - it('should be ok', async () => { - const response = await utils.GET('internal/network/state') - - expect(response.status).toBe(200) - - expect(response.data).toBeObject() - - expect(response.data).toHaveProperty('data') - }) - - it('should return 403 without x-auth', async () => { - delete utils.headers['x-auth'] - const response = await utils.GET('internal/network/state') - - expect(response.status).toBe(403) - }) - }) -}) diff --git a/packages/core-p2p/__tests__/server/internal.test.ts b/packages/core-p2p/__tests__/server/internal.test.ts new file mode 100644 index 0000000000..456adb707e --- /dev/null +++ b/packages/core-p2p/__tests__/server/internal.test.ts @@ -0,0 +1,130 @@ +import { generateTransfers } from "@arkecosystem/core-test-utils/src/generators/transactions/transfer"; +import { models } from "@arkecosystem/crypto"; +import blockFixture from "../../../core-debugger-cli/__tests__/__fixtures__/block.json"; +import { setUp, tearDown } from "../__support__/setup"; +import { utils } from "../__support__/utils"; + +const { Block, Transaction } = models; + +let genesisBlock; +let genesisTransaction; + +beforeAll(async () => { + await setUp(); + + // Create the genesis block after the setup has finished or else it uses a potentially + // wrong network config. + genesisBlock = new Block(require("@arkecosystem/core-test-utils/src/config/testnet/genesisBlock.json")); + genesisTransaction = new Transaction(genesisBlock.transactions[0]); +}); + +beforeEach(() => { + utils.headers["x-auth"] = "forger"; +}); + +afterAll(async () => { + delete utils.headers["x-auth"]; + await tearDown(); +}); + +describe("API - Internal", () => { + describe("GET /rounds/current", () => { + it("should be ok", async () => { + const response = await utils.GET("internal/rounds/current"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("data"); + }); + + it("should return 403 without x-auth", async () => { + delete utils.headers["x-auth"]; + const response = await utils.GET("internal/rounds/current"); + + expect(response.status).toBe(403); + }); + }); + + describe("POST /blocks", () => { + it("should be ok", async () => { + const block = new Block(blockFixture.data); + const response = await utils.POST("internal/blocks", { + block: block.toJson(), + }); + expect(response.status).toBe(204); + }); + + it("should return 403 without x-auth", async () => { + delete utils.headers["x-auth"]; + const response = await utils.POST("internal/blocks", { + block: genesisBlock.toJson(), + }); + + expect(response.status).toBe(403); + }); + }); + + describe("POST /transactions/verify", () => { + it("should be ok", async () => { + const transaction = generateTransfers("testnet")[0]; + const response = await utils.POST("internal/transactions/verify", { + transaction: Transaction.serialize(transaction).toString("hex"), + }); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("data"); + }); + + it("should return 403 without x-auth", async () => { + delete utils.headers["x-auth"]; + const response = await utils.POST("internal/transactions/verify", { + transaction: genesisTransaction, + }); + + expect(response.status).toBe(403); + }); + }); + + describe("GET /transactions/forging", () => { + it("should be ok", async () => { + const response = await utils.GET("internal/transactions/forging"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("data"); + }); + + it("should return 403 without x-auth", async () => { + delete utils.headers["x-auth"]; + const response = await utils.GET("internal/transactions/forging"); + + expect(response.status).toBe(403); + }); + }); + + describe("GET /network/state", () => { + it("should be ok", async () => { + const response = await utils.GET("internal/network/state"); + + expect(response.status).toBe(200); + + expect(response.data).toBeObject(); + + expect(response.data).toHaveProperty("data"); + }); + + it("should return 403 without x-auth", async () => { + delete utils.headers["x-auth"]; + const response = await utils.GET("internal/network/state"); + + expect(response.status).toBe(403); + }); + }); +}); diff --git a/packages/core-p2p/__tests__/utils/check-dns.test.js b/packages/core-p2p/__tests__/utils/check-dns.test.js deleted file mode 100644 index 68f389f5da..0000000000 --- a/packages/core-p2p/__tests__/utils/check-dns.test.js +++ /dev/null @@ -1,27 +0,0 @@ -const app = require('../__support__/setup') - -let checker - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(() => { - checker = require('../../lib/utils/check-dns') -}) - -describe('Check DNS', () => { - it('should be a function', () => { - expect(checker).toBeFunction() - }) - - it('should be ok', async () => { - const response = await checker(['1.1.1.1']) - - expect(response).toBe('1.1.1.1') - }) -}) diff --git a/packages/core-p2p/__tests__/utils/check-dns.test.ts b/packages/core-p2p/__tests__/utils/check-dns.test.ts new file mode 100644 index 0000000000..0a78b44190 --- /dev/null +++ b/packages/core-p2p/__tests__/utils/check-dns.test.ts @@ -0,0 +1,17 @@ +import { checkDNS } from "../../src/utils"; +import { setUp, tearDown } from "../__support__/setup"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("Check DNS", () => { + it("should be ok", async () => { + const response = await checkDNS(["1.1.1.1"]); + expect(response).toBe("1.1.1.1"); + }); +}); diff --git a/packages/core-p2p/__tests__/utils/check-ntp.test.js b/packages/core-p2p/__tests__/utils/check-ntp.test.js deleted file mode 100644 index 5cee0056a0..0000000000 --- a/packages/core-p2p/__tests__/utils/check-ntp.test.js +++ /dev/null @@ -1,44 +0,0 @@ -const app = require('../__support__/setup') - -let checker - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(() => { - checker = require('../../lib/utils/check-ntp') -}) - -describe('Check NTP', () => { - const hosts = ['pool.ntp.org', 'time.google.com'] - const host = hosts[0] - - it('should be a function', () => { - expect(checker).toBeFunction() - }) - - it('should get the time from hosts', async () => { - const response = await checker([host]) - - expect(response).toBeObject() - expect(response.host).toBe(host) - expect(response.time).toBeObject() - expect(response.time.t).toBeNumber() - }) - - describe('when none of the host could be reached', () => { - it('produces an error', async () => { - try { - await checker(['notime.unknown.not']) - throw new Error('An error should have been thrown') - } catch (error) { - expect(error.message).toMatch(/ntp.*connect/i) - } - }) - }) -}) diff --git a/packages/core-p2p/__tests__/utils/check-ntp.test.ts b/packages/core-p2p/__tests__/utils/check-ntp.test.ts new file mode 100644 index 0000000000..7f8635de0c --- /dev/null +++ b/packages/core-p2p/__tests__/utils/check-ntp.test.ts @@ -0,0 +1,35 @@ +import { checkNTP } from "../../src/utils"; +import { setUp, tearDown } from "../__support__/setup"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("Check NTP", () => { + const hosts = ["pool.ntp.org", "time.google.com"]; + const host = hosts[0]; + + it("should get the time from hosts", async () => { + const response = await checkNTP([host]); + + expect(response).toBeObject(); + expect(response.host).toBe(host); + expect(response.time).toBeObject(); + expect(response.time.t).toBeNumber(); + }); + + describe("when none of the host could be reached", () => { + it("produces an error", async () => { + try { + await checkNTP(["notime.unknown.not"]); + throw new Error("An error should have been thrown"); + } catch (error) { + expect(error.message).toMatch(/ntp.*connect/i); + } + }); + }); +}); diff --git a/packages/core-p2p/__tests__/utils/is-myself.test.js b/packages/core-p2p/__tests__/utils/is-myself.test.js deleted file mode 100644 index 873aa9d8f9..0000000000 --- a/packages/core-p2p/__tests__/utils/is-myself.test.js +++ /dev/null @@ -1,32 +0,0 @@ -const os = require('os') -const isMyself = require('../../lib/utils/is-myself') - -describe('isMyself', () => { - it('should be a function', () => { - expect(isMyself).toBeFunction() - }) - - it('should be ok for localhost addresses', () => { - expect(isMyself('127.0.0.1')).toBeTrue() - - expect(isMyself('192.167.22.1')).toBeFalse() - }) - - it('should be ok for non-quad-dotted localhost addresses', () => { - expect(isMyself('2130706433')).toBeTrue() - }) - - it('should be ok for LAN addresses', () => { - const interfaces = os.networkInterfaces() - const addresses = [] - - // getting local addresses - Object.keys(interfaces).forEach(ifname => { - interfaces[ifname].some(iface => addresses.push(iface.address)) - }) - - addresses.forEach(ipAddress => { - expect(isMyself(ipAddress)).toBeTrue() - }) - }) -}) diff --git a/packages/core-p2p/__tests__/utils/is-valid-peer.test.ts b/packages/core-p2p/__tests__/utils/is-valid-peer.test.ts new file mode 100644 index 0000000000..61f1970ece --- /dev/null +++ b/packages/core-p2p/__tests__/utils/is-valid-peer.test.ts @@ -0,0 +1,58 @@ +import "jest-extended"; + +import os from "os"; +import { isValidPeer } from "../../src/utils"; + +describe("isValidPeer", () => { + it("should not be ok for 127.0.0.1", () => { + expect(isValidPeer({ ip: "127.0.0.1" })).toBeFalse(); + }); + + it("should not be ok for ::ffff:127.0.0.1", () => { + const peer = { ip: "::ffff:127.0.0.1" }; + expect(isValidPeer(peer)).toBeFalse(); + }); + + it("should not be ok for 0.0.0.0", () => { + expect(isValidPeer({ ip: "0.0.0.0" })).toBeFalse(); + }); + + it("should not be ok for ::1", () => { + const peer = { ip: "::1" }; + expect(isValidPeer(peer)).toBeFalse(); + }); + + it("should not be ok for 2130706433", () => { + const peer = { ip: "2130706433" }; + expect(isValidPeer(peer)).toBeFalse(); + }); + + it("should not be ok for garbage", () => { + expect(isValidPeer({ ip: "garbage" })).toBeFalse(); + }); + + it("should not be ok for invalid status", () => { + expect(isValidPeer({ ip: "5.196.105.32", status: 400 })).toBeFalse(); + }); + + it("should not be ok for LAN addresses", () => { + const interfaces = os.networkInterfaces(); + const addresses = []; + + // getting local addresses + Object.keys(interfaces).forEach(ifname => { + interfaces[ifname].some(iface => (addresses as any).push(iface.address)); + }); + + addresses.forEach(ipAddress => { + expect(isValidPeer({ ip: ipAddress })).toBeFalse(); + }); + }); + + it("should be ok", () => { + expect(isValidPeer({ ip: "192.168.178.0" })).toBeTrue(); + expect(isValidPeer({ ip: "5.196.105.32" })).toBeTrue(); + expect(isValidPeer({ ip: "5.196.105.32", status: 200 })).toBeTrue(); + expect(isValidPeer({ ip: "5.196.105.32", status: "OK" })).toBeTrue(); + }); +}); diff --git a/packages/core-p2p/__tests__/utils/is-whitelist.test.js b/packages/core-p2p/__tests__/utils/is-whitelist.test.js deleted file mode 100644 index 4d16aa68f8..0000000000 --- a/packages/core-p2p/__tests__/utils/is-whitelist.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const isWhitelist = require('../../lib/utils/is-whitelist') - -const whitelisted = ['127.0.0.1', '::ffff:127.0.0.1'] - -describe('isWhitelist', () => { - it('should be a function', () => { - expect(isWhitelist).toBeFunction() - }) - - it('should be ok for 127.0.0.1', () => { - expect(isWhitelist(whitelisted, '127.0.0.1')).toBeTrue() - }) - - it('should be ok for ::ffff:127.0.0.1', () => { - expect(isWhitelist(whitelisted, '::ffff:127.0.0.1')).toBeTrue() - }) - - it('should not be ok', () => { - expect(isWhitelist(whitelisted, 'dummy')).toBeFalse() - }) -}) diff --git a/packages/core-p2p/__tests__/utils/is-whitelist.test.ts b/packages/core-p2p/__tests__/utils/is-whitelist.test.ts new file mode 100644 index 0000000000..8894feab69 --- /dev/null +++ b/packages/core-p2p/__tests__/utils/is-whitelist.test.ts @@ -0,0 +1,17 @@ +import { isWhitelisted } from "../../src/utils"; + +const whitelist = ["127.0.0.1", "::ffff:127.0.0.1"]; + +describe("isWhitelist", () => { + it("should be ok for 127.0.0.1", () => { + expect(isWhitelisted(whitelist, "127.0.0.1")).toBeTrue(); + }); + + it("should be ok for ::ffff:127.0.0.1", () => { + expect(isWhitelisted(whitelist, "::ffff:127.0.0.1")).toBeTrue(); + }); + + it("should not be ok", () => { + expect(isWhitelisted(whitelist, "dummy")).toBeFalse(); + }); +}); diff --git a/packages/core-p2p/jest.config.js b/packages/core-p2p/jest.config.js deleted file mode 100644 index 9b709ac547..0000000000 --- a/packages/core-p2p/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: false, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-p2p/lib/court/guard.js b/packages/core-p2p/lib/court/guard.js deleted file mode 100644 index 8656efd516..0000000000 --- a/packages/core-p2p/lib/court/guard.js +++ /dev/null @@ -1,315 +0,0 @@ -const app = require('@arkecosystem/core-container') -const dayjs = require('dayjs-ext') -const head = require('lodash/head') -const prettyMs = require('pretty-ms') -const semver = require('semver') -const sumBy = require('lodash/sumBy') - -const config = app.resolvePlugin('config') -const logger = app.resolvePlugin('logger') - -const isMyself = require('../utils/is-myself') -const offences = require('./offences') - -class Guard { - /** - * Create a new guard instance. - */ - constructor() { - this.suspensions = {} - } - - /** - * Initialise a new guard. - * @param {Monitor} monitor - */ - init(monitor) { - this.monitor = monitor - - return this - } - - /** - * Get a list of all suspended peers. - * @return {Object} - */ - all() { - return this.suspensions - } - - /** - * Get the suspended peer for the give IP. - * @return {Object} - */ - get(ip) { - return this.suspensions[ip] - } - - /** - * Suspends a peer unless whitelisted. - * @param {Peer} peer - */ - suspend(peer) { - if (config.peers.whiteList && config.peers.whiteList.includes(peer.ip)) { - return - } - - if (peer.offences.length > 0) { - if (dayjs().isAfter(head(peer.offences).until)) { - peer.offences = [] - } - } - - const offence = this.__determineOffence(peer) - - peer.offences.push(offence) - - this.suspensions[peer.ip] = { - peer, - until: offence.until, - reason: offence.reason, - } - - this.monitor.removePeer(peer) - } - - /** - * Remove a suspended peer. - * @param {Peer} peer - * @return {void} - */ - async unsuspend(peer) { - if (!this.suspensions[peer.ip]) { - return - } - - // Don't unsuspend critical offenders before the ban is expired. - if (peer.offences.some(offence => offence.critical)) { - if (dayjs().isBefore(this.suspensions[peer.ip].until)) { - return - } - } - - delete this.suspensions[peer.ip] - delete peer.nextSuspensionReminder - - await this.monitor.acceptNewPeer(peer) - } - - /** - * Reset suspended peers - * @return {void} - */ - async resetSuspendedPeers() { - logger.info('Clearing suspended peers.') - await Promise.all( - Object.values(this.suspensions).map(suspension => - this.unsuspend(suspension.peer), - ), - ) - } - - /** - * Determine if peer is suspended or not. - * @param {Peer} peer - * @return {Boolean} - */ - isSuspended(peer) { - const suspendedPeer = this.get(peer.ip) - - if (suspendedPeer && dayjs().isBefore(suspendedPeer.until)) { - const nextSuspensionReminder = suspendedPeer.nextSuspensionReminder - - if (!nextSuspensionReminder || dayjs().isAfter(nextSuspensionReminder)) { - const untilDiff = suspendedPeer.until.diff(dayjs()) - - logger.debug( - `${peer.ip} still suspended for ${prettyMs(untilDiff, { - verbose: true, - })} because of "${suspendedPeer.reason}".`, - ) - - suspendedPeer.nextSuspensionReminder = dayjs().add(5, 'm') - } - - return true - } - - if (suspendedPeer) { - delete this.suspensions[peer.ip] - } - - return false - } - - /** - * Determine if the peer is whitelisted. - * @param {Peer} peer - * @return {Boolean} - */ - isWhitelisted(peer) { - return config.peers.whiteList.includes(peer.ip) - } - - /** - * Determine if the peer is blacklisted. - * @param {Peer} peer - * @return {Boolean} - */ - isBlacklisted(peer) { - return config.peers.blackList.includes(peer.ip) - } - - /** - * Determine if the peer is within the version constraints. - * @param {Peer} peer - * @return {Boolean} - */ - isValidVersion(peer) { - const version = peer.version || (peer.headers && peer.headers.version) - return semver.satisfies(version, config.peers.minimumVersion) - } - - /** - * Determine if the peer is on the right network. - * @param {Peer} peer - * @return {Boolean} - */ - isValidNetwork(peer) { - const nethash = peer.nethash || (peer.headers && peer.headers.nethash) - return nethash === config.network.nethash - } - - /** - * Determine if the peer has a valid port. - * @param {Peer} peer - * @return {Boolean} - */ - isValidPort(peer) { - return peer.port === app.resolveOptions('p2p').port - } - - /** - * Determine if the peer is localhost. - * @param {Peer} peer - * @return {Boolean} - */ - isMyself(peer) { - return isMyself(peer.ip) - } - - /** - * Decide if the given peer is a repeat offender. - * @param {Object} peer - * @return {Boolean} - */ - isRepeatOffender(peer) { - return sumBy(peer.offences, 'weight') >= 150 - } - - /** - * Decide for how long the peer should be banned. - * @param {Peer} peer - * @return {dayjs} - */ - __determineOffence(peer) { - if (this.isBlacklisted(peer)) { - return this.__determinePunishment(peer, offences.BLACKLISTED) - } - - try { - const state = app.resolve('state') - - if (state.forkedBlock && peer.ip === state.forkedBlock.ip) { - return this.__determinePunishment(peer, offences.FORK) - } - } catch (error) { - logger.warn( - `The state storage is not ready, skipped fork check for ${peer.ip}.`, - ) - } - - if (peer.commonBlocks === false) { - delete peer.commonBlocks - - return this.__determinePunishment(peer, offences.NO_COMMON_BLOCKS) - } - - if (peer.commonId === false) { - delete peer.commonId - - return this.__determinePunishment(peer, offences.NO_COMMON_ID) - } - - // NOTE: We check this extra because a response can still succeed if - // it returns any codes that are not 4xx or 5xx. - if (peer.status === 503) { - return this.__determinePunishment(peer, offences.BLOCKCHAIN_NOT_READY) - } - - if (peer.status === 429) { - return this.__determinePunishment(peer, offences.TOO_MANY_REQUESTS) - } - - if (peer.status && peer.status !== 200) { - return this.__determinePunishment(peer, offences.INVALID_STATUS) - } - - if (peer.delay === -1) { - return this.__determinePunishment(peer, offences.TIMEOUT) - } - - if (peer.delay > 2000) { - return this.__determinePunishment(peer, offences.HIGH_LATENCY) - } - - if (!this.isValidNetwork(peer)) { - return this.__determinePunishment(peer, offences.INVALID_NETWORK) - } - - if (!this.isValidVersion(peer)) { - return this.__determinePunishment(peer, offences.INVALID_VERSION) - } - - // NOTE: Suspending this peer only means that we no longer - // will download blocks from him but he can still download blocks from us. - const heightDifference = Math.abs( - this.monitor.getNetworkHeight() - peer.state.height, - ) - - if (heightDifference >= 153) { - return this.__determinePunishment(peer, offences.INVALID_HEIGHT) - } - - return this.__determinePunishment(peer, offences.UNKNOWN) - } - - /** - * Compile the information about the punishment the peer will face. - * @param {Object} peer - * @param {Object} offence - * @return {Object} - */ - __determinePunishment(peer, offence) { - if (this.isRepeatOffender(peer)) { - offence = offences.REPEAT_OFFENDER - } - - const until = dayjs().add(offence.number, offence.period) - const untilDiff = until.diff(dayjs()) - - logger.debug( - `Suspended ${peer.ip} for ${prettyMs(untilDiff, { - verbose: true, - })} because of "${offence.reason}"`, - ) - - return { - until, - reason: offence.reason, - weight: offence.weight, - } - } -} - -module.exports = new Guard() diff --git a/packages/core-p2p/lib/court/index.js b/packages/core-p2p/lib/court/index.js deleted file mode 100644 index 1fb69f49f6..0000000000 --- a/packages/core-p2p/lib/court/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - guard: require('./guard'), -} diff --git a/packages/core-p2p/lib/court/offences.js b/packages/core-p2p/lib/court/offences.js deleted file mode 100644 index ca48eff938..0000000000 --- a/packages/core-p2p/lib/court/offences.js +++ /dev/null @@ -1,89 +0,0 @@ -module.exports = { - BLACKLISTED: { - number: 12, - period: 'hours', - reason: 'Blacklisted', - weight: 10, - }, - NO_COMMON_BLOCKS: { - number: 5, - period: 'minutes', - reason: 'No Common Blocks', - weight: 1, - critical: true, - }, - NO_COMMON_ID: { - number: 5, - period: 'minutes', - reason: 'No Common Id', - weight: 1, - critical: true, - }, - INVALID_VERSION: { - number: 6, - period: 'hours', - reason: 'Invalid Version', - weight: 7, - }, - INVALID_HEIGHT: { - number: 10, - period: 'minutes', - reason: 'Node is not at height', - weight: 5, - }, - INVALID_NETWORK: { - number: 5, - period: 'minutes', - reason: 'Invalid Network', - weight: 5, - }, - INVALID_STATUS: { - number: 5, - period: 'minutes', - reason: 'Invalid Response Status', - weight: 3, - }, - TIMEOUT: { - number: 2, - period: 'minutes', - reason: 'Timeout', - weight: 2, - }, - HIGH_LATENCY: { - number: 1, - period: 'minutes', - reason: 'High Latency', - weight: 1, - }, - BLOCKCHAIN_NOT_READY: { - number: 30, - period: 'seconds', - reason: 'Blockchain not ready', - weight: 0, - }, - TOO_MANY_REQUESTS: { - number: 60, - period: 'seconds', - reason: 'Rate limit exceeded', - weight: 0, - }, - UNKNOWN: { - number: 30, - period: 'minutes', - reason: 'Unknown', - weight: 5, - }, - REPEAT_OFFENDER: { - number: 1, - period: 'day', - reason: 'Repeat Offender', - weight: 100, - }, - FORK: { - number: 1, - period: 'day', - reason: 'Fork', - weight: 150, - critical: true, - }, -} diff --git a/packages/core-p2p/lib/defaults.js b/packages/core-p2p/lib/defaults.js deleted file mode 100644 index 211e99f2d6..0000000000 --- a/packages/core-p2p/lib/defaults.js +++ /dev/null @@ -1,29 +0,0 @@ -module.exports = { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4002, - remoteInterface: false, - dns: [ - // Google - '8.8.8.8', - '8.8.4.4', - // CloudFlare - '1.1.1.1', - '1.0.0.1', - // OpenDNS - '208.67.222.222', - '208.67.220.220', - ], - ntp: ['pool.ntp.org', 'time.google.com'], - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - // @see https://github.com/wraithgar/hapi-rate-limit - rateLimit: { - enabled: true, - pathLimit: false, - userLimit: 20, - userCache: { - expiresIn: 1000, - }, - ipWhitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - maxPeersBroadcast: 20, -} diff --git a/packages/core-p2p/lib/index.js b/packages/core-p2p/lib/index.js deleted file mode 100644 index bb7eda1474..0000000000 --- a/packages/core-p2p/lib/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const monitor = require('./monitor') -const startServer = require('./server') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'p2p', - async register(container, options) { - container.resolvePlugin('logger').info('Starting P2P Interface') - - monitor.server = await startServer(monitor, options) - - await monitor.start(options) - - return monitor - }, - async deregister(container, options) { - container.resolvePlugin('logger').info('Stopping P2P Interface') - - const p2p = container.resolvePlugin('p2p') - p2p.dumpPeers() - - return p2p.server.stop() - }, -} diff --git a/packages/core-p2p/lib/monitor.js b/packages/core-p2p/lib/monitor.js deleted file mode 100755 index 483bb211c0..0000000000 --- a/packages/core-p2p/lib/monitor.js +++ /dev/null @@ -1,897 +0,0 @@ -/* eslint no-restricted-globals: "off" */ - -const prettyMs = require('pretty-ms') -const fs = require('fs') -const dayjs = require('dayjs-ext') -const delay = require('delay') -const flatten = require('lodash/flatten') -const groupBy = require('lodash/groupBy') -const sample = require('lodash/sample') -const shuffle = require('lodash/shuffle') -const take = require('lodash/take') -const pluralize = require('pluralize') - -const { slots } = require('@arkecosystem/crypto') - -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const logger = app.resolvePlugin('logger') -const emitter = app.resolvePlugin('event-emitter') - -const Peer = require('./peer') -const { guard } = require('./court') -const networkState = require('./utils/network-state') - -const checkDNS = require('./utils/check-dns') -const checkNTP = require('./utils/check-ntp') - -class Monitor { - /** - * @constructor - * @throws {Error} If no seed peers - */ - constructor() { - this.peers = {} - this.coldStartPeriod = dayjs().add(config.peers.coldStart || 30, 'seconds') - - this.nextUpdateNetworkStatusScheduled = false - - // Holds temporary peers which are in the process of being accepted. Prevents that - // peers who are not accepted yet, but send multiple requests in a short timeframe will - // get processed multiple times in `acceptNewPeer`. - this.pendingPeers = {} - } - - /** - * Method to run on startup. - * @param {Object} options - */ - async start(options) { - this.config = options - - await this.__checkDNSConnectivity(options.dns) - await this.__checkNTPConnectivity(options.ntp) - - this.guard = guard.init(this) - - this.__populateSeedPeers() - - if (this.config.skipDiscovery) { - logger.warn( - 'Skipped peer discovery because the relay is in skip-discovery mode.', - ) - } else { - await this.updateNetworkStatus(options.networkStart) - - for (const [version, peers] of Object.entries( - groupBy(this.peers, 'version'), - )) { - logger.info( - `Discovered ${pluralize( - 'peer', - peers.length, - true, - )} with v${version}.`, - ) - } - - if (config.network.name !== 'mainnet') { - for (const [hashid, peers] of Object.entries( - groupBy(this.peers, 'hashid'), - )) { - logger.info( - `Discovered ${pluralize( - 'peer', - peers.length, - true, - )} on commit ${hashid}.`, - ) - } - } - } - - return this - } - - /** - * Update network status (currently only peers are updated). - * @param {Boolean} networkStart - * @return {Promise} - */ - async updateNetworkStatus(networkStart) { - if (process.env.ARK_ENV === 'test' || process.env.NODE_ENV === 'test') { - return - } - - if (networkStart) { - logger.warn( - 'Skipped peer discovery because the relay is in genesis-start mode.', - ) - return - } - - if (this.config.disableDiscovery) { - logger.warn( - 'Skipped peer discovery because the relay is in non-discovery mode.', - ) - return - } - - try { - await this.discoverPeers() - await this.cleanPeers() - } catch (error) { - logger.error(`Network Status: ${error.message}`) - } - - let nextRunDelaySeconds = 600 - - if (!this.__hasMinimumPeers()) { - this.__populateSeedPeers(); - nextRunDelaySeconds = 5; - logger.info(`Couldn't find enough peers. Falling back to seed peers.`); - } - - this.__scheduleUpdateNetworkStatus(nextRunDelaySeconds) - } - - /** - * Accept and store a valid peer. - * @param {Peer} peer - * @throws {Error} If invalid peer - */ - async acceptNewPeer(peer) { - if (this.config.disableDiscovery && !this.pendingPeers[peer.ip]) { - logger.warn( - `Rejected ${peer.ip} because the relay is in non-discovery mode.`, - ) - return - } - - if ( - this.guard.isSuspended(peer) || - this.guard.isMyself(peer) || - this.pendingPeers[peer.ip] || - process.env.ARK_ENV === 'test' - ) { - return - } - - const newPeer = new Peer(peer.ip, peer.port) - newPeer.setHeaders(peer) - - if (this.guard.isBlacklisted(peer)) { - logger.debug(`Rejected peer ${peer.ip} as it is blacklisted`) - - return this.guard.suspend(newPeer) - } - - if (!this.guard.isValidVersion(peer) && !this.guard.isWhitelisted(peer)) { - logger.debug( - `Rejected peer ${ - peer.ip - } as it doesn't meet the minimum version requirements. Expected: ${ - config.peers.minimumVersion - } - Received: ${peer.version}`, - ) - - return this.guard.suspend(newPeer) - } - - if (!this.guard.isValidNetwork(peer)) { - logger.debug( - `Rejected peer ${peer.ip} as it isn't on the same network. Expected: ${ - config.network.nethash - } - Received: ${peer.nethash}`, - ) - - return this.guard.suspend(newPeer) - } - - if (this.getPeer(peer.ip)) { - return - } - - try { - this.pendingPeers[peer.ip] = true - - await newPeer.ping(1500) - - this.peers[peer.ip] = newPeer - - logger.debug(`Accepted new peer ${newPeer.ip}:${newPeer.port}`) - - emitter.emit('peer.added', newPeer) - } catch (error) { - logger.debug( - `Could not accept new peer '${newPeer.ip}:${newPeer.port}' - ${error}`, - ) - - this.guard.suspend(newPeer) - } finally { - delete this.pendingPeers[peer.ip] - } - } - - /** - * Remove peer from monitor. - * @param {Peer} peer - */ - removePeer(peer) { - delete this.peers[peer.ip] - } - - /** - * Clear peers which aren't responding. - * @param {Boolean} fast - * @param {Boolean} tracker - * @param {Boolean} forcePing - */ - async cleanPeers(fast = false, tracker = true, forcePing = false) { - const keys = Object.keys(this.peers) - let count = 0 - let unresponsivePeers = 0 - const pingDelay = fast ? 1500 : config.peers.globalTimeout - const max = keys.length - - logger.info(`Checking ${max} peers :telescope:`) - await Promise.all( - keys.map(async ip => { - const peer = this.getPeer(ip) - try { - await peer.ping(pingDelay, forcePing) - - if (tracker) { - logger.printTracker('Peers Discovery', ++count, max) - } - } catch (error) { - unresponsivePeers++ - - const formattedDelay = prettyMs(pingDelay, { verbose: true }) - logger.debug( - `Removed peer ${ip} because it didn't respond within ${formattedDelay}.`, - ) - emitter.emit('peer.removed', peer) - - this.removePeer(peer) - - return null - } - }), - ) - - if (tracker) { - logger.stopTracker('Peers Discovery', max, max) - logger.info( - `${max - - unresponsivePeers} of ${max} peers on the network are responsive`, - ) - logger.info( - `Median Network Height: ${this.getNetworkHeight().toLocaleString()}`, - ) - logger.info(`Network PBFT status: ${this.getPBFTForgingStatus()}`) - } - } - - /** - * Suspend an existing peer. - * @param {Peer} peer - * @return {void} - */ - suspendPeer(ip) { - const peer = this.peers[ip] - - if (peer && !this.guard.isSuspended(peer)) { - this.guard.suspend(peer) - } - } - - /** - * Get a list of all suspended peers. - * @return {void} - */ - getSuspendedPeers() { - return this.guard.all() - } - - /** - * Get all available peers. - * @return {Peer[]} - */ - getPeers() { - return Object.values(this.peers) - } - - /** - * Get the peer available peers. - * @param {String} ip - * @return {Peer} - */ - getPeer(ip) { - return this.peers[ip] - } - - async peerHasCommonBlocks(peer, blockIds) { - if (!this.guard.isMyself(peer) && !(await peer.hasCommonBlocks(blockIds))) { - logger.error(`Could not get common block for ${peer.ip}`) - - peer.commonBlocks = false - - this.guard.suspend(peer) - - return false - } - - return true - } - - /** - * Get a random, available peer. - * @param {(Number|undefined)} acceptableDelay - * @return {Peer} - */ - getRandomPeer(acceptableDelay, downloadSize, failedAttempts) { - failedAttempts = failedAttempts === undefined ? 0 : failedAttempts - - const peers = this.getPeers().filter(peer => { - if (peer.ban < new Date().getTime()) { - return true - } - - if (acceptableDelay && peer.delay < acceptableDelay) { - return true - } - - if (downloadSize && peer.downloadSize !== downloadSize) { - return true - } - - return false - }) - - const randomPeer = sample(peers) - if (!randomPeer) { - failedAttempts++ - - if (failedAttempts > 10) { - throw new Error('Failed to find random peer') - } else if (failedAttempts > 5) { - return this.getRandomPeer(null, downloadSize, failedAttempts) - } - - return this.getRandomPeer(acceptableDelay, downloadSize, failedAttempts) - } - - return randomPeer - } - - /** - * Get a random, available peer which can be used for downloading blocks. - * @return {Peer} - */ - async getRandomDownloadBlocksPeer(minHeight) { - const randomPeer = this.getRandomPeer(null, 100) - - const recentBlockIds = await this.__getRecentBlockIds() - if (!(await this.peerHasCommonBlocks(randomPeer, recentBlockIds))) { - return this.getRandomDownloadBlocksPeer(minHeight) - } - - return randomPeer - } - - /** - * Populate list of available peers from random peers. - */ - async discoverPeers() { - const shuffledPeers = shuffle(this.getPeers()) - - for (const peer of shuffledPeers) { - try { - const hisPeers = await peer.getPeers() - - for (const p of hisPeers) { - if (Peer.isOk(p) && !this.getPeer(p.ip) && !this.guard.isMyself(p)) { - this.__addPeer(p) - } - } - } catch (error) { - // Just try with the next peer from shuffledPeers. - } - - if (this.__hasMinimumPeers()) { - return; - } - } - } - - /** - * Check if we have any peers. - * @return {bool} - */ - hasPeers() { - return !!this.getPeers().length - } - - /** - * Get the median network height. - * @return {Number} - */ - getNetworkHeight() { - const medians = this.getPeers() - .filter(peer => peer.state.height) - .map(peer => peer.state.height) - .sort() - - return medians[Math.floor(medians.length / 2)] || 0 - } - - /** - * Get the PBFT Forging status. - * @return {Number} - */ - getPBFTForgingStatus() { - const height = this.getNetworkHeight() - const slot = slots.getSlotNumber() - - let allowedToForge = 0 - let syncedPeers = 0 - - for (const peer of this.getPeers()) { - if (peer.state) { - if (peer.state.currentSlot === slot) { - syncedPeers++ - - if (peer.state.forgingAllowed && peer.state.height >= height) { - allowedToForge++ - } - } - } - } - - const pbft = allowedToForge / syncedPeers - - return isNaN(pbft) ? 0 : pbft - } - - async getNetworkState() { - if (!this.__isColdStartActive()) { - await this.cleanPeers(true, false, true) - } - - return networkState(this, app.resolvePlugin('blockchain').getLastBlock()) - } - - /** - * Refresh all peers after a fork. Peers with no common blocks are - * suspended. - * @return {void} - */ - async refreshPeersAfterFork() { - logger.info(`Refreshing ${this.getPeers().length} peers after fork.`) - - // Reset all peers, except peers banned because of causing a fork. - await this.guard.resetSuspendedPeers() - - // Ban peer who caused the fork - const forkedBlock = app.resolve('state').forkedBlock - if (forkedBlock) { - this.suspendPeer(forkedBlock.ip) - } - - const recentBlockIds = await this.__getRecentBlockIds() - - await Promise.all( - this.getPeers().map(peer => - this.peerHasCommonBlocks(peer, recentBlockIds), - ), - ) - } - - /** - * Download blocks from a random peer. - * @param {Number} fromBlockHeight - * @return {Object[]} - */ - async downloadBlocks(fromBlockHeight) { - let randomPeer - - try { - randomPeer = await this.getRandomDownloadBlocksPeer(fromBlockHeight) - } catch (error) { - logger.error(`Could not download blocks: ${error.message}`) - - return [] - } - try { - logger.info( - `Downloading blocks from height ${fromBlockHeight.toLocaleString()} via ${ - randomPeer.ip - }`, - ) - - const blocks = await randomPeer.downloadBlocks(fromBlockHeight) - blocks.forEach(block => { - block.ip = randomPeer.ip - }) - - return blocks - } catch (error) { - logger.error(`Could not download blocks: ${error.message}`) - - return this.downloadBlocks(fromBlockHeight) - } - } - - /** - * Broadcast block to all peers. - * @param {Block} block - * @return {Promise} - */ - async broadcastBlock(block) { - const blockchain = app.resolvePlugin('blockchain') - - if (!blockchain) { - logger.info( - `Skipping broadcast of block ${block.data.height.toLocaleString()} as blockchain is not ready`, - ) - return - } - - let blockPing = blockchain.getBlockPing() - let peers = this.getPeers() - - if (blockPing && blockPing.block.id === block.data.id) { - // wait a bit before broadcasting if a bit early - const diff = blockPing.last - blockPing.first - const maxHop = 4 - let proba = (maxHop - blockPing.count) / maxHop - - if (diff < 500 && proba > 0) { - await delay(500 - diff) - - blockPing = blockchain.getBlockPing() - - // got aleady a new block, no broadcast - if (blockPing.block.id !== block.data.id) { - return - } - - proba = (maxHop - blockPing.count) / maxHop - } - - // TODO: to be put in config? - peers = peers.filter(p => Math.random() < proba) - } - - logger.info( - `Broadcasting block ${block.data.height.toLocaleString()} to ${pluralize( - 'peer', - peers.length, - true, - )}`, - ) - - await Promise.all(peers.map(peer => peer.postBlock(block.toJson()))) - } - - /** - * Broadcast transactions to a fixed number of random peers. - * @param {Transaction[]} transactions - */ - async broadcastTransactions(transactions) { - const maxPeersBroadcast = app.resolveOptions('p2p').maxPeersBroadcast - const peers = take(shuffle(this.getPeers()), maxPeersBroadcast) - - logger.debug( - `Broadcasting ${pluralize( - 'transaction', - transactions.length, - true, - )} to ${pluralize('peer', peers.length, true)}`, - ) - - transactions = transactions.map(tx => tx.toJson()) - return Promise.all(peers.map(peer => peer.postTransactions(transactions))) - } - - /** - * Update all peers based on height and last block id. - * - * Grouping peers by height and then by common id results in one of the following - * scenarios: - * - * 1) Same height, same common id - * 2) Same height, mixed common id - * 3) Mixed height, same common id - * 4) Mixed height, mixed common id - * - * Scenario 1: Do nothing. - * Scenario 2-4: - * - If own height is ahead of majority do nothing for now. - * - Pick most common id from peers with most common height and calculate quota, - * depending on which the node rolls back or waits. - * - * NOTE: Only called when the network is consecutively missing blocks `p2pUpdateCounter` times. - * @return {String} - */ - async updatePeersOnMissingBlocks() { - // First ping all peers to get updated heights and remove unresponsive ones. - if (!this.__isColdStartActive()) { - await this.cleanPeers(true, false) - } - - const peersGroupedByHeight = groupBy(this.getPeers(), 'state.height') - const commonHeightGroups = Object.values(peersGroupedByHeight).sort( - (a, b) => b.length - a.length, - ) - const peersMostCommonHeight = commonHeightGroups[0] - const groupedByCommonId = groupBy(peersMostCommonHeight, 'state.header.id') - const commonIdGroupCount = Object.keys(groupedByCommonId).length - let state = '' - - if (commonHeightGroups.length === 1 && commonIdGroupCount === 1) { - // No need to do anything. - return state - } - - const lastBlock = app.resolve('state').getLastBlock() - - // Do nothing if majority of peers are lagging behind - if (commonHeightGroups.length > 1) { - if (lastBlock.data.height > peersMostCommonHeight[0].state.height) { - logger.info( - `${pluralize( - 'peer', - peersMostCommonHeight.length, - true, - )} are at height ${peersMostCommonHeight[0].state.height.toLocaleString()} and lagging behind last height ${lastBlock.data.height.toLocaleString()}. :zzz:`, - ) - return state - } - } - - // Sort common id groups by length DESC - const commonIdGroups = Object.values(groupedByCommonId).sort( - (a, b) => b.length - a.length, - ) - - // Peers are sitting on the same height, but there might not be enough - // quorum to move on, because of different last blocks. - if (commonIdGroupCount > 1) { - const chosenPeers = commonIdGroups[0] - const restGroups = commonIdGroups.slice(1) - - if (restGroups.some(group => group.length === chosenPeers.length)) { - logger.warn( - 'Peers are evenly split at same height with different block ids. :zap:', - ) - } - - logger.info( - `Detected peers at the same height ${peersMostCommonHeight[0].state.height.toLocaleString()} with different block ids: ${JSON.stringify( - Object.keys(groupedByCommonId).map( - k => `${k}: ${groupedByCommonId[k].length}`, - ), - null, - 4, - )}`, - ) - - const badLastBlock = - chosenPeers[0].state.height === lastBlock.data.height && - chosenPeers[0].state.header.id !== lastBlock.data.id - const quota = chosenPeers.length / flatten(commonIdGroups).length - if (badLastBlock && quota >= 0.66) { - // Rollback if last block is bad and quota high - logger.info( - `Last block id ${ - lastBlock.data.id - } is bad. Going to rollback. :repeat:`, - ) - state = 'rollback' - } else if (quota < 0.66) { - // or quota too low TODO: find better number - logger.info( - `Common id quota '${quota}' is too low. Going to rollback. :repeat:`, - ) - state = 'rollback' - } - - if (state === 'rollback') { - // Ban all rest peers - const peersToBan = flatten(restGroups) - peersToBan.forEach(peer => { - peer.commonId = false - this.suspendPeer(peer.ip) - }) - - logger.debug( - `Banned ${pluralize( - 'peer', - peersToBan.length, - true, - )} at height '${peersMostCommonHeight[0].state.height.toLocaleString()}' which do not have common id '${ - chosenPeers[0].state.header.id - }'.`, - ) - } else { - logger.info(`But got enough common id quota: ${quota} :sparkles:`) - } - } else { - // Under certain circumstances the headers can be missing (i.e. seed peers when starting up) - const commonHeader = peersMostCommonHeight[0].state.header - logger.info( - `All peers at most common height ${peersMostCommonHeight[0].state.height.toLocaleString()} share the same block id${ - commonHeader ? ` '${commonHeader.id}'` : '' - }. :pray:`, - ) - } - - return state - } - - /** - * Dump the list of active peers. - * @return {void} - */ - dumpPeers() { - const peers = Object.values(this.peers).map(peer => ({ - ip: peer.ip, - port: peer.port, - version: peer.version, - })) - - try { - fs.writeFileSync( - `${process.env.ARK_PATH_CONFIG}/peers_backup.json`, - JSON.stringify(peers, null, 2), - ) - } catch (err) { - logger.error(`Failed to dump the peer list because of "${err.message}"`) - } - } - - /** - * Get last 10 block IDs from database. - * @return {[]String} - */ - async __getRecentBlockIds() { - return app.resolvePlugin('database').getRecentBlockIds() - } - - /** - * Determines if coldstart is still active. - * We need this for the network to start, so we dont forge, while - * not all peers are up, or the network is not active - */ - __isColdStartActive() { - return this.coldStartPeriod.isAfter(dayjs()) - } - - /** - * Check if the node can connect to any DNS host. - * @return {void} - */ - async __checkDNSConnectivity(options) { - try { - const host = await checkDNS(options) - - logger.info(`Your network connectivity has been verified by ${host}`) - } catch (error) { - logger.error(error.message) - } - } - - /** - * Check if the node can connect to any NTP host. - * @return {void} - */ - async __checkNTPConnectivity(options) { - try { - const { host, time } = await checkNTP(options) - - logger.info(`Your NTP connectivity has been verified by ${host}`) - - logger.info( - `Local clock is off by ${parseInt(time.t)}ms from NTP :alarm_clock:`, - ) - } catch (error) { - logger.error(error.message) - } - } - - /** - * Add a new peer after it passes a few checks. - * @param {Peer} peer - * @return {void} - */ - __addPeer(peer) { - if (this.guard.isBlacklisted(peer)) { - return - } - - if (!this.guard.isValidVersion(peer)) { - return - } - - if (!this.guard.isValidNetwork(peer)) { - return - } - - if (!this.guard.isValidPort(peer)) { - return - } - - this.peers[peer.ip] = new Peer(peer.ip, peer.port) - } - - /** - * Add new peers after they pass a few checks. - * @param {Peer[]} peers - * @return {void} - */ - __addPeers(peers) { - for (const peer of peers) { - this.__addPeer(peer) - } - } - - /** - * Schedule the next update network status. - * @param {Number} nextUpdateInSeconds - * @returns {void} - */ - async __scheduleUpdateNetworkStatus(nextUpdateInSeconds) { - if (this.nextUpdateNetworkStatusScheduled) { - return - } - - this.nextUpdateNetworkStatusScheduled = true - - await delay(nextUpdateInSeconds * 1000) - - this.nextUpdateNetworkStatusScheduled = false - - this.updateNetworkStatus(this.config.networkStart) - } - - /** - * Returns if the minimum amount of peers are available. - * @return {Boolean} - */ - __hasMinimumPeers() { - return Object.keys(this.peers).length >= config.peers.minimumNetworkReach - } - - /** - * Populate the initial seed list. - * @return {void} - */ - __populateSeedPeers() { - if (!config.peers.list) { - app.forceExit("No seed peers defined in peers.json :interrobang:") - } - - let peers = config.peers.list.map(peer => { - peer.version = app.getVersion() - return peer - }) - - if (config.peers_backup) { - peers = { ...peers, ...config.peers_backup } - } - - const filteredPeers = Object.values(peers).filter( - peer => !this.guard.isMyself(peer) || !this.guard.isValidPort(peer) || !this.guard.isValid - ) - - for (const peer of filteredPeers) { - delete this.guard.suspensions[peer.ip] - this.peers[peer.ip] = new Peer(peer.ip, peer.port) - } - } -} - -module.exports = new Monitor() diff --git a/packages/core-p2p/lib/peer.js b/packages/core-p2p/lib/peer.js deleted file mode 100755 index 083be6aee7..0000000000 --- a/packages/core-p2p/lib/peer.js +++ /dev/null @@ -1,320 +0,0 @@ -const axios = require('axios') -const chunk = require('lodash/chunk') -const util = require('util') -const dayjs = require('dayjs-ext') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const config = app.resolvePlugin('config') - -module.exports = class Peer { - /** - * @constructor - * @param {String} ip - * @param {Number} port - */ - constructor(ip, port) { - this.ip = ip - this.port = port - this.ban = new Date().getTime() - this.url = `${port % 443 === 0 ? 'https://' : 'http://'}${ip}:${port}` - this.state = {} - this.offences = [] - this.lastPinged = null - - this.headers = { - version: app.getVersion(), - port: app.resolveOptions('p2p').port, - nethash: config.network.nethash, - height: null, - 'Content-Type': 'application/json', - } - - if (config.network.name !== 'mainnet') { - this.headers.hashid = app.getHashid() - } - } - - /** - * Set the given headers for the peer. - * @param {Object} headers - * @return {void} - */ - setHeaders(headers) { - ;['nethash', 'os', 'version'].forEach(key => { - this[key] = headers[key] - }) - } - - /** - * Get information to broadcast. - * @return {Object} - */ - toBroadcastInfo() { - const data = { - ip: this.ip, - port: +this.port, - nethash: this.nethash, - version: this.version, - os: this.os, - status: this.status, - height: this.state.height, - delay: this.delay, - } - - if (config.network.name !== 'mainnet') { - data.hashid = this.hashid || 'unknown' - } - - return data - } - - static isOk(peer) { - return peer.status === 200 || peer.status === 'OK' - } - - /** - * Perform POST request for a block. - * @param {Block} block - * @return {(Object|undefined)} - */ - async postBlock(block) { - return this.__post( - '/peer/blocks', - { block }, - { - headers: this.headers, - timeout: 5000, - }, - ) - } - - /** - * Perform POST request for a transactions. - * @param {Transaction[]} transactions - * @return {(Object|undefined)} - */ - async postTransactions(transactions) { - try { - const response = await this.__post( - '/peer/transactions', - { - transactions, - }, - { - headers: this.headers, - timeout: 8000, - }, - ) - - return response - } catch (err) { - throw err - } - } - - async getTransactionsFromIds(ids) { - // useless since there is a bug on v1 - const response = await this.__get( - `/peer/transactionsFromIds?ids=${ids.join(',')}`, - ) - - return response.success ? response.transactions : [] - } - - async getTransactionsFromBlock(blockId) { - const response = await this.__get(`/api/transactions?blockId=${blockId}`) - - return response.success ? response.transactions : [] - } - - /** - * Download blocks from peer. - * @param {Number} fromBlockHeight - * @return {(Object[]|undefined)} - */ - async downloadBlocks(fromBlockHeight) { - try { - const response = await axios.get(`${this.url}/peer/blocks`, { - params: { lastBlockHeight: fromBlockHeight }, - headers: this.headers, - timeout: 10000, - }) - - this.__parseHeaders(response) - - const { blocks } = response.data - const size = blocks.length - - if (size === 100 || size === 400) { - this.downloadSize = size - } - - return blocks - } catch (error) { - logger.debug( - `Cannot download blocks from peer ${this.url} - ${util.inspect(error, { - depth: 1, - })}`, - ) - - this.ban = - new Date().getTime() + (Math.floor(Math.random() * 40) + 20) * 60000 - - throw error - } - } - - /** - * Perform ping request on this peer if it has not been - * recently pinged. - * @param {Number} [delay=5000] - * @param {Boolean} force - * @return {Object} - * @throws {Error} If fail to get peer status. - */ - async ping(delay, force = false) { - if (this.recentlyPinged() && !force) { - return - } - - const body = await this.__get( - '/peer/status', - delay || config.peers.globalTimeout, - ) - - if (!body) { - throw new Error(`Peer ${this.ip} is unresponsive`) - } - - this.lastPinged = dayjs() - this.state = body - return body - } - - /** - * Returns true if this peer was pinged the past 2 minutes. - * @return {Boolean} - */ - recentlyPinged() { - return !!this.lastPinged && dayjs().diff(this.lastPinged, 'm') < 2 - } - - /** - * Refresh peer list. It removes blacklisted peers from the fetch - * @return {Object[]} - */ - async getPeers() { - logger.info(`Fetching a fresh peer list from ${this.url}`) - - await this.ping(2000) - - const body = await this.__get('/peer/list') - - return body.peers.filter(peer => !config.peers.blackList.includes(peer.ip)) - } - - /** - * Check if peer has common blocks. - * @param {[]String} ids - * @return {Boolean} - */ - async hasCommonBlocks(ids) { - try { - let url = `/peer/blocks/common?ids=${ids.join(',')}` - if (ids.length === 1) { - url += ',' - } - const body = await this.__get(url) - - return body && body.success && body.common - } catch (error) { - logger.error( - `Could not determine common blocks with ${this.ip}: ${error}`, - ) - } - - return false - } - - /** - * Perform GET request. - * @param {String} endpoint - * @param {Number} [timeout=10000] - * @return {(Object|undefined)} - */ - async __get(endpoint, timeout) { - const temp = new Date().getTime() - - try { - const response = await axios.get(`${this.url}${endpoint}`, { - headers: this.headers, - timeout: timeout || config.peers.globalTimeout, - }) - - this.delay = new Date().getTime() - temp - - this.__parseHeaders(response) - - return response.data - } catch (error) { - this.delay = -1 - - logger.debug( - `Request to ${this.url}${endpoint} failed because of "${ - error.message - }"`, - ) - - if (error.response) { - this.__parseHeaders(error.response) - } - } - } - - /** - * Perform POST request. - * @param {String} endpoint - * @param {Object} body - * @param {Object} headers - * @return {(Object|undefined)} - */ - async __post(endpoint, body, headers) { - try { - const response = await axios.post(`${this.url}${endpoint}`, body, headers) - - this.__parseHeaders(response) - - return response.data - } catch (error) { - logger.debug( - `Request to ${this.url}${endpoint} failed because of "${ - error.message - }"`, - ) - - if (error.response) { - this.__parseHeaders(error.response) - } - } - } - - /** - * Parse headers from response. - * @param {Object} response - * @return {Object} - */ - __parseHeaders(response) { - ;['nethash', 'os', 'version', 'hashid'].forEach(key => { - this[key] = response.headers[key] || this[key] - }) - - if (response.headers.height) { - this.state.height = +response.headers.height - } - - this.status = response.status - - return response - } -} diff --git a/packages/core-p2p/lib/server/index.js b/packages/core-p2p/lib/server/index.js deleted file mode 100755 index 57c031ae08..0000000000 --- a/packages/core-p2p/lib/server/index.js +++ /dev/null @@ -1,110 +0,0 @@ -const { - createServer, - mountServer, - plugins, -} = require('@arkecosystem/core-http-utils') - -/** - * Create a new hapi.js server. - * @param {Object} config - * @return {Hapi.Server} - */ -module.exports = async (p2p, config) => { - const server = await createServer({ - host: config.host, - port: config.port, - }) - - // TODO: enable after mainnet migration - // await server.register({ plugin: plugins.contentType }) - - await server.register({ - plugin: require('hapi-rate-limit'), - options: config.rateLimit, - }) - - await server.register({ - plugin: require('./plugins/validate-headers'), - }) - - await server.register({ - plugin: require('./plugins/accept-request'), - options: { - whitelist: config.whitelist, - }, - }) - - await server.register({ - plugin: require('./plugins/set-headers'), - }) - - await server.register({ - plugin: require('./plugins/blockchain-ready'), - options: { - routes: [ - '/peer/height', - '/peer/blocks/common', - '/peer/status', - '/peer/blocks', - '/peer/transactions', - '/peer/getTransactionsFromIds', - '/internal/round', - '/internal/blocks', - '/internal/forgingTransactions', - '/internal/networkState', - '/internal/syncCheck', - '/internal/usernames', - '/remote/blockchain/{event}', - ], - }, - }) - - await server.register({ - plugin: plugins.corsHeaders, - }) - - await server.register({ - plugin: plugins.transactionPayload, - options: { - routes: [ - { - method: 'POST', - path: '/peer/transactions', - }, - ], - }, - }) - - // await server.register({ - // plugin: require('./plugins/transaction-pool-ready'), - // options: { - // routes: [ - // '/peer/transactions' - // ] - // } - // }) - - await server.register({ - plugin: require('./versions/config'), - routes: { prefix: '/config' }, - }) - - await server.register({ - plugin: require('./versions/1'), - routes: { prefix: '/peer' }, - }) - - await server.register({ - plugin: require('./versions/internal'), - routes: { prefix: '/internal' }, - }) - - if (config.remoteInterface) { - await server.register({ - plugin: require('./versions/remote'), - routes: { prefix: '/remote' }, - }) - } - - return mountServer('P2P API', server) -} diff --git a/packages/core-p2p/lib/server/plugins/accept-request.js b/packages/core-p2p/lib/server/plugins/accept-request.js deleted file mode 100644 index d766755687..0000000000 --- a/packages/core-p2p/lib/server/plugins/accept-request.js +++ /dev/null @@ -1,68 +0,0 @@ -const Boom = require('boom') -const isWhitelisted = require('../../utils/is-whitelist') -const monitor = require('../../monitor') - -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - const requiredHeaders = ['nethash', 'version', 'port', 'os'] - - server.ext({ - type: 'onRequest', - async method(request, h) { - const remoteAddress = request.info.remoteAddress - - if (request.path.startsWith('/config')) { - return h.continue - } - - if ( - request.headers['x-auth'] === 'forger' || - request.path.startsWith('/remote') - ) { - return isWhitelisted(options.whitelist, remoteAddress) - ? h.continue - : Boom.forbidden() - } - - // Only forger requests are internal - if (request.path.startsWith('/internal')) { - return Boom.forbidden() - } - - if (!monitor.guard) { - return Boom.serverUnavailable('Peer Monitor not ready') - } - - if (request.path.startsWith('/peer')) { - const peer = { ip: remoteAddress } - - requiredHeaders.forEach(key => { - peer[key] = request.headers[key] - }) - - try { - await monitor.acceptNewPeer(peer) - } catch (error) { - return Boom.badImplementation(error.message) - } - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'accept-request', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/plugins/blockchain-ready.js b/packages/core-p2p/lib/server/plugins/blockchain-ready.js deleted file mode 100644 index d4b07d2014..0000000000 --- a/packages/core-p2p/lib/server/plugins/blockchain-ready.js +++ /dev/null @@ -1,35 +0,0 @@ -const Boom = require('boom') -const app = require('@arkecosystem/core-container') - -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.ext({ - type: 'onRequest', - async method(request, h) { - if (!options.routes.includes(request.path)) { - return h.continue - } - - if (!app.resolvePlugin('blockchain')) { - return Boom.serverUnavailable('Blockchain not ready') - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'blockchain-ready', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/plugins/set-headers.js b/packages/core-p2p/lib/server/plugins/set-headers.js deleted file mode 100644 index c1645d193d..0000000000 --- a/packages/core-p2p/lib/server/plugins/set-headers.js +++ /dev/null @@ -1,67 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') - -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - const headers = { - nethash: config.network.nethash, - version: app.getVersion(), - port: app.resolveOptions('p2p').port, - os: require('os').platform(), - height: null, - } - - const requiredHeaders = ['nethash', 'version', 'port', 'os', 'height'] - - if (config.network.name !== 'mainnet') { - headers.hashid = app.getHashid() - requiredHeaders.push('hashid') - } - - server.ext({ - type: 'onPreResponse', - async method(request, h) { - const blockchain = app.resolvePlugin('blockchain') - if (blockchain) { - const lastBlock = blockchain.getLastBlock() - if (lastBlock) { - headers.height = lastBlock.data.height - } - } - - const response = request.response - if (response.isBoom) { - if (response.data) { - // Deleting the property beforehand makes it appear last in the - // response body. - delete response.output.payload.error - response.output.payload.error = response.data - } - - requiredHeaders.forEach(key => { - response.output.headers[key] = headers[key] - }) - } else { - requiredHeaders.forEach(key => response.header(key, headers[key])) - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'set-headers', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/plugins/transaction-pool-ready.js b/packages/core-p2p/lib/server/plugins/transaction-pool-ready.js deleted file mode 100644 index 120c03507d..0000000000 --- a/packages/core-p2p/lib/server/plugins/transaction-pool-ready.js +++ /dev/null @@ -1,35 +0,0 @@ -const Boom = require('boom') -const app = require('@arkecosystem/core-container') - -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.ext({ - type: 'onRequest', - async method(request, h) { - if (!options.routes.includes(request.path)) { - return h.continue - } - - if (!app.resolvePlugin('transactionPool')) { - return Boom.serverUnavailable('Transaction Pool not ready') - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'transaction-pool-ready', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/plugins/validate-headers.js b/packages/core-p2p/lib/server/plugins/validate-headers.js deleted file mode 100644 index 6a71dce04e..0000000000 --- a/packages/core-p2p/lib/server/plugins/validate-headers.js +++ /dev/null @@ -1,84 +0,0 @@ -const ip = require('ip') -const AJV = require('ajv') - -/** - * The register method used by hapi.js. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - const ajv = new AJV() - - ajv.addFormat('ip', { - type: 'string', - validate: value => ip.isV4Format(value) || ip.isV6Format(value), - }) - - server.ext({ - type: 'onRequest', - async method(request, h) { - if (request.path.startsWith('/config')) { - return h.continue - } - - if (request.headers.port) { - request.headers.port = +request.headers.port - } - - const errors = ajv.validate( - { - type: 'object', - properties: { - ip: { - type: 'string', - format: 'ip', - }, - port: { - type: 'integer', - minimum: 1, - maximum: 65535, - }, - os: { - type: 'string', - maxLength: 64, - }, - nethash: { - type: 'string', - maxLength: 64, - }, - version: { - type: 'string', - maxLength: 11, - }, - }, - required: ['version', 'nethash', 'port'], - }, - request.headers, - ) - ? null - : ajv.errors - - if (errors) { - return h - .response({ - error: errors[0].message, - success: false, - }) - .takeover() - } - - return h.continue - }, - }) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'validate-headers', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/versions/1/handlers.js b/packages/core-p2p/lib/server/versions/1/handlers.js deleted file mode 100644 index a49f07e3fb..0000000000 --- a/packages/core-p2p/lib/server/versions/1/handlers.js +++ /dev/null @@ -1,379 +0,0 @@ -/* eslint no-restricted-globals: "off" */ - -const app = require('@arkecosystem/core-container') -const { TransactionGuard } = require('@arkecosystem/core-transaction-pool') -const { slots, crypto } = require('@arkecosystem/crypto') -const { Block, Transaction } = require('@arkecosystem/crypto').models -const Joi = require('@arkecosystem/crypto').validator.engine.joi - -const pluralize = require('pluralize') - -const transactionPool = app.resolvePlugin('transactionPool') -const config = app.resolvePlugin('config') -const logger = app.resolvePlugin('logger') - -const monitor = require('../../../monitor') - -/** - * @type {Object} - */ -exports.getPeers = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - try { - const peers = monitor - .getPeers() - .map(peer => peer.toBroadcastInfo()) - .sort((a, b) => a.delay - b.delay) - - return { - success: true, - peers, - } - } catch (error) { - return h - .response({ success: false, message: error.message }) - .code(500) - .takeover() - } - }, -} - -/** - * @type {Object} - */ -exports.getHeight = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = app.resolvePlugin('blockchain').getLastBlock() - - return { - success: true, - height: lastBlock.data.height, - id: lastBlock.data.id, - } - }, -} - -/** - * @type {Object} - */ -exports.getCommonBlocks = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - if (!request.query.ids) { - return { - success: false, - } - } - - const blockchain = app.resolvePlugin('blockchain') - - const ids = request.query.ids - .split(',') - .slice(0, 9) - .filter(id => id.match(/^\d+$/)) - - try { - const commonBlocks = await blockchain.database.getCommonBlocks(ids) - - return { - success: true, - common: commonBlocks.length ? commonBlocks[0] : null, - lastBlockHeight: blockchain.getLastBlock().data.height, - } - } catch (error) { - return h - .response({ success: false, message: error.message }) - .code(500) - .takeover() - } - }, -} - -/** - * @type {Object} - */ -exports.getTransactionsFromIds = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - try { - const blockchain = app.resolvePlugin('blockchain') - const maxTransactions = config.getConstants(blockchain.getLastHeight()) - .block.maxTransactions - - const transactionIds = request.query.ids - .split(',') - .slice(0, maxTransactions) - .filter(id => id.match('[0-9a-fA-F]{32}')) - - const rows = await app - .resolvePlugin('database') - .getTransactionsFromIds(transactionIds) - - // TODO: v1 compatibility patch. Add transformer and refactor later on - const transactions = await rows.map(row => { - const transaction = Transaction.deserialize( - row.serialized.toString('hex'), - ) - transaction.blockId = row.block_id - transaction.senderId = crypto.getAddress(transaction.senderPublicKey) - return transaction - }) - - transactionIds.forEach((transaction, i) => { - transactionIds[i] = transactions.find( - tx2 => tx2.id === transactionIds[i], - ) - }) - - return { success: true, transactions: transactionIds } - } catch (error) { - return h - .response({ success: false, message: error.message }) - .code(500) - .takeover() - } - }, -} - -/** - * @type {Object} - */ -exports.getTransactions = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - return { success: true, transactions: [] } - }, -} - -/** - * @type {Object} - */ -exports.getStatus = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const lastBlock = app.resolvePlugin('blockchain').getLastBlock() - - return { - success: true, - height: lastBlock ? lastBlock.data.height : 0, - forgingAllowed: slots.isForgingAllowed(), - currentSlot: slots.getSlotNumber(), - header: lastBlock ? lastBlock.getHeader() : {}, - } - }, -} - -/** - * @type {Object} - */ -exports.postBlock = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const blockchain = app.resolvePlugin('blockchain') - - try { - if (!request.payload || !request.payload.block) { - return { success: false } - } - - const block = request.payload.block - - if (blockchain.pingBlock(block)) return { success: true } - // already got it? - const lastDownloadedBlock = blockchain.getLastDownloadedBlock() - - // Are we ready to get it? - if ( - lastDownloadedBlock && - lastDownloadedBlock.data.height + 1 !== block.height - ) { - return { success: true } - } - - const b = new Block(block) - - if (!b.verification.verified) { - return { success: false } - } - - blockchain.pushPingBlock(b.data) - - if (b.headerOnly) { - // let missingIds = [] - let transactions = [] - // if (transactionPool) { - // transactions = block.transactionIds - // .map(async id => await transactionPool.getTransaction(id) || id) - // missingIds = transactions.filter(tx => !tx.id) - // } else { - // missingIds = block.transactionIds.slice(0) - // } - // if (missingIds.length > 0) { - let peer = await monitor.getPeer(request.info.remoteAddress) - // only for test because it can be used for DDOS attack - if (!peer && process.env.NODE_ENV === 'test_p2p') { - peer = await monitor.getRandomPeer() - } - - if (!peer) { - return { success: false } - } - - transactions = await peer.getTransactionsFromIds(block.transactionIds) - // issue on v1, using /api/ instead of /peer/ - if (transactions.length < block.transactionIds.length) { - transactions = await peer.getTransactionsFromBlock(block.id) - } - - // reorder them correctly - block.transactions = block.transactionIds.map(id => - transactions.find(tx => tx.id === id), - ) - logger.debug( - `Found missing transactions: ${block.transactions.map(tx => tx.id)}`, - ) - - if (block.transactions.length !== block.numberOfTransactions) { - return { success: false } - } - } - // } else return { success: false } - - block.ip = request.info.remoteAddress - blockchain.queueBlock(block) - - return { success: true } - } catch (error) { - logger.error(error) - return { success: false } - } - }, -} - -/** - * @type {Object} - */ -exports.postTransactions = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - if (!transactionPool) { - return { - success: false, - message: 'Transaction pool not available', - } - } - - const guard = new TransactionGuard(transactionPool) - - const result = await guard.validate(request.payload.transactions) - - if (result.invalid.length > 0) { - return { - success: false, - message: 'Transactions list is not conform', - error: 'Transactions list is not conform', - } - } - - if (result.broadcast.length > 0) { - app - .resolvePlugin('p2p') - .broadcastTransactions(guard.getBroadcastTransactions()) - } - - return { - success: true, - transactionIds: result.accept, - } - }, - options: { - cors: { - additionalHeaders: ['nethash', 'port', 'version'], - }, - validate: { - payload: { - transactions: Joi.arkTransactions() - .min(1) - .max(app.resolveOptions('transactionPool').maxTransactionsPerRequest) - .options({ stripUnknown: true }), - }, - }, - }, -} - -/** - * @type {Object} - */ -exports.getBlocks = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - try { - const database = app.resolvePlugin('database') - const blockchain = app.resolvePlugin('blockchain') - - const reqBlockHeight = parseInt(request.query.lastBlockHeight) + 1 - let blocks = [] - - if (!request.query.lastBlockHeight || isNaN(reqBlockHeight)) { - blocks.push(blockchain.getLastBlock()) - } else { - blocks = await database.getBlocks(reqBlockHeight, 400) - } - - logger.info( - `${request.info.remoteAddress} has downloaded ${pluralize( - 'block', - blocks.length, - true, - )} from height ${(!isNaN(reqBlockHeight) - ? reqBlockHeight - : blocks[0].data.height - ).toLocaleString()}`, - ) - - return { success: true, blocks: blocks || [] } - } catch (error) { - logger.error(error.stack) - - return h.response({ success: false, error }).code(500) - } - }, -} diff --git a/packages/core-p2p/lib/server/versions/1/index.js b/packages/core-p2p/lib/server/versions/1/index.js deleted file mode 100644 index fba71d7016..0000000000 --- a/packages/core-p2p/lib/server/versions/1/index.js +++ /dev/null @@ -1,35 +0,0 @@ -const handlers = require('./handlers') - -/** - * Register v1 routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.route([ - { method: 'GET', path: '/list', ...handlers.getPeers }, - { method: 'GET', path: '/blocks', ...handlers.getBlocks }, - { - method: 'GET', - path: '/transactionsFromIds', - ...handlers.getTransactionsFromIds, - }, - { method: 'GET', path: '/height', ...handlers.getHeight }, - { method: 'GET', path: '/transactions', ...handlers.getTransactions }, - { method: 'GET', path: '/blocks/common', ...handlers.getCommonBlocks }, - { method: 'GET', path: '/status', ...handlers.getStatus }, - { method: 'POST', path: '/blocks', ...handlers.postBlock }, - { method: 'POST', path: '/transactions', ...handlers.postTransactions }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark P2P API - v1', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/versions/1/schema.js b/packages/core-p2p/lib/server/versions/1/schema.js deleted file mode 100644 index c209cd37d3..0000000000 --- a/packages/core-p2p/lib/server/versions/1/schema.js +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @type {Object} - */ -module.exports = { - getStatus: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - height: { - type: 'integer', - minimum: 0, - }, - currentSlot: { - type: 'integer', - minimum: 0, - }, - forgingAllowed: { - type: 'boolean', - }, - header: { - type: 'object', - }, - }, - required: ['success', 'height', 'header', 'currentSlot', 'forgingAllowed'], - }, - getHeight: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - height: { - type: 'integer', - minimum: 0, - }, - header: { - type: 'object', - }, - }, - required: ['success', 'height', 'header'], - }, - postTransactions: { - type: 'object', - }, - getTransactions: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - transactions: { - type: 'array', - uniqueItems: true, - }, - }, - required: ['transactions'], - }, - getTransactionsFromIds: { - type: 'object', - }, - getBlocks: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - blocks: { - type: 'array', - }, - }, - required: ['blocks'], - }, - postBlock: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - blockId: { - type: 'string', - }, - }, - required: ['success', 'blockId'], - }, - getBlock: { - type: 'object', - }, - getCommonBlocks: { - type: 'object', - }, - getPeers: { - type: 'object', - properties: { - success: { - type: 'boolean', - }, - peers: { - type: 'array', - }, - }, - required: ['peers'], - }, -} diff --git a/packages/core-p2p/lib/server/versions/config/handlers/index.js b/packages/core-p2p/lib/server/versions/config/handlers/index.js deleted file mode 100644 index d3b615198c..0000000000 --- a/packages/core-p2p/lib/server/versions/config/handlers/index.js +++ /dev/null @@ -1,61 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const transform = require('../transformers/plugins') - -exports.config = { - async handler(request, h) { - return { - data: { - version: app.getVersion(), - network: { - version: config.network.pubKeyHash, - nethash: config.network.nethash, - explorer: config.network.client.explorer, - token: { - name: config.network.client.token, - symbol: config.network.client.symbol, - }, - }, - plugins: transform(config), - }, - } - }, - config: { - cors: true, - }, -} - -exports.network = { - handler(request, h) { - return { - data: require(`${process.env.ARK_PATH_CONFIG}/network.json`), - } - }, -} - -exports.genesisBlock = { - handler(request, h) { - return { - data: require(`${process.env.ARK_PATH_CONFIG}/genesisBlock.json`), - } - }, -} - -exports.peers = { - handler(request, h) { - return { - data: require(`${process.env.ARK_PATH_CONFIG}/peers.json`), - } - }, -} - -exports.delegates = { - handler(request, h) { - const data = require(`${process.env.ARK_PATH_CONFIG}/delegates.json`) - data.secrets = [] - delete data.bip38 - - return { data } - }, -} diff --git a/packages/core-p2p/lib/server/versions/config/index.js b/packages/core-p2p/lib/server/versions/config/index.js deleted file mode 100644 index 367713700d..0000000000 --- a/packages/core-p2p/lib/server/versions/config/index.js +++ /dev/null @@ -1,26 +0,0 @@ -const handlers = require('./handlers') -/** - * Register v1 routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.route([ - { method: 'GET', path: '/', ...handlers.config }, - { method: 'GET', path: '/network', ...handlers.network }, - { method: 'GET', path: '/genesis-block', ...handlers.genesisBlock }, - { method: 'GET', path: '/peers', ...handlers.peers }, - { method: 'GET', path: '/delegates', ...handlers.delegates }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark P2P - Config API', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/versions/config/transformers/plugins.js b/packages/core-p2p/lib/server/versions/config/transformers/plugins.js deleted file mode 100644 index e5d7fe93f2..0000000000 --- a/packages/core-p2p/lib/server/versions/config/transformers/plugins.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Turns a "config" object into readable object. - * @param {Object} model - * @return {Object} - */ -module.exports = config => { - const allowed = [ - '@arkecosystem/core-api', - '@arkecosystem/core-graphql', - '@arkecosystem/core-json-rpc', - '@arkecosystem/core-webhooks', - ] - - const result = {} - - for (const [name, options] of Object.entries(config.plugins)) { - if (allowed.includes(name)) { - if (options.server) { - result[name] = { - enabled: !!options.server.enabled, - port: +options.server.port, - } - - continue - } - - result[name] = { - enabled: !!options.enabled, - port: +options.port, - } - } - } - - return result -} diff --git a/packages/core-p2p/lib/server/versions/internal/handlers/blockchain.js b/packages/core-p2p/lib/server/versions/internal/handlers/blockchain.js deleted file mode 100644 index 0a51720ea0..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/handlers/blockchain.js +++ /dev/null @@ -1,21 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') - -/** - * @type {Object} - */ -exports.sync = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - logger.debug('Blockchain sync check WAKEUP requested by forger :bed:') - - app.resolvePlugin('blockchain').forceWakeup() - - return h.response(null).code(204) - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/handlers/blocks.js b/packages/core-p2p/lib/server/versions/internal/handlers/blocks.js deleted file mode 100644 index e99e97a784..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/handlers/blocks.js +++ /dev/null @@ -1,23 +0,0 @@ -const app = require('@arkecosystem/core-container') -const schema = require('../schemas/blocks') - -/** - * @type {Object} - */ -exports.store = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler: (request, h) => { - request.payload.block.ip = request.info.remoteAddress - - app.resolvePlugin('blockchain').queueBlock(request.payload.block) - - return h.response(null).code(204) - }, - options: { - validate: schema.store, - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/handlers/network.js b/packages/core-p2p/lib/server/versions/internal/handlers/network.js deleted file mode 100644 index eb7ffbf7e7..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/handlers/network.js +++ /dev/null @@ -1,17 +0,0 @@ -const monitor = require('../../../../monitor') - -/** - * @type {Object} - */ -exports.state = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - return { - data: await monitor.getNetworkState(), - } - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/handlers/rounds.js b/packages/core-p2p/lib/server/versions/internal/handlers/rounds.js deleted file mode 100644 index 88a2f8b064..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/handlers/rounds.js +++ /dev/null @@ -1,45 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') - -const { slots } = require('@arkecosystem/crypto') - -/** - * @type {Object} - */ -exports.current = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const database = app.resolvePlugin('database') - const blockchain = app.resolvePlugin('blockchain') - - const lastBlock = blockchain.getLastBlock() - - const height = lastBlock.data.height + 1 - const maxActive = config.getConstants(height).activeDelegates - const blockTime = config.getConstants(height).blocktime - const reward = config.getConstants(height).reward - const delegates = await database.getActiveDelegates(height) - const timestamp = slots.getTime() - - return { - data: { - current: parseInt(height / maxActive), - reward, - timestamp, - delegates, - currentForger: delegates[parseInt(timestamp / blockTime) % maxActive], - nextForger: - delegates[(parseInt(timestamp / blockTime) + 1) % maxActive], - lastBlock: lastBlock.data, - canForge: - parseInt(1 + lastBlock.data.timestamp / blockTime) * blockTime - < timestamp - 1, - }, - } - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/handlers/transactions.js b/packages/core-p2p/lib/server/versions/internal/handlers/transactions.js deleted file mode 100644 index 914e44f6db..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/handlers/transactions.js +++ /dev/null @@ -1,55 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') - -const { Transaction } = require('@arkecosystem/crypto').models - -const schema = require('../schemas/transactions') - -/** - * @type {Object} - */ -exports.verify = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const transaction = new Transaction( - Transaction.deserialize(request.payload.transaction), - ) - - return { - data: { - valid: await app - .resolvePlugin('database') - .verifyTransaction(transaction), - }, - } - }, - options: { - validate: schema.verify, - }, -} - -/** - * @type {Object} - */ -exports.forging = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler(request, h) { - const blockchain = app.resolvePlugin('blockchain') - - const height = blockchain.getLastBlock().data.height - const maxTransactions = config.getConstants(height).block.maxTransactions - - return { - data: blockchain.getUnconfirmedTransactions(maxTransactions, true), - } - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/handlers/utils.js b/packages/core-p2p/lib/server/versions/internal/handlers/utils.js deleted file mode 100644 index 6dd2f91854..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/handlers/utils.js +++ /dev/null @@ -1,54 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const emitter = app.resolvePlugin('event-emitter') - -const schema = require('../schemas/utils') - -/** - * @type {Object} - */ -exports.usernames = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const blockchain = app.resolvePlugin('blockchain') - const walletManager = app.resolvePlugin('database').walletManager - - const lastBlock = blockchain.getLastBlock() - const delegates = await blockchain.database.getActiveDelegates( - lastBlock ? lastBlock.data.height + 1 : 1, - ) - - const data = {} - for (const delegate of delegates) { - data[delegate.publicKey] = walletManager.findByPublicKey( - delegate.publicKey, - ).username - } - - return { data } - }, -} - -/** - * Emit the given event and payload to the local host. - * @type {Object} - */ -exports.emitEvent = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler: (request, h) => { - emitter.emit(request.payload.event, request.payload.body) - - return h.response(null).code(204) - }, - options: { - validate: schema.emitEvent, - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/index.js b/packages/core-p2p/lib/server/versions/internal/index.js deleted file mode 100644 index c0539958ca..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/index.js +++ /dev/null @@ -1,40 +0,0 @@ -const blockchain = require('./handlers/blockchain') -const blocks = require('./handlers/blocks') -const network = require('./handlers/network') -const rounds = require('./handlers/rounds') -const transactions = require('./handlers/transactions') -const utils = require('./handlers/utils') - -/** - * Register internal routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.route([ - { method: 'GET', path: '/network/state', ...network.state }, - - { method: 'GET', path: '/blockchain/sync', ...blockchain.sync }, - - { method: 'POST', path: '/blocks', ...blocks.store }, - - { method: 'GET', path: '/rounds/current', ...rounds.current }, - - { method: 'POST', path: '/transactions/verify', ...transactions.verify }, - { method: 'GET', path: '/transactions/forging', ...transactions.forging }, - - { method: 'GET', path: '/utils/usernames', ...utils.usernames }, - { method: 'POST', path: '/utils/events', ...utils.emitEvent }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark P2P API - Internal', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/versions/internal/schemas/blocks.js b/packages/core-p2p/lib/server/versions/internal/schemas/blocks.js deleted file mode 100644 index b6c56ac379..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/schemas/blocks.js +++ /dev/null @@ -1,10 +0,0 @@ -const Joi = require('@arkecosystem/crypto').validator.engine.joi - -/** - * @type {Object} - */ -exports.store = { - payload: { - block: Joi.arkBlock().options({ stripUnknown: true }), - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/schemas/transactions.js b/packages/core-p2p/lib/server/versions/internal/schemas/transactions.js deleted file mode 100644 index 4f638ae050..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/schemas/transactions.js +++ /dev/null @@ -1,10 +0,0 @@ -const Joi = require('joi') - -/** - * @type {Object} - */ -exports.verify = { - payload: { - transaction: Joi.string().hex(), - }, -} diff --git a/packages/core-p2p/lib/server/versions/internal/schemas/utils.js b/packages/core-p2p/lib/server/versions/internal/schemas/utils.js deleted file mode 100644 index 6ea9d91398..0000000000 --- a/packages/core-p2p/lib/server/versions/internal/schemas/utils.js +++ /dev/null @@ -1,11 +0,0 @@ -const Joi = require('joi') - -/** - * @type {Object} - */ -exports.emitEvent = { - payload: { - event: Joi.string(), - body: Joi.any(), - }, -} diff --git a/packages/core-p2p/lib/server/versions/remote/handlers/blockchain.js b/packages/core-p2p/lib/server/versions/remote/handlers/blockchain.js deleted file mode 100644 index 30a2e5eb93..0000000000 --- a/packages/core-p2p/lib/server/versions/remote/handlers/blockchain.js +++ /dev/null @@ -1,26 +0,0 @@ -const app = require('@arkecosystem/core-container') -const schema = require('../schemas/blockchain') - -/** - * Respond with a blockchain event. - * @type {Object} - */ -exports.emitEvent = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - handler: (request, h) => { - const event = app.resolvePlugin('blockchain').events[ - request.params.event - ] - - request.query.param ? event(request.query.params) : event() - - return h.response(null).code(204) - }, - options: { - validate: schema.emitEvent, - }, -} diff --git a/packages/core-p2p/lib/server/versions/remote/index.js b/packages/core-p2p/lib/server/versions/remote/index.js deleted file mode 100644 index b81de5091a..0000000000 --- a/packages/core-p2p/lib/server/versions/remote/index.js +++ /dev/null @@ -1,23 +0,0 @@ -const blockchain = require('./handlers/blockchain') - -/** - * Register remote routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - server.route([ - { method: 'GET', path: '/blockchain/{event}', ...blockchain.emitEvent }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark P2P - Remote API', - version: '0.1.0', - register, -} diff --git a/packages/core-p2p/lib/server/versions/remote/schemas/blockchain.js b/packages/core-p2p/lib/server/versions/remote/schemas/blockchain.js deleted file mode 100644 index ffada1bce9..0000000000 --- a/packages/core-p2p/lib/server/versions/remote/schemas/blockchain.js +++ /dev/null @@ -1,10 +0,0 @@ -const Joi = require('joi') - -/** - * @type {Object} - */ -exports.emitEvent = { - params: { - event: Joi.string(), - }, -} diff --git a/packages/core-p2p/lib/utils/check-dns.js b/packages/core-p2p/lib/utils/check-dns.js deleted file mode 100644 index 9f1b7b3641..0000000000 --- a/packages/core-p2p/lib/utils/check-dns.js +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const util = require('util') -const dns = require('dns') -const shuffle = require('lodash/shuffle') -const logger = require('@arkecosystem/core-container').resolvePlugin('logger') - -module.exports = async hosts => { - hosts = shuffle(hosts) - - const lookupService = util.promisify(dns.lookupService) - - for (let i = hosts.length - 1; i >= 0; i--) { - try { - await lookupService(hosts[i], 53) - - return Promise.resolve(hosts[i]) - } catch (err) { - logger.error(err.message) - } - } - - return Promise.reject( - new Error( - "Please check your network connectivity, couldn't connect to any host.", - ), - ) -} diff --git a/packages/core-p2p/lib/utils/check-ntp.js b/packages/core-p2p/lib/utils/check-ntp.js deleted file mode 100644 index 17363969ff..0000000000 --- a/packages/core-p2p/lib/utils/check-ntp.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const Sntp = require('sntp') -const shuffle = require('lodash/shuffle') -const logger = require('@arkecosystem/core-container').resolvePlugin('logger') - -/** - * Check if it is possible to connect to any NTP host. - * @param {Array} hosts - * @param {Number} [timeout = 1000] - * @return {Promise} - */ -module.exports = (hosts, timeout = 1000) => { - hosts = shuffle(hosts) - - return new Promise(async (resolve, reject) => { - for (const host of hosts) { - try { - const time = await Sntp.time({ host, timeout }) - - return time.errno - ? logger.error(`Host ${host} responsed with: ${time.message}`) - : resolve({ time, host }) - } catch (err) { - logger.error(`Host ${host} responsed with: ${err.message}`) - } - } - - reject( - new Error( - "Please check your NTP connectivity, couldn't connect to any host.", - ), - ) - }) -} diff --git a/packages/core-p2p/lib/utils/is-myself.js b/packages/core-p2p/lib/utils/is-myself.js deleted file mode 100644 index 45ca858fa4..0000000000 --- a/packages/core-p2p/lib/utils/is-myself.js +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint max-len: "off" */ - -const os = require('os') -const ip = require("ipaddr.js") - -/** - * Checks if IP belongs to local computer (all network interfaces are checked) - * @param {String} ipAddress to check - * @returns {Boolean} true/false - */ -module.exports = ipAddress => { - const interfaces = os.networkInterfaces() - - try { - ipAddress = ip.parse(ipAddress).toString(); - } catch (error) { - return true; - } - - return ( - ipAddress.startsWith('127.') || - ipAddress.startsWith('0.') || - Object.keys(interfaces).some(ifname => - interfaces[ifname].some(iface => iface.address === ipAddress), - ) - ) -} diff --git a/packages/core-p2p/lib/utils/is-whitelist.js b/packages/core-p2p/lib/utils/is-whitelist.js deleted file mode 100644 index c4a760d4e0..0000000000 --- a/packages/core-p2p/lib/utils/is-whitelist.js +++ /dev/null @@ -1,19 +0,0 @@ -const mm = require('micromatch') - -/** - * Check if the given IP address is whitelisted. - * @param {[]String} value - * @param {String} value - * @return {boolean} - */ -module.exports = (whitelist, value) => { - if (Array.isArray(whitelist)) { - for (let i = 0; i < whitelist.length; i++) { - if (mm.isMatch(value, whitelist[i])) { - return true - } - } - } - - return false -} diff --git a/packages/core-p2p/lib/utils/network-state.js b/packages/core-p2p/lib/utils/network-state.js deleted file mode 100644 index 1f3b65d62d..0000000000 --- a/packages/core-p2p/lib/utils/network-state.js +++ /dev/null @@ -1,73 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') - -const { slots } = require('@arkecosystem/crypto') - -/** - * Returns current network state. Peers are update before the call - * @param {Monitor} monitor - * @private {Block} lastBlock - * @returns {Object} JSON response for the forger to assess if allowed to forge or not - */ -module.exports = (monitor, lastBlock) => { - const createStateObject = ( - quorum, - minimumNetworkReach, - coldStart, - overHeightBlockHeader, - ) => ({ - quorum, - nodeHeight: lastBlock.data.height, - lastBlockId: lastBlock.data.id, - overHeightBlockHeader, - minimumNetworkReach, - coldStart, - }) - - const peers = monitor.getPeers() - const minimumNetworkReach = config.peers.minimumNetworkReach || 20 - const currentSlot = slots.getSlotNumber() - - let quorum = 0 - let noQuorum = 0 - let overHeightQuorum = 0 - let overHeightBlockHeader = null - - if (monitor.__isColdStartActive()) { - return createStateObject(0, true, true, overHeightBlockHeader) - } - - if (process.env.ARK_ENV === 'test') { - return createStateObject(1, true, false, overHeightBlockHeader) - } - - if (peers.length < minimumNetworkReach) { - return createStateObject(0, false, false, overHeightBlockHeader) - } - - for (const peer of peers) { - if (peer.state.height === lastBlock.data.height) { - if ( - peer.state.header.id === lastBlock.data.id - && peer.state.currentSlot === currentSlot - && peer.state.forgingAllowed - ) { - quorum += 1 - } else { - noQuorum += 1 - } - } else if (peer.state.height > lastBlock.data.height) { - noQuorum += 1 - overHeightQuorum += 1 - overHeightBlockHeader = peer.state.header - } else if (lastBlock.data.height - peer.state.height < 3) { - // suppose the max network elasticity accross 3 blocks - noQuorum += 1 - } - } - - const calculatedQuorum = quorum / (quorum + noQuorum) - - return createStateObject(calculatedQuorum, true, false, overHeightBlockHeader) -} diff --git a/packages/core-p2p/package.json b/packages/core-p2p/package.json index de4975a019..50e588ac07 100644 --- a/packages/core-p2p/package.json +++ b/packages/core-p2p/package.json @@ -1,58 +1,93 @@ { - "name": "@arkecosystem/core-p2p", - "description": "P2P API for Ark Core", - "version": "0.2.11", - "contributors": [ - "François-Xavier Thoorens ", - "Kristjan Košič ", - "Brian Faust ", - "Alex Barnsley " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "@arkecosystem/core-transaction-pool": "~0.2", - "@arkecosystem/crypto": "~0.2", - "ajv": "^6.5.5", - "axios": "^0.18.0", - "boom": "^7.3.0", - "dayjs-ext": "^2.2.0", - "delay": "^4.1.0", - "hapi-rate-limit": "^3.0.0", - "ip": "^1.1.5", - "joi": "^14.3.0", - "lodash.chunk": "^4.2.0", - "lodash.flatten": "^4.4.0", - "lodash.groupby": "^4.6.0", - "lodash.head": "^4.0.1", - "lodash.sample": "^4.2.1", - "lodash.shuffle": "^4.2.0", - "lodash.sumby": "^4.6.0", - "lodash.take": "^4.1.1", - "micromatch": "^3.1.10", - "pluralize": "^7.0.0", - "pretty-ms": "^4.0.0", - "semver": "^5.6.0", - "sntp": "^3.0.2" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2", - "axios-mock-adapter": "^1.15.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-p2p", + "description": "P2P API for Ark Core", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Kristjan Košič ", + "Brian Faust ", + "Alex Barnsley " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@arkecosystem/core-transaction-pool": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/joi": "^14.0.1", + "@types/lodash.chunk": "^4.2.4", + "@types/lodash.flatten": "^4.4.4", + "@types/lodash.get": "^4.4.4", + "@types/lodash.groupby": "^4.6.4", + "@types/lodash.head": "^4.0.4", + "@types/lodash.sample": "^4.2.4", + "@types/lodash.set": "^4.3.4", + "@types/lodash.shuffle": "^4.2.4", + "@types/lodash.sumby": "^4.6.4", + "@types/lodash.take": "^4.1.4", + "@types/micromatch": "^3.1.0", + "@types/pluralize": "^0.0.29", + "@types/semver": "^5.5.0", + "ajv": "^6.6.2", + "axios": "^0.18.0", + "boom": "^7.3.0", + "dayjs-ext": "^2.2.0", + "delay": "^4.1.0", + "hapi-rate-limit": "^3.0.0", + "ip": "^1.1.5", + "joi": "^14.3.0", + "lodash.chunk": "^4.2.0", + "lodash.flatten": "^4.4.0", + "lodash.get": "^4.4.2", + "lodash.groupby": "^4.6.0", + "lodash.head": "^4.0.1", + "lodash.sample": "^4.2.1", + "lodash.set": "^4.3.2", + "lodash.shuffle": "^4.2.0", + "lodash.sumby": "^4.6.0", + "lodash.take": "^4.1.1", + "micromatch": "^3.1.10", + "pluralize": "^7.0.0", + "pretty-ms": "^4.0.0", + "semver": "^5.6.0", + "sntp": "^3.0.2" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0", + "@types/boom": "^7.2.1", + "@types/ip": "^1.1.0", + "@types/pretty-ms": "^4.0.0", + "axios-mock-adapter": "^1.15.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-p2p/src/config.ts b/packages/core-p2p/src/config.ts new file mode 100644 index 0000000000..3a4c096ad0 --- /dev/null +++ b/packages/core-p2p/src/config.ts @@ -0,0 +1,3 @@ +import { Shared } from "@arkecosystem/core-interfaces"; + +export const config = new Shared.Config(); diff --git a/packages/core-p2p/src/court/guard.ts b/packages/core-p2p/src/court/guard.ts new file mode 100644 index 0000000000..c0de92cd52 --- /dev/null +++ b/packages/core-p2p/src/court/guard.ts @@ -0,0 +1,330 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import dayjs from "dayjs-ext"; +import head from "lodash/head"; +import sumBy from "lodash/sumBy"; +import prettyMs from "pretty-ms"; +import semver from "semver"; + +import { config as localConfig } from "../config"; +import * as utils from "../utils"; +import { offences } from "./offences"; + +const config = app.getConfig(); +const logger = app.resolvePlugin("logger"); + +export interface ISuspension { + peer: any; + reason: string; + until: dayjs.Dayjs; + nextSuspensionReminder?: dayjs.Dayjs; +} + +export class Guard { + public readonly suspensions: { [ip: string]: ISuspension }; + public config: any; + private monitor: any; + + /** + * Create a new guard instance. + */ + constructor() { + this.suspensions = {}; + this.config = localConfig; + } + + /** + * Initialise a new guard. + * @param {IMonitor} monitor + */ + public init(monitor) { + this.monitor = monitor; + + return this; + } + + /** + * Get a list of all suspended peers. + * @return {Object} + */ + public all() { + return this.suspensions; + } + + /** + * Get the suspended peer for the give IP. + * @return {Object} + */ + public get(ip) { + return this.suspensions[ip]; + } + + /** + * Suspends a peer unless whitelisted. + * @param {Peer} peer + */ + public suspend(peer) { + const whitelist = this.config.get("whitelist"); + if (whitelist && whitelist.includes(peer.ip)) { + return; + } + + if (peer.offences.length > 0) { + if (dayjs().isAfter((head(peer.offences) as any).until)) { + peer.offences = []; + } + } + + const offence = this.__determineOffence(peer); + + peer.offences.push(offence); + + this.suspensions[peer.ip] = { + peer, + until: offence.until, + reason: offence.reason, + }; + + this.monitor.removePeer(peer); + } + + /** + * Remove a suspended peer. + * @param {Peer} peer + * @return {void} + */ + public async unsuspend(peer) { + if (!this.suspensions[peer.ip]) { + return; + } + + // Don't unsuspend critical offenders before the ban is expired. + if (peer.offences.some(offence => offence.critical)) { + if (dayjs().isBefore(this.suspensions[peer.ip].until)) { + return; + } + } + + delete this.suspensions[peer.ip]; + delete peer.nextSuspensionReminder; + + await this.monitor.acceptNewPeer(peer); + } + + /** + * Reset suspended peers + * @return {void} + */ + public async resetSuspendedPeers() { + logger.info("Clearing suspended peers."); + await Promise.all(Object.values(this.suspensions).map(suspension => this.unsuspend(suspension.peer))); + } + + /** + * Determine if peer is suspended or not. + * @param {Peer} peer + * @return {Boolean} + */ + public isSuspended(peer) { + const suspendedPeer = this.get(peer.ip); + + if (suspendedPeer && dayjs().isBefore(suspendedPeer.until)) { + const nextSuspensionReminder = suspendedPeer.nextSuspensionReminder; + + if (!nextSuspensionReminder || dayjs().isAfter(nextSuspensionReminder)) { + // @ts-ignore + const untilDiff = suspendedPeer.until.diff(dayjs()); + + logger.debug( + `${peer.ip} still suspended for ${prettyMs(untilDiff, { + verbose: true, + })} because of "${suspendedPeer.reason}".`, + ); + + suspendedPeer.nextSuspensionReminder = dayjs().add(5, "minute"); + } + + return true; + } + + if (suspendedPeer) { + delete this.suspensions[peer.ip]; + } + + return false; + } + + /** + * Determine if the peer is whitelisted. + * @param {Peer} peer + * @return {Boolean} + */ + public isWhitelisted(peer) { + return this.config.get("whitelist").includes(peer.ip); + } + + /** + * Determine if the peer is blacklisted. + * @param {Peer} peer + * @return {Boolean} + */ + public isBlacklisted(peer) { + return this.config.get("blacklist").includes(peer.ip); + } + + /** + * Determine if the peer is within the version constraints. + * @param {Peer} peer + * @return {Boolean} + */ + public isValidVersion(peer) { + const version = peer.version || (peer.headers && peer.headers.version); + if (!semver.valid(version)) { + return false; + } + + return semver.satisfies(version, this.config.get("minimumVersion")); + } + + /** + * Determine if the peer is on the right network. + * @param {Peer} peer + * @return {Boolean} + */ + public isValidNetwork(peer) { + const nethash = peer.nethash || (peer.headers && peer.headers.nethash); + return nethash === config.get("network.nethash"); + } + + /** + * Determine if the peer is has the same milestones. + * @param {Peer} peer + * @return {Boolean} + */ + public isValidMilestoneHash(peer) { + const milestoneHash = peer.milestoneHash || (peer.headers && peer.headers.milestoneHash); + return milestoneHash === config.get("milestoneHash"); + } + + /** + * Determine if the peer has a valid port. + * @param {Peer} peer + * @return {Boolean} + */ + public isValidPort(peer) { + return peer.port === this.config.get("port"); + } + + /** + * Decide if the given peer is a repeat offender. + * @param {Object} peer + * @return {Boolean} + */ + public isRepeatOffender(peer) { + return sumBy(peer.offences, "weight") >= 150; + } + + /** + * Decide for how long the peer should be banned. + * @param {Peer} peer + * @return {dayjs} + */ + public __determineOffence(peer) { + if (this.isBlacklisted(peer)) { + return this.__determinePunishment(peer, offences.BLACKLISTED); + } + + if (app.has("state")) { + const state = app.resolve("state"); + if (state && state.forkedBlock && peer.ip === state.forkedBlock.ip) { + return this.__determinePunishment(peer, offences.FORK); + } + } + + if (peer.commonBlocks === false) { + delete peer.commonBlocks; + + return this.__determinePunishment(peer, offences.NO_COMMON_BLOCKS); + } + + if (peer.commonId === false) { + delete peer.commonId; + + return this.__determinePunishment(peer, offences.NO_COMMON_ID); + } + + // NOTE: We check this extra because a response can still succeed if + // it returns any codes that are not 4xx or 5xx. + if (peer.status === 503) { + return this.__determinePunishment(peer, offences.BLOCKCHAIN_NOT_READY); + } + + if (peer.status === 429) { + return this.__determinePunishment(peer, offences.TOO_MANY_REQUESTS); + } + + if (peer.status && peer.status !== 200) { + return this.__determinePunishment(peer, offences.INVALID_STATUS); + } + + if (peer.delay === -1) { + return this.__determinePunishment(peer, offences.TIMEOUT); + } + + if (peer.delay > 2000) { + return this.__determinePunishment(peer, offences.HIGH_LATENCY); + } + + if (!this.isValidNetwork(peer)) { + return this.__determinePunishment(peer, offences.INVALID_NETWORK); + } + + if (!this.isValidVersion(peer)) { + return this.__determinePunishment(peer, offences.INVALID_VERSION); + } + + if (!this.isValidMilestoneHash(peer)) { + return this.__determinePunishment(peer, offences.INVALID_MILESTONE_HASH); + } + + // NOTE: Suspending this peer only means that we no longer + // will download blocks from him but he can still download blocks from us. + const heightDifference = Math.abs(this.monitor.getNetworkHeight() - peer.state.height); + + if (heightDifference >= 153) { + return this.__determinePunishment(peer, offences.INVALID_HEIGHT); + } + + return this.__determinePunishment(peer, offences.UNKNOWN); + } + + /** + * Compile the information about the punishment the peer will face. + * @param {Object} peer + * @param {Object} offence + * @return {Object} + */ + public __determinePunishment(peer, offence) { + if (this.isRepeatOffender(peer)) { + offence = offences.REPEAT_OFFENDER; + } + + const until = dayjs().add(offence.number, offence.period); + // @ts-ignore + const untilDiff = until.diff(dayjs()); + + logger.debug( + `Suspended ${peer.ip} for ${prettyMs(untilDiff, { + verbose: true, + })} because of "${offence.reason}"`, + ); + + return { + until, + reason: offence.reason, + weight: offence.weight, + }; + } +} + +export const guard = new Guard(); diff --git a/packages/core-p2p/src/court/index.ts b/packages/core-p2p/src/court/index.ts new file mode 100644 index 0000000000..e012d57a5a --- /dev/null +++ b/packages/core-p2p/src/court/index.ts @@ -0,0 +1 @@ +export * from "./guard"; diff --git a/packages/core-p2p/src/court/offences.ts b/packages/core-p2p/src/court/offences.ts new file mode 100644 index 0000000000..9ce3d3c3db --- /dev/null +++ b/packages/core-p2p/src/court/offences.ts @@ -0,0 +1,96 @@ +export const offences = { + BLACKLISTED: { + number: 1, + period: "year", + reason: "Blacklisted", + weight: 10, + critical: true, + }, + NO_COMMON_BLOCKS: { + number: 5, + period: "minute", + reason: "No Common Blocks", + weight: 1, + critical: true, + }, + NO_COMMON_ID: { + number: 5, + period: "minute", + reason: "No Common Id", + weight: 1, + critical: true, + }, + INVALID_VERSION: { + number: 5, + period: "minute", + reason: "Invalid Version", + weight: 2, + }, + INVALID_MILESTONE_HASH: { + number: 5, + period: "minute", + reason: "Invalid Milestones", + weight: 2, + }, + INVALID_HEIGHT: { + number: 10, + period: "minute", + reason: "Node is not at height", + weight: 3, + }, + INVALID_NETWORK: { + number: 5, + period: "minute", + reason: "Invalid Network", + weight: 5, + critical: true, + }, + INVALID_STATUS: { + number: 5, + period: "minute", + reason: "Invalid Response Status", + weight: 3, + }, + TIMEOUT: { + number: 2, + period: "minute", + reason: "Timeout", + weight: 2, + }, + HIGH_LATENCY: { + number: 1, + period: "minute", + reason: "High Latency", + weight: 1, + }, + BLOCKCHAIN_NOT_READY: { + number: 30, + period: "second", + reason: "Blockchain not ready", + weight: 0, + }, + TOO_MANY_REQUESTS: { + number: 60, + period: "second", + reason: "Rate limit exceeded", + weight: 0, + }, + FORK: { + number: 15, + period: "minute", + reason: "Fork", + weight: 10, + }, + UNKNOWN: { + number: 10, + period: "minute", + reason: "Unknown", + weight: 5, + }, + REPEAT_OFFENDER: { + number: 1, + period: "day", + reason: "Repeat Offender", + weight: 100, + }, +}; diff --git a/packages/core-p2p/src/defaults.ts b/packages/core-p2p/src/defaults.ts new file mode 100644 index 0000000000..29c64af6d0 --- /dev/null +++ b/packages/core-p2p/src/defaults.ts @@ -0,0 +1,74 @@ +export const defaults = { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4002, + /** + * The minimum peer version we expect + */ + minimumVersion: ">=2.1.0", + /** + * The number of peers we expect to be available to start a relay + */ + minimumNetworkReach: 20, + /** + * The timeout for requests to other peers + */ + globalTimeout: 5000, + /** + * The number of seconds until we allow forging + */ + coldStart: 30, + /** + * The maximum number of peers we will broadcast data to + */ + maxPeersBroadcast: 20, + /** + * The list of IPs we allow to be added to the peer list. + */ + whitelist: ["*"], + /** + * The list of IPs we do not allow to be added to the peer list. + */ + blacklist: [], + /** + * The list of IPs can access the remote/internal API. + * + * This should usually only include your localhost to grant access to + * the internal API to your forger. If you run a split relay and forger + * you will need to specify the IP of your forger here. + */ + remoteAccess: ["127.0.0.1", "::ffff:127.0.0.1"], + /** + * The DNS servers we use to verify connectivity + */ + dns: [ + // Google + "8.8.8.8", + "8.8.4.4", + // CloudFlare + "1.1.1.1", + "1.0.0.1", + // OpenDNS + "208.67.222.222", + "208.67.220.220", + ], + /** + * The NTP servers we use to verify connectivity + */ + ntp: ["pool.ntp.org", "time.google.com"], + /** + * @see https://github.com/wraithgar/hapi-rate-limit + */ + rateLimit: { + enabled: true, + pathLimit: false, + userLimit: 20, + userCache: { + expiresIn: 1000, + }, + ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + /** + * Whether or not we enable the remote API (Caution!) + */ + remoteInterface: false, +}; diff --git a/packages/core-p2p/src/index.ts b/packages/core-p2p/src/index.ts new file mode 100644 index 0000000000..5c15e09dbd --- /dev/null +++ b/packages/core-p2p/src/index.ts @@ -0,0 +1,5 @@ +export * from "./monitor"; +export * from "./peer"; +export * from "./court"; +export * from "./plugin"; +export * from "./network-state"; diff --git a/packages/core-p2p/src/monitor.ts b/packages/core-p2p/src/monitor.ts new file mode 100644 index 0000000000..3697e7d4ab --- /dev/null +++ b/packages/core-p2p/src/monitor.ts @@ -0,0 +1,867 @@ +/* tslint:disable:max-line-length */ + +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database, EventEmitter, Logger, P2P } from "@arkecosystem/core-interfaces"; +import { slots } from "@arkecosystem/crypto"; +import dayjs from "dayjs-ext"; +import delay from "delay"; +import fs from "fs"; +import flatten from "lodash/flatten"; +import groupBy from "lodash/groupBy"; +import sample from "lodash/sample"; +import shuffle from "lodash/shuffle"; +import take from "lodash/take"; +import pluralize from "pluralize"; +import prettyMs from "pretty-ms"; + +import { config as localConfig } from "./config"; +import { guard, Guard } from "./court"; +import { NetworkState } from "./network-state"; +import { Peer } from "./peer"; + +import { checkDNS, checkNTP, isValidPeer, restorePeers } from "./utils"; + +let config; +let logger: Logger.ILogger; +let emitter: EventEmitter.EventEmitter; + +export class Monitor implements P2P.IMonitor { + public peers: { [ip: string]: any }; + public server: any; + public guard: Guard; + public config: any; + public nextUpdateNetworkStatusScheduled: boolean; + private initializing: boolean; + private pendingPeers: { [ip: string]: any }; + private coldStartPeriod: dayjs.Dayjs; + + /** + * @constructor + * @throws {Error} If no seed peers + */ + constructor() { + this.peers = {}; + this.coldStartPeriod = dayjs().add(localConfig.get("coldStart"), "second"); + this.initializing = true; + + // Holds temporary peers which are in the process of being accepted. Prevents that + // peers who are not accepted yet, but send multiple requests in a short timeframe will + // get processed multiple times in `acceptNewPeer`. + this.pendingPeers = {}; + } + + /** + * Method to run on startup. + * @param {Object} options + */ + public async start(options) { + this.config = options; + + config = app.getConfig(); + logger = app.resolvePlugin("logger"); + emitter = app.resolvePlugin("event-emitter"); + + await this.__checkDNSConnectivity(options.dns); + await this.__checkNTPConnectivity(options.ntp); + + this.guard = guard.init(this); + + const cachedPeers = restorePeers(); + localConfig.set("peers", cachedPeers); + + this.populateSeedPeers(); + + if (this.config.skipDiscovery) { + logger.warn("Skipped peer discovery because the relay is in skip-discovery mode."); + } else { + await this.updateNetworkStatus(options.networkStart); + + for (const [version, peers] of Object.entries(groupBy(this.peers, "version"))) { + logger.info(`Discovered ${pluralize("peer", peers.length, true)} with v${version}.`); + } + + if (config.get("network.name") !== "mainnet") { + for (const [hashid, peers] of Object.entries(groupBy(this.peers, "hashid"))) { + logger.info(`Discovered ${pluralize("peer", peers.length, true)} on commit ${hashid}.`); + } + } + } + + this.initializing = false; + return this; + } + + /** + * Update network status (currently only peers are updated). + * @param {Boolean} networkStart + * @return {Promise} + */ + public async updateNetworkStatus(networkStart: boolean = false) { + if (process.env.CORE_ENV === "test" || process.env.NODE_ENV === "test") { + return; + } + + if (networkStart) { + logger.warn("Skipped peer discovery because the relay is in genesis-start mode."); + return; + } + + if (this.config.disableDiscovery) { + logger.warn("Skipped peer discovery because the relay is in non-discovery mode."); + return; + } + + try { + await this.discoverPeers(); + await this.cleanPeers(); + } catch (error) { + logger.error(`Network Status: ${error.message}`); + } + + let nextRunDelaySeconds = 600; + + if (!this.hasMinimumPeers()) { + this.populateSeedPeers(); + nextRunDelaySeconds = 5; + logger.info(`Couldn't find enough peers. Falling back to seed peers.`); + } + + this.scheduleUpdateNetworkStatus(nextRunDelaySeconds); + } + + /** + * Accept and store a valid peer. + * @param {Peer} peer + * @throws {Error} If invalid peer + */ + public async acceptNewPeer(peer) { + if (this.config.disableDiscovery && !this.pendingPeers[peer.ip]) { + logger.warn(`Rejected ${peer.ip} because the relay is in non-discovery mode.`); + return; + } + + if ( + !isValidPeer(peer) || + this.guard.isSuspended(peer) || + this.pendingPeers[peer.ip] || + process.env.CORE_ENV === "test" + ) { + return; + } + + const newPeer = new Peer(peer.ip, peer.port); + newPeer.setHeaders(peer); + + if (this.guard.isBlacklisted(peer)) { + logger.debug(`Rejected peer ${peer.ip} as it is blacklisted`); + + return this.guard.suspend(newPeer); + } + + if (!this.guard.isValidVersion(peer) && !this.guard.isWhitelisted(peer)) { + const minimumVersion: string = localConfig.get("minimumVersion"); + + logger.debug( + `Rejected peer ${ + peer.ip + } as it doesn't meet the minimum version requirements. Expected: ${minimumVersion} - Received: ${ + peer.version + }`, + ); + + return this.guard.suspend(newPeer); + } + + if (!this.guard.isValidNetwork(peer)) { + logger.debug( + `Rejected peer ${peer.ip} as it isn't on the same network. Expected: ${config.get( + "network.nethash", + )} - Received: ${peer.nethash}`, + ); + + return this.guard.suspend(newPeer); + } + + if (!this.guard.isValidMilestoneHash(newPeer)) { + logger.debug( + `Rejected peer ${peer.ip} as it has a different milestone hash. Expected: ${config.get( + "milestoneHash", + )} - Received: ${peer.milestoneHash}`, + ); + + return this.guard.suspend(newPeer); + } + + if (this.getPeer(peer.ip)) { + return; + } + + try { + this.pendingPeers[peer.ip] = true; + + await newPeer.ping(1500); + + this.peers[peer.ip] = newPeer; + + logger.debug(`Accepted new peer ${newPeer.ip}:${newPeer.port}`); + + emitter.emit("peer.added", newPeer); + } catch (error) { + logger.debug(`Could not accept new peer '${newPeer.ip}:${newPeer.port}' - ${error}`); + + this.guard.suspend(newPeer); + } finally { + delete this.pendingPeers[peer.ip]; + } + } + + /** + * Remove peer from monitor. + * @param {Peer} peer + */ + public removePeer(peer) { + delete this.peers[peer.ip]; + } + + /** + * Clear peers which aren't responding. + * @param {Boolean} fast + * @param {Boolean} tracker + * @param {Boolean} forcePing + */ + public async cleanPeers(fast = false, forcePing = false) { + const keys = Object.keys(this.peers); + let unresponsivePeers = 0; + const pingDelay = fast ? 1500 : localConfig.get("globalTimeout"); + const max = keys.length; + + logger.info(`Checking ${max} peers :telescope:`); + await Promise.all( + keys.map(async ip => { + const peer = this.getPeer(ip); + try { + await peer.ping(pingDelay, forcePing); + } catch (error) { + unresponsivePeers++; + + const formattedDelay = prettyMs(pingDelay, { verbose: true }); + logger.debug(`Removed peer ${ip} because it didn't respond within ${formattedDelay}.`); + emitter.emit("peer.removed", peer); + + this.removePeer(peer); + + return null; + } + }), + ); + + if (this.initializing) { + logger.info(`${max - unresponsivePeers} of ${max} peers on the network are responsive`); + logger.info(`Median Network Height: ${this.getNetworkHeight().toLocaleString()}`); + logger.info(`Network PBFT status: ${this.getPBFTForgingStatus()}`); + } + } + + /** + * Suspend an existing peer. + * @param {Peer} peer + * @return {void} + */ + public suspendPeer(ip) { + const peer = this.peers[ip]; + + if (peer && !this.guard.isSuspended(peer)) { + this.guard.suspend(peer); + } + } + + /** + * Get a list of all suspended peers. + * @return {void} + */ + public getSuspendedPeers() { + return this.guard.all(); + } + + /** + * Get all available peers. + * @return {Peer[]} + */ + public getPeers() { + return Object.values(this.peers) as Peer[]; + } + + /** + * Get the peer available peers. + * @param {String} ip + * @return {Peer} + */ + public getPeer(ip) { + return this.peers[ip]; + } + + public async peerHasCommonBlocks(peer, blockIds) { + if (!(await peer.hasCommonBlocks(blockIds))) { + logger.error(`Could not get common block for ${peer.ip}`); + + peer.commonBlocks = false; + + this.guard.suspend(peer); + + return false; + } + + return true; + } + + /** + * Get a random, available peer. + * @param {(Number|undefined)} acceptableDelay + * @return {Peer} + */ + public getRandomPeer(acceptableDelay?, downloadSize?, failedAttempts?) { + failedAttempts = failedAttempts === undefined ? 0 : failedAttempts; + + const peers = this.getPeers().filter(peer => { + if (peer.ban < new Date().getTime()) { + return true; + } + + if (acceptableDelay && peer.delay < acceptableDelay) { + return true; + } + + if (downloadSize && peer.downloadSize !== downloadSize) { + return true; + } + + return false; + }); + + const randomPeer = sample(peers); + if (!randomPeer) { + failedAttempts++; + + if (failedAttempts > 10) { + throw new Error("Failed to find random peer"); + } else if (failedAttempts > 5) { + return this.getRandomPeer(null, downloadSize, failedAttempts); + } + + return this.getRandomPeer(acceptableDelay, downloadSize, failedAttempts); + } + + return randomPeer; + } + + /** + * Get a random, available peer which can be used for downloading blocks. + * @return {Peer} + */ + public async getRandomDownloadBlocksPeer() { + const randomPeer = this.getRandomPeer(null, 100); + + const recentBlockIds = await this.__getRecentBlockIds(); + if (!(await this.peerHasCommonBlocks(randomPeer, recentBlockIds))) { + return this.getRandomDownloadBlocksPeer(); + } + + return randomPeer; + } + + /** + * Populate list of available peers from random peers. + */ + public async discoverPeers() { + const shuffledPeers = shuffle(this.getPeers()); + + for (const peer of shuffledPeers) { + try { + const hisPeers = await peer.getPeers(); + + for (const p of hisPeers) { + if (isValidPeer(p) && !this.getPeer(p.ip)) { + this.addPeer(p); + } + } + } catch (error) { + // Just try with the next peer from shuffledPeers. + } + + if (this.hasMinimumPeers()) { + return; + } + } + } + + /** + * Check if we have any peers. + * @return {bool} + */ + public hasPeers() { + return !!this.getPeers().length; + } + + /** + * Get the median network height. + * @return {Number} + */ + public getNetworkHeight() { + const medians = this.getPeers() + .filter(peer => peer.state.height) + .map(peer => peer.state.height) + .sort(); + + return medians[Math.floor(medians.length / 2)] || 0; + } + + /** + * Get the PBFT Forging status. + * @return {Number} + */ + public getPBFTForgingStatus() { + const height = this.getNetworkHeight(); + const slot = slots.getSlotNumber(); + + let allowedToForge = 0; + let syncedPeers = 0; + + for (const peer of this.getPeers()) { + if (peer.state) { + if (peer.state.currentSlot === slot) { + syncedPeers++; + + if (peer.state.forgingAllowed && peer.state.height >= height) { + allowedToForge++; + } + } + } + } + + const pbft = allowedToForge / syncedPeers; + + return isNaN(pbft) ? 0 : pbft; + } + + public async getNetworkState(): Promise { + if (!this.__isColdStartActive()) { + await this.cleanPeers(true, true); + } + + return NetworkState.analyze(this); + } + + /** + * Refresh all peers after a fork. Peers with no common blocks are + * suspended. + * @return {void} + */ + public async refreshPeersAfterFork() { + logger.info(`Refreshing ${this.getPeers().length} peers after fork.`); + + // Reset all peers, except peers banned because of causing a fork. + await this.guard.resetSuspendedPeers(); + + // Ban peer who caused the fork + const forkedBlock = app.resolve("state").forkedBlock; + if (forkedBlock) { + this.suspendPeer(forkedBlock.ip); + } + + const recentBlockIds = await this.__getRecentBlockIds(); + + await Promise.all(this.getPeers().map(peer => this.peerHasCommonBlocks(peer, recentBlockIds))); + } + + /** + * Download blocks from a random peer. + * @param {Number} fromBlockHeight + * @return {Object[]} + */ + public async downloadBlocks(fromBlockHeight) { + let randomPeer; + + try { + randomPeer = await this.getRandomDownloadBlocksPeer(); + } catch (error) { + logger.error(`Could not download blocks: ${error.message}`); + + return []; + } + try { + logger.info(`Downloading blocks from height ${fromBlockHeight.toLocaleString()} via ${randomPeer.ip}`); + + const blocks = await randomPeer.downloadBlocks(fromBlockHeight); + blocks.forEach(block => { + block.ip = randomPeer.ip; + }); + + return blocks; + } catch (error) { + logger.error(`Could not download blocks: ${error.message}`); + + return this.downloadBlocks(fromBlockHeight); + } + } + + /** + * Broadcast block to all peers. + * @param {Block} block + * @return {Promise} + */ + public async broadcastBlock(block) { + const blockchain = app.resolvePlugin("blockchain"); + + if (!blockchain) { + logger.info(`Skipping broadcast of block ${block.data.height.toLocaleString()} as blockchain is not ready`); + return; + } + + let blockPing = blockchain.getBlockPing(); + let peers = this.getPeers(); + + if (blockPing && blockPing.block.id === block.data.id) { + // wait a bit before broadcasting if a bit early + const diff = blockPing.last - blockPing.first; + const maxHop = 4; + let proba = (maxHop - blockPing.count) / maxHop; + + if (diff < 500 && proba > 0) { + await delay(500 - diff); + + blockPing = blockchain.getBlockPing(); + + // got aleady a new block, no broadcast + if (blockPing.block.id !== block.data.id) { + return; + } + + proba = (maxHop - blockPing.count) / maxHop; + } + + // TODO: to be put in config? + peers = peers.filter(p => Math.random() < proba); + } + + logger.info( + `Broadcasting block ${block.data.height.toLocaleString()} to ${pluralize("peer", peers.length, true)}`, + ); + + await Promise.all(peers.map(peer => peer.postBlock(block.toJson()))); + } + + /** + * Broadcast transactions to a fixed number of random peers. + * @param {Transaction[]} transactions + */ + public async broadcastTransactions(transactions) { + const peers = take(shuffle(this.getPeers()), localConfig.get("maxPeersBroadcast")); + + logger.debug( + `Broadcasting ${pluralize("transaction", transactions.length, true)} to ${pluralize( + "peer", + peers.length, + true, + )}`, + ); + + transactions = transactions.map(tx => tx.toJson()); + return Promise.all(peers.map(peer => peer.postTransactions(transactions))); + } + + /** + * Update all peers based on height and last block id. + * + * Grouping peers by height and then by common id results in one of the following + * scenarios: + * + * 1) Same height, same common id + * 2) Same height, mixed common id + * 3) Mixed height, same common id + * 4) Mixed height, mixed common id + * + * Scenario 1: Do nothing. + * Scenario 2-4: + * - If own height is ahead of majority do nothing for now. + * - Pick most common id from peers with most common height and calculate quota, + * depending on which the node rolls back or waits. + * + * NOTE: Only called when the network is consecutively missing blocks `p2pUpdateCounter` times. + * @return {String} + */ + public async updatePeersOnMissingBlocks() { + // First ping all peers to get updated heights and remove unresponsive ones. + if (!this.__isColdStartActive()) { + await this.cleanPeers(true); + } + + const peersGroupedByHeight = groupBy(this.getPeers(), "state.height"); + const commonHeightGroups = Object.values(peersGroupedByHeight).sort((a, b) => b.length - a.length); + const peersMostCommonHeight = commonHeightGroups[0]; + const groupedByCommonId = groupBy(peersMostCommonHeight, "state.header.id"); + const commonIdGroupCount = Object.keys(groupedByCommonId).length; + let state = ""; + + if (commonHeightGroups.length === 1 && commonIdGroupCount === 1) { + // No need to do anything. + return state; + } + + const lastBlock = app.resolve("state").getLastBlock(); + + // Do nothing if majority of peers are lagging behind + if (commonHeightGroups.length > 1) { + if (lastBlock.data.height > peersMostCommonHeight[0].state.height) { + logger.info( + `${pluralize( + "peer", + peersMostCommonHeight.length, + true, + )} are at height ${peersMostCommonHeight[0].state.height.toLocaleString()} and lagging behind last height ${lastBlock.data.height.toLocaleString()}. :zzz:`, + ); + return state; + } + } + + // Sort common id groups by length DESC + const commonIdGroups = Object.values(groupedByCommonId).sort((a, b) => b.length - a.length); + + // Peers are sitting on the same height, but there might not be enough + // quorum to move on, because of different last blocks. + if (commonIdGroupCount > 1) { + const chosenPeers = commonIdGroups[0]; + const restGroups = commonIdGroups.slice(1); + + if (restGroups.some(group => group.length === chosenPeers.length)) { + logger.warn("Peers are evenly split at same height with different block ids. :zap:"); + } + + logger.info( + `Detected peers at the same height ${peersMostCommonHeight[0].state.height.toLocaleString()} with different block ids: ${JSON.stringify( + Object.keys(groupedByCommonId).map(k => `${k}: ${groupedByCommonId[k].length}`), + null, + 4, + )}`, + ); + + const badLastBlock = + chosenPeers[0].state.height === lastBlock.data.height && + chosenPeers[0].state.header.id !== lastBlock.data.id; + const quota = chosenPeers.length / flatten(commonIdGroups).length; + if (quota < 0.66) { + // or quota too low TODO: find better number + logger.info(`Common id quota '${quota}' is too low. Going to rollback. :repeat:`); + state = "rollback"; + } else if (badLastBlock) { + // Rollback if last block is bad and quota high + logger.info( + `Last block id ${lastBlock.data.id} is bad, ` + + `but got enough common id quota: ${quota}. Going to rollback. :repeat:`, + ); + state = "rollback"; + } + + if (state === "rollback") { + // Ban all rest peers + const peersToBan = flatten(restGroups); + peersToBan.forEach(peer => { + (peer as any).commonId = false; + this.suspendPeer(peer.ip); + }); + + logger.debug( + `Banned ${pluralize( + "peer", + peersToBan.length, + true, + )} at height '${peersMostCommonHeight[0].state.height.toLocaleString()}' which do not have common id '${ + chosenPeers[0].state.header.id + }'.`, + ); + } + } else { + // Under certain circumstances the headers can be missing (i.e. seed peers when starting up) + const commonHeader = peersMostCommonHeight[0].state.header; + logger.info( + `All peers at most common height ${peersMostCommonHeight[0].state.height.toLocaleString()} share the same block id${ + commonHeader ? ` '${commonHeader.id}'` : "" + }. :pray:`, + ); + } + + return state; + } + + /** + * Dump the list of active peers. + * @return {void} + */ + public dumpPeers() { + const peers = Object.values(this.peers).map(peer => ({ + ip: peer.ip, + port: peer.port, + version: peer.version, + })); + + try { + fs.writeFileSync(`${process.env.CORE_PATH_CACHE}/peers.json`, JSON.stringify(peers, null, 2)); + } catch (err) { + logger.error(`Failed to dump the peer list because of "${err.message}"`); + } + } + + /** + * Get last 10 block IDs from database. + * @return {[]String} + */ + public async __getRecentBlockIds() { + return app.resolvePlugin("database").getRecentBlockIds(); + } + + /** + * Determines if coldstart is still active. + * We need this for the network to start, so we dont forge, while + * not all peers are up, or the network is not active + */ + public __isColdStartActive() { + return this.coldStartPeriod.isAfter(dayjs()); + } + + /** + * Check if the node can connect to any DNS host. + * @return {void} + */ + public async __checkDNSConnectivity(options) { + try { + const host = await checkDNS(options); + + logger.info(`Your network connectivity has been verified by ${host}`); + } catch (error) { + logger.error(error.message); + } + } + + /** + * Check if the node can connect to any NTP host. + * @return {void} + */ + public async __checkNTPConnectivity(options) { + try { + const { host, time } = await checkNTP(options); + + logger.info(`Your NTP connectivity has been verified by ${host}`); + + logger.info( + `Local clock is off by ${time.t < 0 ? "-" : ""}${prettyMs(Math.abs(time.t))} from NTP :alarm_clock:`, + ); + } catch (error) { + logger.error(error.message); + } + } + + /** + * Add a new peer after it passes a few checks. + * @param {Peer} peer + * @return {void} + */ + private addPeer(peer) { + if (this.guard.isBlacklisted(peer)) { + return; + } + + if (!this.guard.isValidVersion(peer)) { + return; + } + + if (!this.guard.isValidNetwork(peer)) { + return; + } + + if (!this.guard.isValidMilestoneHash(peer)) { + return; + } + + if (!this.guard.isValidPort(peer)) { + return; + } + + this.peers[peer.ip] = new Peer(peer.ip, peer.port); + } + + /** + * Schedule the next update network status. + * @param {Number} nextUpdateInSeconds + * @returns {void} + */ + private async scheduleUpdateNetworkStatus(nextUpdateInSeconds) { + if (this.nextUpdateNetworkStatusScheduled) { + return; + } + + this.nextUpdateNetworkStatusScheduled = true; + + await delay(nextUpdateInSeconds * 1000); + + this.nextUpdateNetworkStatusScheduled = false; + + this.updateNetworkStatus(this.config.networkStart); + } + + /** + * Returns if the minimum amount of peers are available. + * @return {Boolean} + */ + private hasMinimumPeers() { + if (this.config.ignoreMinimumNetworkReach) { + logger.warn("Ignored the minimum network reach because the relay is in seed mode."); + + return true; + } + + return Object.keys(this.peers).length >= localConfig.get("minimumNetworkReach"); + } + + /** + * Populate the initial seed list. + * @return {void} + */ + private populateSeedPeers() { + const peerList = config.get("peers.list"); + + if (!peerList) { + app.forceExit("No seed peers defined in peers.json :interrobang:"); + } + + let peers = peerList.map(peer => { + peer.version = app.getVersion(); + return peer; + }); + + if (localConfig.get("peers")) { + peers = { ...peers, ...localConfig.get("peers") }; + } + + const filteredPeers: any[] = Object.values(peers).filter((peer: any) => { + if (!isValidPeer(peer)) { + return false; + } + + if (!this.guard.isValidPort(peer)) { + return false; + } + + if (!this.guard.isValidVersion(peer)) { + return false; + } + + return true; + }); + + for (const peer of filteredPeers) { + delete this.guard.suspensions[peer.ip]; + this.peers[peer.ip] = new Peer(peer.ip, peer.port); + } + } +} + +export const monitor = new Monitor(); diff --git a/packages/core-p2p/src/network-state.ts b/packages/core-p2p/src/network-state.ts new file mode 100644 index 0000000000..31adf66331 --- /dev/null +++ b/packages/core-p2p/src/network-state.ts @@ -0,0 +1,191 @@ +/* tslint:disable:no-shadowed-variable member-ordering max-classes-per-file */ +import { app } from "@arkecosystem/core-container"; +import { slots } from "@arkecosystem/crypto"; +import { config as localConfig } from "./config"; +import { Monitor } from "./monitor"; +import { Peer } from "./peer"; + +class QuorumDetails { + public getQuorum() { + const quorum = this.peersQuorum / (this.peersQuorum + this.peersNoQuorum); + return isFinite(quorum) ? quorum : 0; + } + + /** + * Number of peers on same height, with same block and same slot. Used for + * quorum calculation. + */ + public peersQuorum = 0; + + /** + * Number of peers which do not meet the quorum requirements. Used for + * quorum calculation. + */ + public peersNoQuorum = 0; + + /** + * Number of overheight peers. + */ + public peersOverHeight = 0; + + /** + * All overheight block headers grouped by id. + */ + public peersOverHeightBlockHeaders: { [id: string]: any } = {}; + + /** + * Number of peers which are up to N (=3) blocks below `nodeHeight`. + * In other words peers lower than `nodeHeight` - N are not considered for quorum. + */ + public peersBelowHeightElasticity = 0; + + /** + * Number of ignored peers (i.e height far below `nodeHeight`). Ignored peers + * are not used for quorum. + */ + public peersQuorumIgnored = 0; + + /** + * The following properties are not mutual exclusive for a peer + * and imply a peer is on the same `nodeHeight`. + */ + + /** + * Number of peers with a different last block id. + */ + public peersDifferentBlockId = 0; + + /** + * Number of peers with a different slot. + */ + public peersDifferentSlot = 0; + + /** + * Number of peers where forging is not allowed. + */ + public peersForgingNotAllowed = 0; +} + +export enum NetworkStateStatus { + Default, + ColdStart, + BelowMinimumPeers, + Test, + Unknown, +} + +export class NetworkState { + private nodeHeight: number; + private lastBlockId: string; + private quorumDetails: QuorumDetails; + + public constructor(readonly status: NetworkStateStatus, lastBlock?: any) { + this.quorumDetails = new QuorumDetails(); + + if (lastBlock) { + this.setLastBlock(lastBlock); + } + } + + public setLastBlock(lastBlock) { + this.nodeHeight = lastBlock.data.height; + this.lastBlockId = lastBlock.data.id; + } + + /** + * Returns the current network state. Peers are updated before the call. + */ + public static analyze(monitor: Monitor): NetworkState { + const lastBlock = app.resolvePlugin("blockchain").getLastBlock(); + + const peers = monitor.getPeers(); + const minimumNetworkReach = localConfig.get("minimumNetworkReach", 20); + + if (monitor.__isColdStartActive()) { + return new NetworkState(NetworkStateStatus.ColdStart, lastBlock); + } else if (process.env.CORE_ENV === "test") { + return new NetworkState(NetworkStateStatus.Test, lastBlock); + } else if (peers.length < minimumNetworkReach) { + return new NetworkState(NetworkStateStatus.BelowMinimumPeers, lastBlock); + } + + return this.analyzeNetwork(lastBlock, peers); + } + + public static parse(data: any): NetworkState { + if (!data || data.status === undefined) { + return new NetworkState(NetworkStateStatus.Unknown); + } + + const networkState = new NetworkState(data.status); + networkState.nodeHeight = data.nodeHeight; + networkState.lastBlockId = data.lastBlockId; + Object.assign(networkState.quorumDetails, data.quorumDetails); + + return networkState; + } + + public getQuorum() { + if (this.status === NetworkStateStatus.Test) { + return 1; + } + + return this.quorumDetails.getQuorum(); + } + + public getOverHeightBlockHeaders() { + return Object.values(this.quorumDetails.peersOverHeightBlockHeaders); + } + + public toJson() { + const data = { quorum: this.getQuorum() } as any; + Object.assign(data, this); + delete data.status; + + return JSON.stringify(data, null, 2); + } + + private static analyzeNetwork(lastBlock, peers: Peer[]): NetworkState { + const networkState = new NetworkState(NetworkStateStatus.Default, lastBlock); + const currentSlot = slots.getSlotNumber(); + + for (const peer of peers) { + networkState.update(peer, currentSlot); + } + + return networkState; + } + + private update(peer: Peer, currentSlot: number) { + if (peer.state.height === this.nodeHeight) { + let quorum = true; + if (peer.state.header.id !== this.lastBlockId) { + quorum = false; + this.quorumDetails.peersDifferentBlockId++; + } + + if (peer.state.currentSlot !== currentSlot) { + quorum = false; + this.quorumDetails.peersDifferentSlot++; + } + + if (!peer.state.forgingAllowed) { + quorum = false; + this.quorumDetails.peersForgingNotAllowed++; + } + + quorum ? this.quorumDetails.peersQuorum++ : this.quorumDetails.peersNoQuorum++; + } else if (peer.state.height > this.nodeHeight) { + this.quorumDetails.peersNoQuorum++; + this.quorumDetails.peersOverHeight++; + this.quorumDetails.peersOverHeightBlockHeaders[peer.state.header.id] = peer.state.header; + } else if (this.nodeHeight - peer.state.height < 3) { + // suppose the max network elasticity accross 3 blocks + this.quorumDetails.peersNoQuorum++; + this.quorumDetails.peersBelowHeightElasticity++; + } else { + // Peers far below own height are ignored for quorum + this.quorumDetails.peersQuorumIgnored++; + } + } +} diff --git a/packages/core-p2p/src/peer.ts b/packages/core-p2p/src/peer.ts new file mode 100644 index 0000000000..f0ad8f4773 --- /dev/null +++ b/packages/core-p2p/src/peer.ts @@ -0,0 +1,332 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger, P2P } from "@arkecosystem/core-interfaces"; +import axios from "axios"; +import dayjs from "dayjs-ext"; +import util from "util"; +import { config as localConfig } from "./config"; + +export class Peer implements P2P.IPeer { + public downloadSize: any; + public hashid: string; + public nethash: any; + public milestoneHash: string; + public version: any; + public os: any; + public status: any; + public delay: any; + public ban: number; + public offences: any[]; + + public headers: { + version: string; + port: number; + nethash: number; + milestoneHash: string; + height: number | null; + "Content-Type": "application/json"; + hashid?: string; + status?: any; + }; + + public state: any; + public url: string; + public lastPinged: dayjs.Dayjs | null; + + private config: any; + private logger: Logger.ILogger; + + /** + * @constructor + * @param {String} ip + * @param {Number} port + */ + constructor(public readonly ip, public readonly port) { + this.logger = app.resolvePlugin("logger"); + this.config = app.getConfig(); + + this.ban = new Date().getTime(); + this.url = `${port % 443 === 0 ? "https://" : "http://"}${ip}:${port}`; + this.state = {}; + this.offences = []; + this.lastPinged = null; + + this.headers = { + version: app.getVersion(), + port: localConfig.get("port"), + nethash: this.config.get("network.nethash"), + milestoneHash: this.config.get("milestoneHash"), + height: null, + "Content-Type": "application/json", + }; + + if (this.config.get("network.name") !== "mainnet") { + this.headers.hashid = app.getHashid(); + } + } + + /** + * Set the given headers for the peer. + * @param {Object} headers + * @return {void} + */ + public setHeaders(headers) { + ["nethash", "milestoneHash", "os", "version"].forEach(key => { + this[key] = headers[key]; + }); + } + + /** + * Set the given status for the peer. + * @param {String} value + * @return {void} + */ + public setStatus(value) { + this.headers.status = value; + } + + /** + * Get information to broadcast. + * @return {Object} + */ + public toBroadcastInfo() { + const data = { + ip: this.ip, + port: +this.port, + nethash: this.nethash, + milestoneHash: this.milestoneHash, + version: this.version, + os: this.os, + status: this.status, + height: this.state.height, + delay: this.delay, + }; + + if (this.config.get("network.name") !== "mainnet") { + (data as any).hashid = this.hashid || "unknown"; + } + + return data; + } + + /** + * Perform POST request for a block. + * @param {Block} block + * @return {(Object|undefined)} + */ + public async postBlock(block) { + return this.__post( + "/peer/blocks", + { block }, + { + headers: this.headers, + timeout: 5000, + }, + ); + } + + /** + * Perform POST request for a transactions. + * @param {Transaction[]} transactions + * @return {(Object|undefined)} + */ + public async postTransactions(transactions) { + try { + const response = await this.__post( + "/peer/transactions", + { + transactions, + }, + { + headers: this.headers, + timeout: 8000, + }, + ); + + return response; + } catch (err) { + throw err; + } + } + + /** + * Download blocks from peer. + * @param {Number} fromBlockHeight + * @return {(Object[]|undefined)} + */ + public async downloadBlocks(fromBlockHeight) { + try { + const response = await axios.get(`${this.url}/peer/blocks`, { + params: { lastBlockHeight: fromBlockHeight }, + headers: this.headers, + timeout: 10000, + }); + + this.__parseHeaders(response); + + const { blocks } = response.data; + const size = blocks.length; + + if (size === 100 || size === 400) { + this.downloadSize = size; + } + + return blocks; + } catch (error) { + this.logger.debug( + `Cannot download blocks from peer ${this.url} - ${util.inspect(error, { + depth: 1, + })}`, + ); + + this.ban = new Date().getTime() + (Math.floor(Math.random() * 40) + 20) * 60000; + + throw error; + } + } + + /** + * Perform ping request on this peer if it has not been + * recently pinged. + * @param {Number} [delay=5000] + * @param {Boolean} force + * @return {Object} + * @throws {Error} If fail to get peer status. + */ + public async ping(delay, force = false) { + if (this.recentlyPinged() && !force) { + return; + } + + const body = await this.__get("/peer/status", delay || localConfig.get("globalTimeout")); + + if (!body) { + throw new Error(`Peer ${this.ip} is unresponsive`); + } + + this.lastPinged = dayjs(); + this.state = body; + return body; + } + + /** + * Returns true if this peer was pinged the past 2 minutes. + * @return {Boolean} + */ + public recentlyPinged() { + return !!this.lastPinged && dayjs().diff(this.lastPinged, "minute") < 2; + } + + /** + * Refresh peer list. It removes blacklisted peers from the fetch + * @return {Object[]} + */ + public async getPeers() { + this.logger.info(`Fetching a fresh peer list from ${this.url}`); + + await this.ping(2000); + + const body = await this.__get("/peer/list"); + + const blacklisted = {}; + localConfig.get("blacklist", []).forEach(ipaddr => (blacklisted[ipaddr] = true)); + return body.peers.filter(peer => !blacklisted[peer.ip]); + } + + /** + * Check if peer has common blocks. + * @param {[]String} ids + * @return {Boolean} + */ + public async hasCommonBlocks(ids) { + try { + let url = `/peer/blocks/common?ids=${ids.join(",")}`; + if (ids.length === 1) { + url += ","; + } + const body = await this.__get(url); + + return body && body.success && body.common; + } catch (error) { + this.logger.error(`Could not determine common blocks with ${this.ip}: ${error}`); + } + + return false; + } + + /** + * Perform GET request. + * @param {String} endpoint + * @param {Number} [timeout=10000] + * @return {(Object|undefined)} + */ + public async __get(endpoint, timeout?) { + const temp = new Date().getTime(); + + try { + const response = await axios.get(`${this.url}${endpoint}`, { + headers: this.headers, + timeout: timeout || this.config.get("peers.globalTimeout"), + }); + + this.delay = new Date().getTime() - temp; + + this.__parseHeaders(response); + + return response.data; + } catch (error) { + this.delay = -1; + + this.logger.debug(`Request to ${this.url}${endpoint} failed because of "${error.message}"`); + + if (error.response) { + this.__parseHeaders(error.response); + } + } + } + + /** + * Perform POST request. + * @param {String} endpoint + * @param {Object} body + * @param {Object} headers + * @return {(Object|undefined)} + */ + public async __post(endpoint, body, headers) { + try { + const response = await axios.post(`${this.url}${endpoint}`, body, headers); + + this.__parseHeaders(response); + + return response.data; + } catch (error) { + this.logger.debug(`Request to ${this.url}${endpoint} failed because of "${error.message}"`); + + if (error.response) { + this.__parseHeaders(error.response); + } + } + } + + /** + * Parse headers from response. + * @param {Object} response + * @return {Object} + */ + public __parseHeaders(response) { + ["nethash", "os", "version", "hashid"].forEach(key => { + this[key] = response.headers[key] || this[key]; + }); + + if (response.headers.milestonehash) { + this.milestoneHash = response.headers.milestonehash; + } + + if (response.headers.height) { + this.state.height = +response.headers.height; + } + + this.status = response.status; + + return response; + } +} diff --git a/packages/core-p2p/src/plugin.ts b/packages/core-p2p/src/plugin.ts new file mode 100644 index 0000000000..c5adda2c82 --- /dev/null +++ b/packages/core-p2p/src/plugin.ts @@ -0,0 +1,34 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { config } from "./config"; +import { defaults } from "./defaults"; +import { monitor, Monitor } from "./monitor"; +import { startServer } from "./server"; + +/** + * The struct used by the plugin container. + * @type {Object} + */ +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "p2p", + async register(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("Starting P2P Interface"); + + config.init(options); + + monitor.server = await startServer(options); + + await monitor.start(options); + + return monitor; + }, + async deregister(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("Stopping P2P Interface"); + + const p2p = container.resolvePlugin("p2p"); + p2p.dumpPeers(); + + return p2p.server.stop(); + }, +}; diff --git a/packages/core-p2p/src/server/index.ts b/packages/core-p2p/src/server/index.ts new file mode 100755 index 0000000000..432e54b2e5 --- /dev/null +++ b/packages/core-p2p/src/server/index.ts @@ -0,0 +1,110 @@ +import { createServer, mountServer, plugins } from "@arkecosystem/core-http-utils"; + +/** + * Create a new hapi.js server. + * @param {Object} config + * @return {Hapi.Server} + */ +const startServer = async config => { + const server = await createServer({ + host: config.host, + port: config.port, + }); + + // @ts-ignore + server.app.config = config; + + // TODO: enable after mainnet migration + // await server.register({ plugin: plugins.contentType }) + + await server.register({ + plugin: require("hapi-rate-limit"), + options: config.rateLimit, + }); + + await server.register({ + plugin: require("./plugins/validate-headers"), + }); + + await server.register({ + plugin: require("./plugins/accept-request"), + options: { + whitelist: config.remoteAccess, + }, + }); + + await server.register({ + plugin: require("./plugins/set-headers"), + }); + + await server.register({ + plugin: require("./plugins/blockchain-ready"), + options: { + routes: [ + "/peer/height", + "/peer/blocks/common", + "/peer/status", + "/peer/blocks", + "/peer/transactions", + "/internal/round", + "/internal/blocks", + "/internal/forgingTransactions", + "/internal/networkState", + "/internal/syncCheck", + "/internal/usernames", + "/remote/blockchain/{event}", + ], + }, + }); + + await server.register({ + plugin: plugins.corsHeaders, + }); + + await server.register({ + plugin: plugins.transactionPayload, + options: { + routes: [ + { + method: "POST", + path: "/peer/transactions", + }, + ], + }, + }); + + // await server.register({ + // plugin: require('./plugins/transaction-pool-ready'), + // options: { + // routes: [ + // '/peer/transactions' + // ] + // } + // }) + + await server.register({ + plugin: require("./versions/config"), + routes: { prefix: "/config" }, + }); + + await server.register({ + plugin: require("./versions/1"), + routes: { prefix: "/peer" }, + }); + + await server.register({ + plugin: require("./versions/internal"), + routes: { prefix: "/internal" }, + }); + + if (config.remoteInterface) { + await server.register({ + plugin: require("./versions/remote"), + routes: { prefix: "/remote" }, + }); + } + + return mountServer("P2P API", server); +}; + +export { startServer }; diff --git a/packages/core-p2p/src/server/plugins/accept-request.ts b/packages/core-p2p/src/server/plugins/accept-request.ts new file mode 100644 index 0000000000..ba1d46407f --- /dev/null +++ b/packages/core-p2p/src/server/plugins/accept-request.ts @@ -0,0 +1,63 @@ +import Boom from "boom"; +import { monitor } from "../../monitor"; +import { isWhitelisted } from "../../utils"; + +/** + * The register method used by hapi.js. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + const requiredHeaders = ["nethash", "milestoneHash", "version", "port", "os"]; + + server.ext({ + type: "onRequest", + async method(request, h) { + const remoteAddress = request.info.remoteAddress; + + if (request.path.startsWith("/config")) { + return h.continue; + } + + if (request.headers["x-auth"] === "forger" || request.path.startsWith("/remote")) { + return isWhitelisted(options.whitelist, remoteAddress) ? h.continue : Boom.forbidden(); + } + + // Only forger requests are internal + if (request.path.startsWith("/internal")) { + return Boom.forbidden(); + } + + if (!monitor.guard) { + return Boom.serverUnavailable("Peer Monitor not ready"); + } + + if (request.path.startsWith("/peer")) { + const peer = { ip: remoteAddress }; + + requiredHeaders.forEach(key => { + peer[key] = request.headers[key]; + }); + + try { + await monitor.acceptNewPeer(peer); + } catch (error) { + return Boom.badImplementation(error.message); + } + } + + return h.continue; + }, + }); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "accept-request", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/plugins/blockchain-ready.ts b/packages/core-p2p/src/server/plugins/blockchain-ready.ts new file mode 100644 index 0000000000..3a47e2a6c2 --- /dev/null +++ b/packages/core-p2p/src/server/plugins/blockchain-ready.ts @@ -0,0 +1,36 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; + +/** + * The register method used by hapi.js. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + server.ext({ + type: "onRequest", + async method(request, h) { + if (!options.routes.includes(request.path)) { + return h.continue; + } + + if (!app.resolvePlugin("blockchain")) { + return Boom.serverUnavailable("Blockchain not ready"); + } + + return h.continue; + }, + }); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "blockchain-ready", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/plugins/set-headers.ts b/packages/core-p2p/src/server/plugins/set-headers.ts new file mode 100644 index 0000000000..a25d7e1198 --- /dev/null +++ b/packages/core-p2p/src/server/plugins/set-headers.ts @@ -0,0 +1,70 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { config as localConfig } from "../../config"; + +const config = app.getConfig(); + +/** + * The register method used by hapi.js. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + const headers = { + nethash: config.get("network.nethash"), + milestoneHash: config.get("milestoneHash"), + version: app.getVersion(), + port: localConfig.get("port"), + os: require("os").platform(), + height: null, + }; + + const requiredHeaders = ["nethash", "milestoneHash", "version", "port", "os", "height"]; + + if (config.get("network.name") !== "mainnet") { + (headers as any).hashid = app.getHashid(); + requiredHeaders.push("hashid"); + } + + server.ext({ + type: "onPreResponse", + async method(request, h) { + const blockchain = app.resolvePlugin("blockchain"); + if (blockchain) { + const lastBlock = blockchain.getLastBlock(); + if (lastBlock) { + headers.height = lastBlock.data.height; + } + } + + const response = request.response; + if (response.isBoom) { + if (response.data) { + // Deleting the property beforehand makes it appear last in the + // response body. + delete response.output.payload.error; + response.output.payload.error = response.data; + } + + requiredHeaders.forEach(key => { + response.output.headers[key] = headers[key]; + }); + } else { + requiredHeaders.forEach(key => response.header(key, headers[key])); + } + + return h.continue; + }, + }); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "set-headers", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/plugins/transaction-pool-ready.ts b/packages/core-p2p/src/server/plugins/transaction-pool-ready.ts new file mode 100644 index 0000000000..f74124aa85 --- /dev/null +++ b/packages/core-p2p/src/server/plugins/transaction-pool-ready.ts @@ -0,0 +1,36 @@ +import { app } from "@arkecosystem/core-container"; +import { TransactionPool } from "@arkecosystem/core-interfaces"; +import Boom from "boom"; + +/** + * The register method used by hapi.js. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + server.ext({ + type: "onRequest", + async method(request, h) { + if (!options.routes.includes(request.path)) { + return h.continue; + } + + if (!app.resolvePlugin("transactionPool")) { + return Boom.serverUnavailable("Transaction Pool not ready"); + } + + return h.continue; + }, + }); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "transaction-pool-ready", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/plugins/validate-headers.ts b/packages/core-p2p/src/server/plugins/validate-headers.ts new file mode 100644 index 0000000000..215dd752f6 --- /dev/null +++ b/packages/core-p2p/src/server/plugins/validate-headers.ts @@ -0,0 +1,84 @@ +import AJV from "ajv"; +import ip from "ip"; + +/** + * The register method used by hapi.js. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + const ajv = new AJV(); + + ajv.addFormat("ip", { + type: "string", + validate: value => ip.isV4Format(value) || ip.isV6Format(value), + }); + + server.ext({ + type: "onRequest", + async method(request, h) { + if (request.path.startsWith("/config")) { + return h.continue; + } + + if (request.headers.port) { + request.headers.port = +request.headers.port; + } + + const errors = ajv.validate( + { + type: "object", + properties: { + ip: { + type: "string", + format: "ip", + }, + port: { + type: "integer", + minimum: 1, + maximum: 65535, + }, + os: { + type: "string", + maxLength: 64, + }, + nethash: { + type: "string", + maxLength: 64, + }, + version: { + type: "string", + maxLength: 11, + }, + }, + required: ["version", "nethash", "port"], + }, + request.headers, + ) + ? null + : ajv.errors; + + if (errors) { + return h + .response({ + error: errors[0].message, + success: false, + }) + .takeover(); + } + + return h.continue; + }, + }); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "validate-headers", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/versions/1/handlers.ts b/packages/core-p2p/src/server/versions/1/handlers.ts new file mode 100644 index 0000000000..6f3a1fed6a --- /dev/null +++ b/packages/core-p2p/src/server/versions/1/handlers.ts @@ -0,0 +1,279 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database, Logger, P2P } from "@arkecosystem/core-interfaces"; +import { TransactionGuard, TransactionPool } from "@arkecosystem/core-transaction-pool"; +import { Joi, models, slots } from "@arkecosystem/crypto"; + +import pluralize from "pluralize"; +import { monitor } from "../../../monitor"; + +const { Block } = models; + +const transactionPool = app.resolvePlugin("transactionPool"); +const logger = app.resolvePlugin("logger"); + +/** + * @type {Object} + */ +export const getPeers = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + try { + const peers = monitor + .getPeers() + .map(peer => peer.toBroadcastInfo()) + .sort((a, b) => a.delay - b.delay); + + return { + success: true, + peers, + }; + } catch (error) { + return h + .response({ success: false, message: error.message }) + .code(500) + .takeover(); + } + }, +}; + +/** + * @type {Object} + */ +export const getHeight = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler(request, h) { + const lastBlock = app.resolvePlugin("blockchain").getLastBlock(); + + return { + success: true, + height: lastBlock.data.height, + id: lastBlock.data.id, + }; + }, +}; + +/** + * @type {Object} + */ +export const getCommonBlocks = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + if (!request.query.ids) { + return { + success: false, + }; + } + + const blockchain = app.resolvePlugin("blockchain"); + + const ids = request.query.ids + .split(",") + .slice(0, 9) + .filter(id => id.match(/^\d+$/)); + + try { + const commonBlocks = await blockchain.database.getCommonBlocks(ids); + + return { + success: true, + common: commonBlocks.length ? commonBlocks[0] : null, + lastBlockHeight: blockchain.getLastBlock().data.height, + }; + } catch (error) { + return h + .response({ success: false, message: error.message }) + .code(500) + .takeover(); + } + }, +}; + +/** + * @type {Object} + */ +export const getTransactions = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler(request, h) { + return { success: true, transactions: [] }; + }, +}; + +/** + * @type {Object} + */ +export const getStatus = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler(request, h) { + const lastBlock = app.resolvePlugin("blockchain").getLastBlock(); + + return { + success: true, + height: lastBlock ? lastBlock.data.height : 0, + forgingAllowed: slots.isForgingAllowed(), + currentSlot: slots.getSlotNumber(), + header: lastBlock ? lastBlock.getHeader() : {}, + }; + }, +}; + +/** + * @type {Object} + */ +export const postBlock = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const blockchain = app.resolvePlugin("blockchain"); + + try { + if (!request.payload || !request.payload.block) { + return { success: false }; + } + + const block = request.payload.block; + + if (blockchain.pingBlock(block)) { + return { success: true }; + } + // already got it? + const lastDownloadedBlock = blockchain.getLastDownloadedBlock(); + + // Are we ready to get it? + if (lastDownloadedBlock && lastDownloadedBlock.data.height + 1 !== block.height) { + return { success: true }; + } + + const b = new Block(block); + + if (!b.verification.verified) { + return { success: false }; + } + + blockchain.pushPingBlock(b.data); + + block.ip = request.info.remoteAddress; + blockchain.handleIncomingBlock(block); + + return { success: true }; + } catch (error) { + logger.error(error); + return { success: false }; + } + }, +}; + +/** + * @type {Object} + */ +export const postTransactions = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + if (!transactionPool) { + return { + success: false, + message: "Transaction pool not available", + }; + } + + const guard = new TransactionGuard(transactionPool); + + const result = await guard.validate(request.payload.transactions); + + if (result.invalid.length > 0) { + return { + success: false, + message: "Transactions list is not conform", + error: "Transactions list is not conform", + }; + } + + if (result.broadcast.length > 0) { + app.resolvePlugin("p2p").broadcastTransactions(guard.getBroadcastTransactions()); + } + + return { + success: true, + transactionIds: result.accept, + }; + }, + options: { + cors: { + additionalHeaders: ["nethash", "port", "version"], + }, + validate: { + payload: { + transactions: Joi.transactionArray() + .min(1) + .max(app.resolveOptions("transactionPool").maxTransactionsPerRequest) + .options({ stripUnknown: true }), + }, + }, + }, +}; + +/** + * @type {Object} + */ +export const getBlocks = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + try { + const databaseService = app.resolvePlugin("database"); + const blockchain = app.resolvePlugin("blockchain"); + + const reqBlockHeight = +request.query.lastBlockHeight + 1; + let blocks = []; + + if (!request.query.lastBlockHeight || isNaN(reqBlockHeight)) { + blocks.push(blockchain.getLastBlock()); + } else { + blocks = await databaseService.getBlocks(reqBlockHeight, 400); + } + + logger.info( + `${request.info.remoteAddress} has downloaded ${pluralize( + "block", + blocks.length, + true, + )} from height ${(!isNaN(reqBlockHeight) ? reqBlockHeight : blocks[0].data.height).toLocaleString()}`, + ); + + return { success: true, blocks: blocks || [] }; + } catch (error) { + logger.error(error.stack); + + return h.response({ success: false, error }).code(500); + } + }, +}; diff --git a/packages/core-p2p/src/server/versions/1/index.ts b/packages/core-p2p/src/server/versions/1/index.ts new file mode 100644 index 0000000000..79b1285940 --- /dev/null +++ b/packages/core-p2p/src/server/versions/1/index.ts @@ -0,0 +1,30 @@ +import * as handlers from "./handlers"; + +/** + * Register v1 routes. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + server.route([ + { method: "GET", path: "/list", ...handlers.getPeers }, + { method: "GET", path: "/blocks", ...handlers.getBlocks }, + { method: "GET", path: "/height", ...handlers.getHeight }, + { method: "GET", path: "/transactions", ...handlers.getTransactions }, + { method: "GET", path: "/blocks/common", ...handlers.getCommonBlocks }, + { method: "GET", path: "/status", ...handlers.getStatus }, + { method: "POST", path: "/blocks", ...handlers.postBlock }, + { method: "POST", path: "/transactions", ...handlers.postTransactions }, + ]); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "Ark P2P API - v1", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/versions/1/schema.ts b/packages/core-p2p/src/server/versions/1/schema.ts new file mode 100644 index 0000000000..e269bf94ce --- /dev/null +++ b/packages/core-p2p/src/server/versions/1/schema.ts @@ -0,0 +1,102 @@ +/** + * @type {Object} + */ +export = { + getStatus: { + type: "object", + properties: { + success: { + type: "boolean", + }, + height: { + type: "integer", + minimum: 0, + }, + currentSlot: { + type: "integer", + minimum: 0, + }, + forgingAllowed: { + type: "boolean", + }, + header: { + type: "object", + }, + }, + required: ["success", "height", "header", "currentSlot", "forgingAllowed"], + }, + getHeight: { + type: "object", + properties: { + success: { + type: "boolean", + }, + height: { + type: "integer", + minimum: 0, + }, + header: { + type: "object", + }, + }, + required: ["success", "height", "header"], + }, + postTransactions: { + type: "object", + }, + getTransactions: { + type: "object", + properties: { + success: { + type: "boolean", + }, + transactions: { + type: "array", + uniqueItems: true, + }, + }, + required: ["transactions"], + }, + getBlocks: { + type: "object", + properties: { + success: { + type: "boolean", + }, + blocks: { + type: "array", + }, + }, + required: ["blocks"], + }, + postBlock: { + type: "object", + properties: { + success: { + type: "boolean", + }, + blockId: { + type: "string", + }, + }, + required: ["success", "blockId"], + }, + getBlock: { + type: "object", + }, + getCommonBlocks: { + type: "object", + }, + getPeers: { + type: "object", + properties: { + success: { + type: "boolean", + }, + peers: { + type: "array", + }, + }, + required: ["peers"], + }, +}; diff --git a/packages/core-p2p/src/server/versions/config/handlers/index.ts b/packages/core-p2p/src/server/versions/config/handlers/index.ts new file mode 100644 index 0000000000..80cba37dc0 --- /dev/null +++ b/packages/core-p2p/src/server/versions/config/handlers/index.ts @@ -0,0 +1,78 @@ +import { app } from "@arkecosystem/core-container"; +import { transformPlugins } from "../transformers/plugins"; + +const appConfig = app.getConfig(); + +export const config = { + async handler(request, h) { + return { + data: { + version: app.getVersion(), + network: { + version: appConfig.get("network.pubKeyHash"), + name: appConfig.get("network.name"), + nethash: appConfig.get("network.nethash"), + explorer: appConfig.get("network.client.explorer"), + token: { + name: appConfig.get("network.client.token"), + symbol: appConfig.get("network.client.symbol"), + }, + }, + plugins: transformPlugins(appConfig.config), + }, + }; + }, + config: { + cors: true, + }, +}; + +export const network = { + handler(request, h) { + return { + data: require(`${process.env.CORE_PATH_CONFIG}/network.json`), + }; + }, +}; + +export const exceptions = { + handler(request, h) { + return { + data: require(`${process.env.CORE_PATH_CONFIG}/exceptions.json`), + }; + }, +}; + +export const milestones = { + handler(request, h) { + return { + data: require(`${process.env.CORE_PATH_CONFIG}/milestones.json`), + }; + }, +}; + +export const genesisBlock = { + handler(request, h) { + return { + data: require(`${process.env.CORE_PATH_CONFIG}/genesisBlock.json`), + }; + }, +}; + +export const peers = { + handler(request, h) { + return { + data: require(`${process.env.CORE_PATH_CONFIG}/peers.json`), + }; + }, +}; + +export const delegates = { + handler(request, h) { + const data = require(`${process.env.CORE_PATH_CONFIG}/delegates.json`); + data.secrets = []; + delete data.bip38; + + return { data }; + }, +}; diff --git a/packages/core-p2p/src/server/versions/config/index.ts b/packages/core-p2p/src/server/versions/config/index.ts new file mode 100644 index 0000000000..ab7fc862a5 --- /dev/null +++ b/packages/core-p2p/src/server/versions/config/index.ts @@ -0,0 +1,28 @@ +import * as handlers from "./handlers"; +/** + * Register v1 routes. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + server.route([ + { method: "GET", path: "/", ...handlers.config }, + { method: "GET", path: "/network", ...handlers.network }, + { method: "GET", path: "/exceptions", ...handlers.exceptions }, + { method: "GET", path: "/milestones", ...handlers.milestones }, + { method: "GET", path: "/genesis-block", ...handlers.genesisBlock }, + { method: "GET", path: "/peers", ...handlers.peers }, + { method: "GET", path: "/delegates", ...handlers.delegates }, + ]); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "Ark P2P - Config API", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/versions/config/transformers/plugins.ts b/packages/core-p2p/src/server/versions/config/transformers/plugins.ts new file mode 100644 index 0000000000..b5f7bcc1e8 --- /dev/null +++ b/packages/core-p2p/src/server/versions/config/transformers/plugins.ts @@ -0,0 +1,35 @@ +/** + * Turns a "config" object into readable object. + * @param {Object} model + * @return {Object} + */ +export function transformPlugins(config) { + const allowed = [ + "@arkecosystem/core-api", + "@arkecosystem/core-graphql", + "@arkecosystem/core-json-rpc", + "@arkecosystem/core-webhooks", + ]; + + const result = {}; + + for (const [name, options] of Object.entries(config.plugins) as any) { + if (allowed.includes(name)) { + if (options.server) { + result[name] = { + enabled: !!options.server.enabled, + port: +options.server.port, + }; + + continue; + } + + result[name] = { + enabled: !!options.enabled, + port: +options.port, + }; + } + } + + return result; +} diff --git a/packages/core-p2p/src/server/versions/internal/handlers/blockchain.ts b/packages/core-p2p/src/server/versions/internal/handlers/blockchain.ts new file mode 100644 index 0000000000..6c6461c5b8 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/handlers/blockchain.ts @@ -0,0 +1,22 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Logger } from "@arkecosystem/core-interfaces"; + +const logger = app.resolvePlugin("logger"); + +/** + * @type {Object} + */ +export const sync = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + logger.debug("Blockchain sync check WAKEUP requested by forger :bed:"); + + app.resolvePlugin("blockchain").forceWakeup(); + + return h.response(null).code(204); + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/handlers/blocks.ts b/packages/core-p2p/src/server/versions/internal/handlers/blocks.ts new file mode 100644 index 0000000000..601aa87b26 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/handlers/blocks.ts @@ -0,0 +1,24 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import * as schema from "../schemas/blocks"; + +/** + * @type {Object} + */ +export const store = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler: (request, h) => { + request.payload.block.ip = request.info.remoteAddress; + + app.resolvePlugin("blockchain").handleIncomingBlock(request.payload.block); + + return h.response(null).code(204); + }, + options: { + validate: schema.store, + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/handlers/network.ts b/packages/core-p2p/src/server/versions/internal/handlers/network.ts new file mode 100644 index 0000000000..5cbf54c63a --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/handlers/network.ts @@ -0,0 +1,17 @@ +import { monitor } from "../../../../monitor"; + +/** + * @type {Object} + */ +export const state = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + return { + data: await monitor.getNetworkState(), + }; + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/handlers/rounds.ts b/packages/core-p2p/src/server/versions/internal/handlers/rounds.ts new file mode 100644 index 0000000000..61ba6a1731 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/handlers/rounds.ts @@ -0,0 +1,45 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database } from "@arkecosystem/core-interfaces"; +import { slots } from "@arkecosystem/crypto"; + +const config = app.getConfig(); + +/** + * @type {Object} + */ +export const current = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const databaseService = app.resolvePlugin("database"); + const blockchain = app.resolvePlugin("blockchain"); + + const lastBlock = blockchain.getLastBlock(); + + const height = lastBlock.data.height + 1; + const maxActive = config.getMilestone(height).activeDelegates; + const blockTime = config.getMilestone(height).blocktime; + const reward = config.getMilestone(height).reward; + const delegates = await databaseService.getActiveDelegates(height); + const timestamp = slots.getTime(); + + const currentForger = parseInt((timestamp / blockTime) as any) % maxActive; + const nextForger = (parseInt((timestamp / blockTime) as any) + 1) % maxActive; + + return { + data: { + current: +(height / maxActive), + reward, + timestamp, + delegates, + currentForger: delegates[currentForger], + nextForger: delegates[nextForger], + lastBlock: lastBlock.data, + canForge: parseInt((1 + lastBlock.data.timestamp / blockTime) as any) * blockTime < timestamp - 1, + }, + }; + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/handlers/transactions.ts b/packages/core-p2p/src/server/versions/internal/handlers/transactions.ts new file mode 100644 index 0000000000..3c38f1ec2b --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/handlers/transactions.ts @@ -0,0 +1,51 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database } from "@arkecosystem/core-interfaces"; +import { models } from "@arkecosystem/crypto"; +import * as schema from "../schemas/transactions"; + +const config = app.getConfig(); +const { Transaction } = models; + +/** + * @type {Object} + */ +export const verify: object = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const transaction = new Transaction(Transaction.deserialize(request.payload.transaction)); + + return { + data: { + valid: await app.resolvePlugin("database").verifyTransaction(transaction), + }, + }; + }, + options: { + validate: schema.verify, + }, +}; + +/** + * @type {Object} + */ +export const forging = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler(request, h) { + const blockchain = app.resolvePlugin("blockchain"); + + const height = blockchain.getLastBlock().data.height; + const maxTransactions = config.getMilestone(height).block.maxTransactions; + + return { + data: blockchain.getUnconfirmedTransactions(maxTransactions), + }; + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/handlers/utils.ts b/packages/core-p2p/src/server/versions/internal/handlers/utils.ts new file mode 100644 index 0000000000..2e153079bd --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/handlers/utils.ts @@ -0,0 +1,52 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, EventEmitter } from "@arkecosystem/core-interfaces"; + +const emitter = app.resolvePlugin("event-emitter"); + +import * as schema from "../schemas/utils"; + +/** + * @type {Object} + */ +export const usernames = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const blockchain = app.resolvePlugin("blockchain"); + const database = blockchain.database; + const walletManager = database.walletManager; + + const lastBlock = blockchain.getLastBlock(); + const delegates = await database.getActiveDelegates(lastBlock ? lastBlock.data.height + 1 : 1); + + const data = {}; + for (const delegate of delegates) { + data[delegate.publicKey] = walletManager.findByPublicKey(delegate.publicKey).username; + } + + return { data }; + }, +}; + +/** + * Emit the given event and payload to the local host. + * @type {Object} + */ +export const emitEvent: object = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler: (request, h) => { + emitter.emit(request.payload.event, request.payload.body); + + return h.response(null).code(204); + }, + options: { + validate: schema.emitEvent, + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/index.ts b/packages/core-p2p/src/server/versions/internal/index.ts new file mode 100644 index 0000000000..243215c5a8 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/index.ts @@ -0,0 +1,40 @@ +import * as blockchain from "./handlers/blockchain"; +import * as blocks from "./handlers/blocks"; +import * as network from "./handlers/network"; +import * as rounds from "./handlers/rounds"; +import * as transactions from "./handlers/transactions"; +import * as utils from "./handlers/utils"; + +/** + * Register internal routes. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + server.route([ + { method: "GET", path: "/network/state", ...network.state }, + + { method: "GET", path: "/blockchain/sync", ...blockchain.sync }, + + { method: "POST", path: "/blocks", ...blocks.store }, + + { method: "GET", path: "/rounds/current", ...rounds.current }, + + { method: "POST", path: "/transactions/verify", ...transactions.verify }, + { method: "GET", path: "/transactions/forging", ...transactions.forging }, + + { method: "GET", path: "/utils/usernames", ...utils.usernames }, + { method: "POST", path: "/utils/events", ...utils.emitEvent }, + ]); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "Ark P2P API - Internal", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/versions/internal/schemas/blocks.ts b/packages/core-p2p/src/server/versions/internal/schemas/blocks.ts new file mode 100644 index 0000000000..b9a74bf5d9 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/schemas/blocks.ts @@ -0,0 +1,10 @@ +import { Joi } from "@arkecosystem/crypto"; + +/** + * @type {Object} + */ +export const store = { + payload: { + block: Joi.block().options({ stripUnknown: true }), + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/schemas/transactions.ts b/packages/core-p2p/src/server/versions/internal/schemas/transactions.ts new file mode 100644 index 0000000000..a055dbb425 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/schemas/transactions.ts @@ -0,0 +1,10 @@ +import Joi from "joi"; + +/** + * @type {Object} + */ +export const verify = { + payload: { + transaction: Joi.string().hex(), + }, +}; diff --git a/packages/core-p2p/src/server/versions/internal/schemas/utils.ts b/packages/core-p2p/src/server/versions/internal/schemas/utils.ts new file mode 100644 index 0000000000..35c411a0e5 --- /dev/null +++ b/packages/core-p2p/src/server/versions/internal/schemas/utils.ts @@ -0,0 +1,11 @@ +import Joi from "joi"; + +/** + * @type {Object} + */ +export const emitEvent = { + payload: { + event: Joi.string(), + body: Joi.any(), + }, +}; diff --git a/packages/core-p2p/src/server/versions/remote/handlers/blockchain.ts b/packages/core-p2p/src/server/versions/remote/handlers/blockchain.ts new file mode 100644 index 0000000000..420039e78a --- /dev/null +++ b/packages/core-p2p/src/server/versions/remote/handlers/blockchain.ts @@ -0,0 +1,27 @@ +import { app } from "@arkecosystem/core-container"; +import * as schema from "../schemas/blockchain"; + +/** + * Respond with a blockchain event. + * @type {Object} + */ +export const emitEvent: object = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + handler: (request, h) => { + /* TODO: Where is this 'events' property coming from? I don't see it set anywhere in core-blockchain. + Leave untyped until I figure out what the proper implementation should be. + */ + const event = app.resolvePlugin("blockchain").events[request.params.event]; + + request.query.param ? event(request.query.params) : event(); + + return h.response(null).code(204); + }, + options: { + validate: schema.emitEvent, + }, +}; diff --git a/packages/core-p2p/src/server/versions/remote/index.ts b/packages/core-p2p/src/server/versions/remote/index.ts new file mode 100644 index 0000000000..3d2fb75080 --- /dev/null +++ b/packages/core-p2p/src/server/versions/remote/index.ts @@ -0,0 +1,21 @@ +import * as blockchain from "./handlers/blockchain"; + +/** + * Register remote routes. + * @param {Hapi.Server} server + * @param {Object} options + * @return {void} + */ +const register = async (server, options) => { + server.route([{ method: "GET", path: "/blockchain/{event}", ...blockchain.emitEvent }]); +}; + +/** + * The struct used by hapi.js. + * @type {Object} + */ +export const plugin = { + name: "Ark P2P - Remote API", + version: "0.1.0", + register, +}; diff --git a/packages/core-p2p/src/server/versions/remote/schemas/blockchain.ts b/packages/core-p2p/src/server/versions/remote/schemas/blockchain.ts new file mode 100644 index 0000000000..358df0f3ef --- /dev/null +++ b/packages/core-p2p/src/server/versions/remote/schemas/blockchain.ts @@ -0,0 +1,10 @@ +import Joi from "joi"; + +/** + * @type {Object} + */ +export const emitEvent = { + params: { + event: Joi.string(), + }, +}; diff --git a/packages/core-p2p/src/utils/check-dns.ts b/packages/core-p2p/src/utils/check-dns.ts new file mode 100644 index 0000000000..2c8783a911 --- /dev/null +++ b/packages/core-p2p/src/utils/check-dns.ts @@ -0,0 +1,24 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import dns from "dns"; +import shuffle from "lodash/shuffle"; +import util from "util"; + +export const checkDNS = async hosts => { + hosts = shuffle(hosts); + + const lookupService = util.promisify(dns.lookupService); + + for (let i = hosts.length - 1; i >= 0; i--) { + try { + await lookupService(hosts[i], 53); + + return Promise.resolve(hosts[i]); + } catch (err) { + const logger = app.resolvePlugin("logger"); + logger.error(err.message); + } + } + + return Promise.reject(new Error("Please check your network connectivity, couldn't connect to any host.")); +}; diff --git a/packages/core-p2p/src/utils/check-ntp.ts b/packages/core-p2p/src/utils/check-ntp.ts new file mode 100644 index 0000000000..a44e171405 --- /dev/null +++ b/packages/core-p2p/src/utils/check-ntp.ts @@ -0,0 +1,32 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import shuffle from "lodash/shuffle"; +import Sntp from "sntp"; + +/** + * Check if it is possible to connect to any NTP host. + * @param {Array} hosts + * @param {Number} [timeout = 1000] + * @return {Promise} + */ +export const checkNTP = (hosts, timeout = 1000): any => { + hosts = shuffle(hosts); + + const logger = app.resolvePlugin("logger"); + + return new Promise(async (resolve, reject) => { + for (const host of hosts) { + try { + const time = await Sntp.time({ host, timeout }); + + return time.errno + ? logger.error(`Host ${host} responsed with: ${time.message}`) + : resolve({ time, host }); + } catch (err) { + logger.error(`Host ${host} responsed with: ${err.message}`); + } + } + + reject(new Error("Please check your NTP connectivity, couldn't connect to any host.")); + }); +}; diff --git a/packages/core-p2p/src/utils/index.ts b/packages/core-p2p/src/utils/index.ts new file mode 100644 index 0000000000..3ae6e63e92 --- /dev/null +++ b/packages/core-p2p/src/utils/index.ts @@ -0,0 +1,5 @@ +export { checkDNS } from "./check-dns"; +export { checkNTP } from "./check-ntp"; +export { isWhitelisted } from "./is-whitelisted"; +export { restorePeers } from "./restore-peers"; +export { isValidPeer } from "./is-valid-peer"; diff --git a/packages/core-p2p/src/utils/is-valid-peer.ts b/packages/core-p2p/src/utils/is-valid-peer.ts new file mode 100644 index 0000000000..7ce8403196 --- /dev/null +++ b/packages/core-p2p/src/utils/is-valid-peer.ts @@ -0,0 +1,51 @@ +import { parse, process } from "ipaddr.js"; +import os from "os"; + +/** + * Checks if the peer is a valid remote peer. + */ +export const isValidPeer = (peer: { ip: string; status?: string | number }): boolean => { + peer.ip = sanitizeRemoteAddress(peer.ip); + + if (!peer.ip) { + return false; + } + + if (isLocalHost(peer.ip)) { + return false; + } + + if (peer.status) { + if (peer.status !== 200 && peer.status !== "OK") { + return false; + } + } + + return true; +}; + +const sanitizeRemoteAddress = (ip: string): string | null => { + try { + return process(ip).toString(); + } catch (error) { + return null; + } +}; + +const isLocalHost = (ip: string): boolean => { + try { + const parsed = parse(ip); + if (parsed.range() === "loopback") { + return true; + } + + if (ip.startsWith("0")) { + return true; + } + + const interfaces = os.networkInterfaces(); + return Object.keys(interfaces).some(ifname => interfaces[ifname].some(iface => iface.address === ip)); + } catch (error) { + return false; + } +}; diff --git a/packages/core-p2p/src/utils/is-whitelisted.ts b/packages/core-p2p/src/utils/is-whitelisted.ts new file mode 100644 index 0000000000..6e40c9e619 --- /dev/null +++ b/packages/core-p2p/src/utils/is-whitelisted.ts @@ -0,0 +1,16 @@ +import mm from "micromatch"; + +/** + * Check if the given IP address is whitelisted. + */ +export const isWhitelisted = (whitelist: string[], ip: string): boolean => { + if (Array.isArray(whitelist)) { + for (const item of whitelist) { + if (mm.isMatch(ip, item)) { + return true; + } + } + } + + return false; +}; diff --git a/packages/core-p2p/src/utils/restore-peers.ts b/packages/core-p2p/src/utils/restore-peers.ts new file mode 100644 index 0000000000..4b605e9617 --- /dev/null +++ b/packages/core-p2p/src/utils/restore-peers.ts @@ -0,0 +1,40 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { existsSync, readFileSync } from "fs"; +import Joi from "joi"; + +const schema = Joi.array().items( + Joi.object().keys({ + ip: Joi.string() + .ip() + .required(), + port: Joi.number() + .port() + .required(), + version: Joi.string().required(), + }), +); + +export const restorePeers = (): any[] => { + const path = `${process.env.CORE_PATH_CACHE}/peers.json`; + if (!existsSync(path)) { + return []; + } + + try { + const peers = JSON.parse(readFileSync(path, { encoding: "utf8" })); + const { value, error } = Joi.validate(peers, schema); + + if (error) { + const logger = app.resolvePlugin("logger"); + if (logger) { + logger.warn("Ignoring corrupt peers from cache."); + } + return []; + } + + return value; + } catch (error) { + return []; + } +}; diff --git a/packages/core-p2p/tsconfig.json b/packages/core-p2p/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-p2p/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-snapshots-cli/CHANGELOG.md b/packages/core-snapshots-cli/CHANGELOG.md deleted file mode 100644 index 6842caf9df..0000000000 --- a/packages/core-snapshots-cli/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.1.0 - 2018-12-03 - -### Added - -- initial release diff --git a/packages/core-snapshots-cli/LICENSE b/packages/core-snapshots-cli/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-snapshots-cli/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-snapshots-cli/README.md b/packages/core-snapshots-cli/README.md index d6036047e2..d41925bdf6 100644 --- a/packages/core-snapshots-cli/README.md +++ b/packages/core-snapshots-cli/README.md @@ -14,8 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Kristjan Košič](https://github.com/kristjank) -- [All Contributors](../../../../contributors) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-snapshots-cli/bin/run b/packages/core-snapshots-cli/bin/run new file mode 100755 index 0000000000..30b14e1773 --- /dev/null +++ b/packages/core-snapshots-cli/bin/run @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +require('@oclif/command').run() +.then(require('@oclif/command/flush')) +.catch(require('@oclif/errors/handle')) diff --git a/packages/core-snapshots-cli/bin/run.cmd b/packages/core-snapshots-cli/bin/run.cmd new file mode 100644 index 0000000000..968fc30758 --- /dev/null +++ b/packages/core-snapshots-cli/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/core-snapshots-cli/bin/snapshot b/packages/core-snapshots-cli/bin/snapshot deleted file mode 100755 index 7a5fa62ec7..0000000000 --- a/packages/core-snapshots-cli/bin/snapshot +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env node - -const cli = require('commander') -const util = require('../lib/utils') -const app = require('@arkecosystem/core-container') - -cli.version(require('../package.json').version) - -const registerCommand = (name, description) => { - return cli - .command(name) - .description(description) - .option('-d, --data ', 'data directory', '~/.ark') - .option('-c, --config ', 'network config', '~/.ark/config') - .option('-t, --token ', 'token name', 'ark') - .option('-n, --network ', 'token network') - .option('--skip-compression', 'skip gzip compression', false) - .option('--trace', 'dumps generated queries and settings to console', false) -} - -registerCommand('create', 'create a full snapshot of the database') - .option('-b, --blocks ', 'blocks to append to, correlates to folder name') - .option('-s, --start ', 'start network height to export', -1) - .option('-e, --end ', 'end network height to export', -1) - .option('--codec ', 'codec name, default is msg-lite binary') - .action(async (options) => { - await util.setUpLite(options) - await require('../lib/commands/create')(options) - }) - -registerCommand('import', 'import data from specified snapshot') - .option('-b, --blocks ', 'blocks to import, corelates to folder name') - .option('--codec ', 'codec name, default is msg-lite binary') - .option('--truncate', 'empty all tables before running import', false) - .option('--skip-restart-round', 'skip revert to current round', false) - .option('--signature-verify', 'signature verification', false) - .action(async (options) => { - await util.setUpLite(options) - await require('../lib/commands/import')(options) - }) - -registerCommand('verify', 'check validity of specified snapshot') - .option('-b, --blocks ', 'blocks to verify, corelates to folder name') - .option('--codec ', 'codec name, default is msg-lite binary') - .option('--signature-verify', 'signature verification', false) - .action(async (options) => { - await util.setUpLite(options) - await require('../lib/commands/verify')(options) - }) - -registerCommand('rollback', 'rollback chain to specified height') - .option('-b, --block-height ', 'block network height number to rollback', -1) - .action(async (options) => { - await util.setUpLite(options) - require('../lib/commands/rollback')(options) - }) - -registerCommand('truncate', 'truncate blockchain database') - .action(async (options) => { - await util.setUpLite(options) - require('../lib/commands/truncate')(options) - }) - -cli - .command('*') - .action(env => { - cli.help() - process.exit(0) - }) - -app.silentShutdown = true -cli.parse(process.argv) diff --git a/packages/core-snapshots-cli/jest.config.js b/packages/core-snapshots-cli/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-snapshots-cli/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-snapshots-cli/lib/commands/create.js b/packages/core-snapshots-cli/lib/commands/create.js deleted file mode 100644 index 81a107e800..0000000000 --- a/packages/core-snapshots-cli/lib/commands/create.js +++ /dev/null @@ -1,24 +0,0 @@ -const fs = require('fs-extra') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const snapshotManager = app.resolvePlugin('snapshots') - -const utils = require('../utils') - -module.exports = async options => { - if (options.filename && !fs.existsSync(utils.getPath(options.filename))) { - logger.error( - `Appending not possible. Existing snapshot ${ - options.filename - } not found. Exiting...`, - ) - throw new Error( - `Appending not possible. Existing snapshot ${ - options.filename - } not found. Exiting...`, - ) - } else { - await snapshotManager.exportData(options) - } -} diff --git a/packages/core-snapshots-cli/lib/commands/import.js b/packages/core-snapshots-cli/lib/commands/import.js deleted file mode 100644 index bcb9f267ac..0000000000 --- a/packages/core-snapshots-cli/lib/commands/import.js +++ /dev/null @@ -1,29 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const snapshotManager = app.resolvePlugin('snapshots') -const emitter = app.resolvePlugin('event-emitter') -const _cliProgress = require('cli-progress') - -module.exports = async options => { - const progressBar = new _cliProgress.Bar( - { - format: - '{bar} {percentage}% | ETA: {eta}s | {value}/{total} | Duration: {duration}s', - }, - _cliProgress.Presets.shades_classic, - ) - - emitter.on('start', data => { - progressBar.start(data.count, 1) - }) - - emitter.on('progress', data => { - progressBar.update(data.value) - }) - - emitter.on('complete', data => { - progressBar.stop() - }) - - await snapshotManager.importData(options) -} diff --git a/packages/core-snapshots-cli/lib/commands/rollback.js b/packages/core-snapshots-cli/lib/commands/rollback.js deleted file mode 100644 index 0b4b675765..0000000000 --- a/packages/core-snapshots-cli/lib/commands/rollback.js +++ /dev/null @@ -1,19 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const snapshotManager = app.resolvePlugin('snapshots') - -module.exports = async options => { - if (options.blockHeight === -1) { - logger.warn( - 'Rollback height is not specified. Rolling back to last completed round.', - ) - } - logger.info( - `Starting the process of blockchain rollback to block height of ${ - options.blockHeight.toLocaleString() - }`, - ) - - await snapshotManager.rollbackChain(options.blockHeight) -} diff --git a/packages/core-snapshots-cli/lib/commands/truncate.js b/packages/core-snapshots-cli/lib/commands/truncate.js deleted file mode 100644 index c6edc37b0a..0000000000 --- a/packages/core-snapshots-cli/lib/commands/truncate.js +++ /dev/null @@ -1,7 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const snapshotManager = app.resolvePlugin('snapshots') - -module.exports = async options => { - await snapshotManager.truncateChain() -} diff --git a/packages/core-snapshots-cli/lib/commands/verify.js b/packages/core-snapshots-cli/lib/commands/verify.js deleted file mode 100644 index 8d3c100f8b..0000000000 --- a/packages/core-snapshots-cli/lib/commands/verify.js +++ /dev/null @@ -1,23 +0,0 @@ -const fs = require('fs-extra') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const snapshotManager = app.resolvePlugin('snapshots') - -module.exports = async options => { - if ( - options.filename - && !fs.existsSync( - `${process.env.ARK_PATH_DATA}/snapshots/${process.env.ARK_NETWORK_NAME}/${ - options.filename - }`, - ) - ) { - logger.error(`Verify not possible. Snapshot ${options.filename} not found.`) - logger.info( - 'Use -f parameter with just the filename and not the full path.', - ) - } else { - await snapshotManager.verifyData(options) - } -} diff --git a/packages/core-snapshots-cli/lib/utils/index.js b/packages/core-snapshots-cli/lib/utils/index.js deleted file mode 100644 index 95c3504077..0000000000 --- a/packages/core-snapshots-cli/lib/utils/index.js +++ /dev/null @@ -1,18 +0,0 @@ -const app = require('@arkecosystem/core-container') - -exports.setUpLite = async options => { - process.env.ARK_SKIP_BLOCKCHAIN = true - await app.setUp('2.0.0', options, { - include: [ - '@arkecosystem/core-config', - '@arkecosystem/core-logger', - '@arkecosystem/core-logger-winston', - '@arkecosystem/core-event-emitter', - '@arkecosystem/core-snapshots', - ], - }) - - return app -} - -exports.tearDown = async () => app.tearDown() diff --git a/packages/core-snapshots-cli/package.json b/packages/core-snapshots-cli/package.json index 2e91fc02d0..7e7d392a9a 100644 --- a/packages/core-snapshots-cli/package.json +++ b/packages/core-snapshots-cli/package.json @@ -1,49 +1,85 @@ { - "name": "@arkecosystem/core-snapshots-cli", - "description": "Provides live cli interface to the core-snapshots module for ARK Core", - "version": "0.1.0", - "contributors": [ - "Kristjan Košič " - ], - "license": "MIT", - "bin": { - "ark:snapshot": "./bin/snapshot" - }, - "scripts": { - "start": "./bin/snapshot", - "debug": "node --inspect-brk ./bin/snapshot", - "create:mainnet": "./bin/snapshot create --config ../core/lib/config/mainnet --network mainnet", - "create:devnet": "./bin/snapshot create --config ../core/lib/config/devnet --network devnet", - "create:testnet": "./bin/snapshot create --config ../core/lib/config/testnet --network testnet", - "import:mainnet": "./bin/snapshot import --config ../core/lib/config/mainnet --network mainnet", - "import:devnet": "./bin/snapshot import --config ../core/lib/config/devnet --network devnet", - "import:testnet": "./bin/snapshot import --config ../core/lib/config/testnet --network testnet", - "verify:mainnet": "./bin/snapshot verify --config ../core/lib/config/mainnet --network mainnet", - "verify:devnet": "./bin/snapshot verify --config ../core/lib/config/devnet --network devnet", - "verify:testnet": "./bin/snapshot verify --config ../core/lib/config/testnet --network testnet", - "rollback:mainnet": "./bin/snapshot rollback --config ../core/lib/config/mainnet --network mainnet", - "rollback:devnet": "./bin/snapshot rollback --config ../core/lib/config/devnet --network devnet", - "rollback:testnet": "./bin/snapshot rollback --config ../core/lib/config/testnet --network testnet", - "truncate:mainnet": "./bin/snapshot truncate --config ../core/lib/config/mainnet --network mainnet", - "truncate:devnet": "./bin/snapshot truncate --config ../core/lib/config/devnet --network devnet", - "truncate:testnet": "./bin/snapshot truncate --config ../core/lib/config/testnet --network testnet", - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand --watch", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "cli-progress": "^2.1.0", - "commander": "^2.19.0", - "fs-extra": "^7.0.1" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-snapshots-cli", + "description": "Provides live cli interface to the core-snapshots module for ARK Core", + "version": "2.1.0", + "contributors": [ + "Kristjan Košič " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "/bin", + "/dist", + "/oclif.manifest.json" + ], + "bin": { + "snapshot": "./bin/run" + }, + "scripts": { + "snapshot": "./bin/run", + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "prepack": "oclif-dev manifest && npm shrinkwrap", + "postpack": "rm -f oclif.manifest.json", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "debug": "node --inspect-brk ./dist/index.js", + "dump": "yarn snapshot dump", + "dump:mainnet": "yarn snapshot dump --network mainnet", + "dump:devnet": "yarn snapshot dump --network devnet", + "dump:testnet": "yarn snapshot dump --network testnet", + "restore": "yarn snapshot restore", + "restore:mainnet": "yarn snapshot restore --network mainnet", + "restore:devnet": "yarn snapshot restore --network devnet", + "restore:testnet": "yarn snapshot restore --network testnet", + "verify": "yarn snapshot verify", + "verify:mainnet": "yarn snapshot verify --network mainnet", + "verify:devnet": "yarn snapshot verify --network devnet", + "verify:testnet": "yarn snapshot verify --network testnet", + "rollback": "yarn snapshot rollback", + "rollback:mainnet": "yarn snapshot rollback --network mainnet", + "rollback:devnet": "yarn snapshot rollback --network devnet", + "rollback:testnet": "yarn snapshot rollback --network testnet", + "truncate": "yarn snapshot truncate", + "truncate:mainnet": "yarn snapshot truncate --network mainnet", + "truncate:devnet": "yarn snapshot truncate --network devnet", + "truncate:testnet": "yarn snapshot truncate --network testnet", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-snapshots": "^2.1.0", + "@oclif/command": "^1.5.8", + "@oclif/config": "^1.12.4", + "@oclif/plugin-help": "^2.1.6", + "@oclif/plugin-not-found": "^1.2.2", + "@types/boom": "^7.2.1", + "@types/cli-progress": "^1.8.0", + "@types/commander": "^2.12.2", + "@types/fs-extra": "^5.0.4", + "cli-progress": "^2.1.1", + "fs-extra": "^7.0.1" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + }, + "oclif": { + "commands": "./dist/commands", + "bin": "snapshot", + "plugins": [ + "@oclif/plugin-help", + "@oclif/plugin-not-found" + ] + } } diff --git a/packages/core-snapshots-cli/src/commands/command.ts b/packages/core-snapshots-cli/src/commands/command.ts new file mode 100644 index 0000000000..c4f02e2d44 --- /dev/null +++ b/packages/core-snapshots-cli/src/commands/command.ts @@ -0,0 +1,25 @@ +import Command, { flags } from "@oclif/command"; + +export abstract class BaseCommand extends Command { + public static flags = { + data: flags.string({ + description: "data directory", + }), + config: flags.string({ + description: "network config", + }), + token: flags.string({ + description: "token name", + default: "ark", + }), + network: flags.string({ + description: "token network", + }), + skipCompression: flags.boolean({ + description: "skip gzip compression", + }), + trace: flags.boolean({ + description: "dumps generated queries and settings to console", + }), + }; +} diff --git a/packages/core-snapshots-cli/src/commands/dump.ts b/packages/core-snapshots-cli/src/commands/dump.ts new file mode 100644 index 0000000000..a06ac275fb --- /dev/null +++ b/packages/core-snapshots-cli/src/commands/dump.ts @@ -0,0 +1,38 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import fs from "fs-extra"; +import { setUpLite } from "../utils"; +import { BaseCommand } from "./command"; + +export class DumpCommand extends BaseCommand { + public static description: string = "create a full snapshot of the database"; + + public static flags = { + ...BaseCommand.flags, + blocks: flags.string({ + description: "blocks to append to, correlates to folder name", + }), + start: flags.integer({ + description: "start network height to export", + default: -1, + }), + end: flags.integer({ + description: "end network height to export", + default: -1, + }), + codec: flags.string({ + description: "codec name, default is msg-lite binary", + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(DumpCommand); + + await setUpLite(flags); + + await app.resolvePlugin("snapshots").exportData(flags); + } +} diff --git a/packages/core-snapshots-cli/src/commands/restore.ts b/packages/core-snapshots-cli/src/commands/restore.ts new file mode 100644 index 0000000000..c3c04e858f --- /dev/null +++ b/packages/core-snapshots-cli/src/commands/restore.ts @@ -0,0 +1,61 @@ +import { app } from "@arkecosystem/core-container"; +import { EventEmitter } from "@arkecosystem/core-interfaces"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import _cliProgress from "cli-progress"; +import { setUpLite } from "../utils"; +import { BaseCommand } from "./command"; + +export class RestoreCommand extends BaseCommand { + public static description: string = "import data from specified snapshot"; + + public static flags = { + ...BaseCommand.flags, + blocks: flags.string({ + description: "blocks to import, corelates to folder name", + required: true, + }), + codec: flags.string({ + description: "codec name, default is msg-lite binary", + }), + truncate: flags.boolean({ + description: "empty all tables before running import", + }), + skipRestartRound: flags.boolean({ + description: "skip revert to current round", + }), + signatureVerify: flags.boolean({ + description: "signature verification", + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(RestoreCommand); + + await setUpLite(flags); + + const emitter = app.resolvePlugin("event-emitter"); + + const progressBar = new _cliProgress.Bar( + { + format: "{bar} {percentage}% | ETA: {eta}s | {value}/{total} | Duration: {duration}s", + }, + _cliProgress.Presets.shades_classic, + ); + + emitter.on("start", data => { + progressBar.start(data.count, 1); + }); + + emitter.on("progress", data => { + progressBar.update(data.value); + }); + + emitter.on("complete", data => { + progressBar.stop(); + }); + + await app.resolvePlugin("snapshots").importData(flags); + } +} diff --git a/packages/core-snapshots-cli/src/commands/rollback.ts b/packages/core-snapshots-cli/src/commands/rollback.ts new file mode 100644 index 0000000000..dfa70112f9 --- /dev/null +++ b/packages/core-snapshots-cli/src/commands/rollback.ts @@ -0,0 +1,35 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import { setUpLite } from "../utils"; +import { BaseCommand } from "./command"; + +export class RollbackCommand extends BaseCommand { + public static description: string = "rollback chain to specified height"; + + public static flags = { + ...BaseCommand.flags, + height: flags.integer({ + description: "block network height number to rollback", + default: -1, + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(RollbackCommand); + + await setUpLite(flags); + + const logger = app.resolvePlugin("logger"); + + if (flags.height === -1) { + logger.warn("Rollback height is not specified. Rolling back to last completed round."); + } + + logger.info(`Starting the process of blockchain rollback to block height of ${flags.height.toLocaleString()}`); + + await app.resolvePlugin("snapshots").rollbackChain(flags.height); + } +} diff --git a/packages/core-snapshots-cli/src/commands/truncate.ts b/packages/core-snapshots-cli/src/commands/truncate.ts new file mode 100644 index 0000000000..1d6a8fa2d3 --- /dev/null +++ b/packages/core-snapshots-cli/src/commands/truncate.ts @@ -0,0 +1,17 @@ +import { app } from "@arkecosystem/core-container"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { setUpLite } from "../utils"; +import { BaseCommand } from "./command"; + +export class TruncateCommand extends BaseCommand { + public static description: string = "truncate blockchain database"; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(TruncateCommand); + + await setUpLite(flags); + + await app.resolvePlugin("snapshots").truncateChain(); + } +} diff --git a/packages/core-snapshots-cli/src/commands/verify.ts b/packages/core-snapshots-cli/src/commands/verify.ts new file mode 100644 index 0000000000..23f2303422 --- /dev/null +++ b/packages/core-snapshots-cli/src/commands/verify.ts @@ -0,0 +1,33 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import fs from "fs-extra"; +import { setUpLite } from "../utils"; +import { BaseCommand } from "./command"; + +export class VerifyCommand extends BaseCommand { + public static description: string = "check validity of specified snapshot"; + + public static flags = { + ...BaseCommand.flags, + blocks: flags.string({ + description: "blocks to verify, corelates to folder name", + }), + codec: flags.string({ + description: "codec name, default is msg-lite binary", + }), + signatureVerify: flags.boolean({ + description: "signature verification", + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(VerifyCommand); + + await setUpLite(flags); + + await app.resolvePlugin("snapshots").verifyData(flags); + } +} diff --git a/packages/core-snapshots-cli/src/index.ts b/packages/core-snapshots-cli/src/index.ts new file mode 100644 index 0000000000..8bdb76f9a0 --- /dev/null +++ b/packages/core-snapshots-cli/src/index.ts @@ -0,0 +1 @@ +export { run } from "@oclif/command"; diff --git a/packages/core-snapshots-cli/src/utils.ts b/packages/core-snapshots-cli/src/utils.ts new file mode 100644 index 0000000000..779bcdfc6f --- /dev/null +++ b/packages/core-snapshots-cli/src/utils.ts @@ -0,0 +1,18 @@ +import { app } from "@arkecosystem/core-container"; + +export const setUpLite = async options => { + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + + await app.setUp("2.0.0", options, { + include: [ + "@arkecosystem/core-logger", + "@arkecosystem/core-logger-winston", + "@arkecosystem/core-event-emitter", + "@arkecosystem/core-snapshots", + ], + }); + + return app; +}; + +export const tearDown = async () => app.tearDown(); diff --git a/packages/core-snapshots-cli/tsconfig.json b/packages/core-snapshots-cli/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-snapshots-cli/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-snapshots/CHANGELOG.md b/packages/core-snapshots/CHANGELOG.md deleted file mode 100644 index 6842caf9df..0000000000 --- a/packages/core-snapshots/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.1.0 - 2018-12-03 - -### Added - -- initial release diff --git a/packages/core-snapshots/LICENSE b/packages/core-snapshots/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-snapshots/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-snapshots/README.md b/packages/core-snapshots/README.md index a5e03644b1..05607af2cf 100644 --- a/packages/core-snapshots/README.md +++ b/packages/core-snapshots/README.md @@ -14,8 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Kristjan Košič](https://github.com/kristjank) -- [All Contributors](../../../../contributors) +- [Joshua Noack](https://github.com/supaiku0) +- [Kristjan Košič](https://github.com/kristjank) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-snapshots/__tests__/fixtures/blocks.js b/packages/core-snapshots/__tests__/fixtures/blocks.js deleted file mode 100644 index 320544a270..0000000000 --- a/packages/core-snapshots/__tests__/fixtures/blocks.js +++ /dev/null @@ -1,184 +0,0 @@ -/* eslint camelcase: "off" */ - -exports.blocks = [ - { - id: '13114381566690093367', - version: 0, - timestamp: 0, - previous_block: null, - height: 1, - number_of_transactions: 52, - total_amount: '12500000000000000', - total_fee: '0', - reward: '0', - payload_length: 11395, - payload_hash: - '2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867', - generator_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - block_signature: - '3044022035694a9b99a9236655c658eb07fc3b02ce5edcc24b76424a7287c54ed3822b0602203621e92defb360490610f763d85e94c2db2807a4bd7756cc8a6a585463ef7bae', - }, - { - id: '15735126799781289065', - version: 0, - timestamp: 45020906, - previous_block: '13114381566690093367', - height: 2, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '02637b15aa50fa95018609a6d7b52b025de807a41b79b164626cee87dd6f61a662', - block_signature: - '304402204020c837df631582fa32c5b160761dd38afc0c65149782773d1277d696dd4596022029d7de41039b7a55ceec37bfd27dec92c1cf4c93e6fc737e74b76fb9f1fb320a', - }, - { - id: '4438392404769884042', - version: 0, - timestamp: 45020914, - previous_block: '15735126799781289065', - height: 3, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '02e9ef70986ab6de9dbd5e1f430018bb8dea671d30c1e34af5146a48f2b73d551d', - block_signature: - '30440220153d88f4017960e6cd920a72c516c68c484d31324bd0e89101218a9da621eed3022055cdd5907d2b9fafd761e0c652c4c63cb8bc2ae5c424189066d44f6068957c13', - }, - { - id: '5712353664969387846', - version: 0, - timestamp: 45020922, - previous_block: '4438392404769884042', - height: 4, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '02951227bb3bc5309aeb96460dbdf945746012810bb4020f35c20feae4eea7e5d4', - block_signature: - '304502210083e2060bcbd7d03ee0d02fcf93ff4a5b18e38003b46d9772b8cab50adfbfd765022001c5941bce33e82bcae8cf00ad666fbbdd812df48dc5bdb2f03afa57d33eb90c', - }, - { - id: '570772616607779515', - version: 0, - timestamp: 45020930, - previous_block: '5712353664969387846', - height: 5, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '022eedf9f1cdae0cfaae635fe415b6a8f1912bc89bc3880ec41135d62cbbebd3d3', - block_signature: - '304402207e05134a7fc5fc1327035f7d86589acdb178f0085ca31eab8f5e46efaa2a5930022006756f0ce06d37a9dcaec3194fcacdbdc3a7773862e61635b93126556a088501', - }, - { - id: '746870545888859312', - version: 0, - timestamp: 45020938, - previous_block: '570772616607779515', - height: 6, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '02ac8d84d81648154f79ba64fbf64cd6ee60f385b2aa52e8eb72bc1374bf55a68c', - block_signature: - '30450221008f5618edd4a4991fc04a57637179d4360ec9f6cc5c044b1bb133c4bf9e9309c002203aaec695288cc935f9003c9013843e6ef26248eb6c937190dbd1dfb09a49a36f', - }, - { - id: '5917444039795114220', - version: 0, - timestamp: 45020946, - previous_block: '746870545888859312', - height: 7, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb', - block_signature: - '3045022100ab5667e66222b7293f0d8d1d1e443cb87529accd86337d3bcbe99f265011479802205d8c904988542bca99a40b0125c335e21ef4beebb829cb100273528082551a2a', - }, - { - id: '8140188225603335547', - version: 0, - timestamp: 45020960, - previous_block: '5917444039795114220', - height: 8, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '021b0f58eca7f123428a8647ffe0644a9454c510f066d3864c27d8c7ad8f5a8aa4', - block_signature: - '30440220670ea1a0acd5af3f01f6e30cfc6bfcbf66592fd0b75933dc9edf2fd06deb5c7502207ae623c982a09e6ee515e06c0e959584e3fb4b5d924c5fe686f9983e729c7057', - }, - { - id: '4508559087330535525', - version: 0, - timestamp: 45020970, - previous_block: '8140188225603335547', - height: 9, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '027f997ab2d8874c8f06d12ef78d39e309ed887a1141d2ba0f50aec8abffaffcde', - block_signature: - '3045022100ceeee0d244d67b087b272a17a9a073622f1472574dbdc0d9c36423645896a4d302206f297a1282b421022489b7de7c162e293906950628d2397729139eba840bee01', - }, - { - id: '13552594290532998422', - version: 0, - timestamp: 45020978, - previous_block: '4508559087330535525', - height: 10, - number_of_transactions: 0, - total_amount: '0', - total_fee: '0', - reward: '0', - payload_length: 0, - payload_hash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generator_public_key: - '03f89a2f86fe683dbbe4856a43c4c326fb2938ae2647fd334ad33261c7c2dee265', - block_signature: - '3044022015d943e2d858f3431434f4c128926c3d78322465a9526312193ff480f76a5f9a022071ea02f6073233bf2ce4be7aa264adc5a5255be1a0b48d5d96af3202670da222', - }, -] diff --git a/packages/core-snapshots/__tests__/fixtures/blocks.ts b/packages/core-snapshots/__tests__/fixtures/blocks.ts new file mode 100644 index 0000000000..c778401498 --- /dev/null +++ b/packages/core-snapshots/__tests__/fixtures/blocks.ts @@ -0,0 +1,164 @@ +/* tslint:disable:max-line-length */ + +export const blocks = [ + { + id: "13114381566690093367", + version: 0, + timestamp: 0, + previous_block: null, + height: 1, + number_of_transactions: 52, + total_amount: "12500000000000000", + total_fee: "0", + reward: "0", + payload_length: 11395, + payload_hash: "2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867", + generator_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + block_signature: + "3044022035694a9b99a9236655c658eb07fc3b02ce5edcc24b76424a7287c54ed3822b0602203621e92defb360490610f763d85e94c2db2807a4bd7756cc8a6a585463ef7bae", + }, + { + id: "15735126799781289065", + version: 0, + timestamp: 45020906, + previous_block: "13114381566690093367", + height: 2, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "02637b15aa50fa95018609a6d7b52b025de807a41b79b164626cee87dd6f61a662", + block_signature: + "304402204020c837df631582fa32c5b160761dd38afc0c65149782773d1277d696dd4596022029d7de41039b7a55ceec37bfd27dec92c1cf4c93e6fc737e74b76fb9f1fb320a", + }, + { + id: "4438392404769884042", + version: 0, + timestamp: 45020914, + previous_block: "15735126799781289065", + height: 3, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "02e9ef70986ab6de9dbd5e1f430018bb8dea671d30c1e34af5146a48f2b73d551d", + block_signature: + "30440220153d88f4017960e6cd920a72c516c68c484d31324bd0e89101218a9da621eed3022055cdd5907d2b9fafd761e0c652c4c63cb8bc2ae5c424189066d44f6068957c13", + }, + { + id: "5712353664969387846", + version: 0, + timestamp: 45020922, + previous_block: "4438392404769884042", + height: 4, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "02951227bb3bc5309aeb96460dbdf945746012810bb4020f35c20feae4eea7e5d4", + block_signature: + "304502210083e2060bcbd7d03ee0d02fcf93ff4a5b18e38003b46d9772b8cab50adfbfd765022001c5941bce33e82bcae8cf00ad666fbbdd812df48dc5bdb2f03afa57d33eb90c", + }, + { + id: "570772616607779515", + version: 0, + timestamp: 45020930, + previous_block: "5712353664969387846", + height: 5, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "022eedf9f1cdae0cfaae635fe415b6a8f1912bc89bc3880ec41135d62cbbebd3d3", + block_signature: + "304402207e05134a7fc5fc1327035f7d86589acdb178f0085ca31eab8f5e46efaa2a5930022006756f0ce06d37a9dcaec3194fcacdbdc3a7773862e61635b93126556a088501", + }, + { + id: "746870545888859312", + version: 0, + timestamp: 45020938, + previous_block: "570772616607779515", + height: 6, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "02ac8d84d81648154f79ba64fbf64cd6ee60f385b2aa52e8eb72bc1374bf55a68c", + block_signature: + "30450221008f5618edd4a4991fc04a57637179d4360ec9f6cc5c044b1bb133c4bf9e9309c002203aaec695288cc935f9003c9013843e6ef26248eb6c937190dbd1dfb09a49a36f", + }, + { + id: "5917444039795114220", + version: 0, + timestamp: 45020946, + previous_block: "746870545888859312", + height: 7, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb", + block_signature: + "3045022100ab5667e66222b7293f0d8d1d1e443cb87529accd86337d3bcbe99f265011479802205d8c904988542bca99a40b0125c335e21ef4beebb829cb100273528082551a2a", + }, + { + id: "8140188225603335547", + version: 0, + timestamp: 45020960, + previous_block: "5917444039795114220", + height: 8, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "021b0f58eca7f123428a8647ffe0644a9454c510f066d3864c27d8c7ad8f5a8aa4", + block_signature: + "30440220670ea1a0acd5af3f01f6e30cfc6bfcbf66592fd0b75933dc9edf2fd06deb5c7502207ae623c982a09e6ee515e06c0e959584e3fb4b5d924c5fe686f9983e729c7057", + }, + { + id: "4508559087330535525", + version: 0, + timestamp: 45020970, + previous_block: "8140188225603335547", + height: 9, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "027f997ab2d8874c8f06d12ef78d39e309ed887a1141d2ba0f50aec8abffaffcde", + block_signature: + "3045022100ceeee0d244d67b087b272a17a9a073622f1472574dbdc0d9c36423645896a4d302206f297a1282b421022489b7de7c162e293906950628d2397729139eba840bee01", + }, + { + id: "13552594290532998422", + version: 0, + timestamp: 45020978, + previous_block: "4508559087330535525", + height: 10, + number_of_transactions: 0, + total_amount: "0", + total_fee: "0", + reward: "0", + payload_length: 0, + payload_hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generator_public_key: "03f89a2f86fe683dbbe4856a43c4c326fb2938ae2647fd334ad33261c7c2dee265", + block_signature: + "3044022015d943e2d858f3431434f4c128926c3d78322465a9526312193ff480f76a5f9a022071ea02f6073233bf2ce4be7aa264adc5a5255be1a0b48d5d96af3202670da222", + }, +]; diff --git a/packages/core-snapshots/__tests__/fixtures/transactions.js b/packages/core-snapshots/__tests__/fixtures/transactions.js deleted file mode 100644 index 706ff0e3c8..0000000000 --- a/packages/core-snapshots/__tests__/fixtures/transactions.js +++ /dev/null @@ -1,212 +0,0 @@ -/* eslint camelcase: "off" */ - -exports.transactions = [ - { - id: '3e3817fd0c35bc36674f3874c2953fa3e35877cbcdb44a08bdc6083dbd39d572', - version: 1, - block_id: '13114381566690093367', - sequence: 0, - timestamp: 0, - sender_public_key: - '0208e6835a8f020cfad439c059b89addc1ce21f8cab0af6e6957e22d3720bff8a4', - recipient_id: 'D6Z26L69gdk9qYmTv5uzk3uGepigtHY4ax', - type: 0, - vendor_field_hex: null, - amount: '12500000000000000', - fee: '0', - serializedHex: - 'ff011e00000000000208e6835a8f020cfad439c059b89addc1ce21f8cab0af6e6957e22d3720bff8a40000000000000000000040b10baf682c00000000001e0f7e658ba40c1cb9bb72db528281abfa59164cd1304402203a3f0f80aad4e0561ae975f241f72a074245f1205d676d290d6e5630ed4c027502207b31fee68e64007c380a4b6baccd4db9b496daef5f7894676586e1347ac30a3b', - }, - { - id: '4041e3a8e3760e40e7b3d0269935b7894df156dbdd08092a9dfab32a00dc0572', - version: 1, - block_id: '13114381566690093367', - sequence: 29, - timestamp: 0, - sender_public_key: - '0212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad', - recipient_id: null, - type: 2, - vendor_field_hex: null, - amount: '0', - fee: '0', - serializedHex: - 'ff011e02000000000212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad0000000000000000000a67656e657369735f33303044022065af2653051345aef10cebf5f98210f228af00768b094eb0f82a5a0765180ca202203c87461e7e40f17273be4638f604b01cae72c47ffe8815917d0dae6f6195709b', - }, - { - id: 'ffc28e2cddf4494fa4bdebd90e5b8fa41253c1faa02a95eca0d7ba6bd04ca616', - version: 1, - block_id: '13114381566690093367', - sequence: 30, - timestamp: 0, - sender_public_key: - '027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb', - recipient_id: null, - type: 2, - vendor_field_hex: null, - amount: '0', - fee: '0', - serializedHex: - 'ff011e0200000000027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb0000000000000000000a67656e657369735f333130450221009c0682b562cb4405cea7e522d7b98b628af4b33c1bfbea354d67351a0d3a066402204e1c060c533c8a8896dbfd5e6219e2a0e7d96c9d40cbbb23bbdf8bc6c0450bb8', - }, - { - id: '7590a188ae48a4deec150be19947638b0fcf273141dc1580385060e6e4c28caf', - version: 1, - block_id: '14911599679969610348', - sequence: 22, - timestamp: 45021290, - sender_public_key: - '03a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a2933', - recipient_id: 'DTKn3J3pMjtPLJxZtjvqR5T6DefPzjgGdf', - type: 3, - vendor_field_hex: null, - amount: '0', - fee: '100000000', - serializedHex: - 'ff011e0366f8ae0203a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a293300e1f5050000000000010103a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a29333044022007dcaa3b9416ea1197a4ef0ca75cda80e50f9b0f58b0f9bcc2e1e00533bd1c9302205ecb2d51c2b8f3373020b4d613f402541a3357cc96a1541f1bf47cbcef762f9a', - }, - { - id: '781397490f2acab13721556ff8d97aaed1156f707c14dcb511e59963c66467c9', - version: 1, - block_id: '14911599679969610348', - sequence: 23, - timestamp: 45021290, - sender_public_key: - '0346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e5359516', - recipient_id: 'D5L5zXgvqtg7qoGimt5vYhFuf5Ued6iWVr', - type: 3, - vendor_field_hex: null, - amount: '0', - fee: '100000000', - serializedHex: - 'ff011e0366f8ae020346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e535951600e1f505000000000001010346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e53595163044022044fb38232294c0457a3ba75cd9da2be4d37c9ebc64eb85d7d890d8b213f8c6ad02207fab96c1a422389e284da5aecd777a7db9ea8250f300328a40295da6f780f46d', - }, - { - id: 'cb173dc8c269b907a0840f77a778ec67503c9e8f01cb448c86480e685cd46bbf', - version: 1, - block_id: '7029786768310419534', - sequence: 0, - timestamp: 45021298, - sender_public_key: - '02257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c1670', - recipient_id: 'D8vKwaX6ksU3mWg7tJDm7v1dbxy4cMo4dh', - type: 3, - vendor_field_hex: null, - amount: '0', - fee: '100000000', - serializedHex: - 'ff011e0367f8ae0202257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c167000e1f5050000000000010102257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c167030440220587ce5f588142845777cf2ebe653e3fc522bf62535d2e73fb29b4a8c36a5a84f02205f0d2041198a01f9e7e65972762f2dddd2b7f3df37dcde6037248e91544f1e51', - }, - { - id: '2fb72fc1f33c9ac479902c74861e95fb93880a9fa6ccc2efe38c7dcf5f0721cc', - version: 1, - block_id: '17044958519703434496', - sequence: 4, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'D5pVkhZbSb4UNXvfmF6j7zdau8yGxfKwSv', - type: 0, - vendor_field_hex: null, - amount: '400000000', - fee: '10000000', - serializedHex: - 'ff011e0017f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff8096980000000000000084d71700000000000000001e077399dbb7a117a4956017b154c0be9cc98ac3bf30450221008e1ae52f2ff48b2fb3d647f828f5ef765127dbe4b66a06a07a00e73ae15a7c6402201302bf455f3f1e6a8541e61ed2f7b3ad9007fef1674b7859199d27e014ab9a31', - }, - { - id: '30baf8c7d428d64b5ac9696d76d229fce56077b557fd4e9633fc385676d36e18', - version: 1, - block_id: '17044958519703434496', - sequence: 5, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'D7u9gSS3KsykEoRys7DxsRNwHjpYoG8mqS', - type: 0, - vendor_field_hex: null, - amount: '4500000000', - fee: '10000000', - serializedHex: - 'ff011e0019f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff809698000000000000008d380c01000000000000001e1e452d07ec1ca63b73aa33c4cb96a17026e1dbd83045022100df0948af0c8b81b0e9b1768c7e2cdfe0c52ac3560441186e2d3d08ca317367a002205888dfce34be3794d736dfa9897f366fd0b6d4595aea02eaa97898a9c8c575e4', - }, - { - id: '3246edce737a748021213d52ae14d14b73e8025c46c5a06a32fe6b314941a885', - version: 1, - block_id: '17044958519703434496', - sequence: 6, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'DSQhC4JyfWaxsZKzF9Kfq5knVifCZA5jLK', - type: 0, - vendor_field_hex: null, - amount: '2300000000', - fee: '10000000', - serializedHex: - 'ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff8096980000000000000037178900000000000000001ee94dce82a20f9e89f88b93933678e7da3bace92a3044022065deb0b2a580825d3204b04990d43b7ccf065876e4c34cf7125920f7b61187a702207cb76e6b2c06fc81c571e6b878bb6584814847dcdf583ea451848f94ce8c2fea', - }, - { - id: '3314b72bced9456955f06d85b29951c9d3cd976154cd4b08c528d5993b3b3a01', - version: 1, - block_id: '17044958519703434496', - sequence: 7, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'DFjnYr6USjvtaz9VZv7agd144W77WUiWtN', - type: 0, - vendor_field_hex: null, - amount: '1900000000', - fee: '10000000', - serializedHex: - 'ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff80969800000000000000b33f7100000000000000001e7440cca101de7e748b6dd53f78aa90d7e97f555d3044022038a76be2094b1fb3341ad74f8a1954aafce1c7a1cbf894c814eaaf2ac111708a02205df57912d98935cf9901451b4c7926c1a54db93f7534863f52e8b03551ef63d0', - }, - { - id: '362ea02651f336167da9e4a7fae1de7591ebee680ed53426ac553d57de351a32', - version: 1, - block_id: '17044958519703434496', - sequence: 8, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'DHaJZBGNrWLzbWbVTc5TnHMJXKeT6comq9', - type: 0, - vendor_field_hex: null, - amount: '2000000000', - fee: '10000000', - serializedHex: - 'ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff8096980000000000000094357700000000000000001e8865f053b768370ab3f8335d625b14fa5d5bdf3e3044022016862ef508260e749e88f66c08d34212786742e78cd62315b965394b0a63580102202e1496c03e4d8534d281cd549ebcdc92b32689d9d66f1f5c78920e1b5b14bd3b', - }, - { - id: '3fd3df05b8a2ed82c4bd149a313d6747af66d5db878de0b50cb1b9819ee0a029', - version: 1, - block_id: '17044958519703434496', - sequence: 9, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'DQUtJHQToFo9Kbgm6R9afGamvXptDzc2mi', - type: 0, - vendor_field_hex: null, - amount: '3000000000', - fee: '10000000', - serializedHex: - 'ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff809698000000000000005ed0b200000000000000001ed4287dabba34a279f51e0aa6292671893e75d3103045022100958c0ca8a7027b37dc6ad000566f9fe1593943f4219bafa62d878301cbe5060c02201e890ca5ccdd2fc9fda33ba7bf021f7f492d2a875a6942b97711d03feabf7327', - }, - { - id: '420103ba4c64f615032eb4a8e83f39242904afa4e66a6c74e4d409aecc14766d', - version: 1, - block_id: '17044958519703434496', - sequence: 10, - timestamp: 45021218, - sender_public_key: - '03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff', - recipient_id: 'DGKgCQ1srb8HZyr47RqQqMvGZ4cDyr4eMo', - type: 0, - vendor_field_hex: null, - amount: '4600000000', - fee: '10000000', - serializedHex: - 'ff011e0019f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff809698000000000000006e2e1201000000000000001e7aa9a3a05f7730f1e9dc8d0636a1f1514a4e3f8e304502210087cf1800256e95a18031ee4119be2aa3be5933b1cbbd494aa497b414335d3b5002201fa0cfab949cf8c73e99d34e2a97ae305af0b77d6edc4fff034a6ed29c325001', - }, -] diff --git a/packages/core-snapshots/__tests__/fixtures/transactions.ts b/packages/core-snapshots/__tests__/fixtures/transactions.ts new file mode 100644 index 0000000000..b7883217a7 --- /dev/null +++ b/packages/core-snapshots/__tests__/fixtures/transactions.ts @@ -0,0 +1,199 @@ +/* tslint:disable:max-line-length */ + +export const transactions = [ + { + id: "3e3817fd0c35bc36674f3874c2953fa3e35877cbcdb44a08bdc6083dbd39d572", + version: 1, + block_id: "13114381566690093367", + sequence: 0, + timestamp: 0, + sender_public_key: "0208e6835a8f020cfad439c059b89addc1ce21f8cab0af6e6957e22d3720bff8a4", + recipient_id: "D6Z26L69gdk9qYmTv5uzk3uGepigtHY4ax", + type: 0, + vendor_field_hex: null, + amount: "12500000000000000", + fee: "0", + serializedHex: + "ff011e00000000000208e6835a8f020cfad439c059b89addc1ce21f8cab0af6e6957e22d3720bff8a40000000000000000000040b10baf682c00000000001e0f7e658ba40c1cb9bb72db528281abfa59164cd1304402203a3f0f80aad4e0561ae975f241f72a074245f1205d676d290d6e5630ed4c027502207b31fee68e64007c380a4b6baccd4db9b496daef5f7894676586e1347ac30a3b", + }, + { + id: "4041e3a8e3760e40e7b3d0269935b7894df156dbdd08092a9dfab32a00dc0572", + version: 1, + block_id: "13114381566690093367", + sequence: 29, + timestamp: 0, + sender_public_key: "0212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad", + recipient_id: null, + type: 2, + vendor_field_hex: null, + amount: "0", + fee: "0", + serializedHex: + "ff011e02000000000212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad0000000000000000000a67656e657369735f33303044022065af2653051345aef10cebf5f98210f228af00768b094eb0f82a5a0765180ca202203c87461e7e40f17273be4638f604b01cae72c47ffe8815917d0dae6f6195709b", + }, + { + id: "ffc28e2cddf4494fa4bdebd90e5b8fa41253c1faa02a95eca0d7ba6bd04ca616", + version: 1, + block_id: "13114381566690093367", + sequence: 30, + timestamp: 0, + sender_public_key: "027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb", + recipient_id: null, + type: 2, + vendor_field_hex: null, + amount: "0", + fee: "0", + serializedHex: + "ff011e0200000000027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb0000000000000000000a67656e657369735f333130450221009c0682b562cb4405cea7e522d7b98b628af4b33c1bfbea354d67351a0d3a066402204e1c060c533c8a8896dbfd5e6219e2a0e7d96c9d40cbbb23bbdf8bc6c0450bb8", + }, + { + id: "7590a188ae48a4deec150be19947638b0fcf273141dc1580385060e6e4c28caf", + version: 1, + block_id: "14911599679969610348", + sequence: 22, + timestamp: 45021290, + sender_public_key: "03a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a2933", + recipient_id: "DTKn3J3pMjtPLJxZtjvqR5T6DefPzjgGdf", + type: 3, + vendor_field_hex: null, + amount: "0", + fee: "100000000", + serializedHex: + "ff011e0366f8ae0203a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a293300e1f5050000000000010103a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a29333044022007dcaa3b9416ea1197a4ef0ca75cda80e50f9b0f58b0f9bcc2e1e00533bd1c9302205ecb2d51c2b8f3373020b4d613f402541a3357cc96a1541f1bf47cbcef762f9a", + }, + { + id: "781397490f2acab13721556ff8d97aaed1156f707c14dcb511e59963c66467c9", + version: 1, + block_id: "14911599679969610348", + sequence: 23, + timestamp: 45021290, + sender_public_key: "0346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e5359516", + recipient_id: "D5L5zXgvqtg7qoGimt5vYhFuf5Ued6iWVr", + type: 3, + vendor_field_hex: null, + amount: "0", + fee: "100000000", + serializedHex: + "ff011e0366f8ae020346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e535951600e1f505000000000001010346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e53595163044022044fb38232294c0457a3ba75cd9da2be4d37c9ebc64eb85d7d890d8b213f8c6ad02207fab96c1a422389e284da5aecd777a7db9ea8250f300328a40295da6f780f46d", + }, + { + id: "cb173dc8c269b907a0840f77a778ec67503c9e8f01cb448c86480e685cd46bbf", + version: 1, + block_id: "7029786768310419534", + sequence: 0, + timestamp: 45021298, + sender_public_key: "02257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c1670", + recipient_id: "D8vKwaX6ksU3mWg7tJDm7v1dbxy4cMo4dh", + type: 3, + vendor_field_hex: null, + amount: "0", + fee: "100000000", + serializedHex: + "ff011e0367f8ae0202257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c167000e1f5050000000000010102257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c167030440220587ce5f588142845777cf2ebe653e3fc522bf62535d2e73fb29b4a8c36a5a84f02205f0d2041198a01f9e7e65972762f2dddd2b7f3df37dcde6037248e91544f1e51", + }, + { + id: "2fb72fc1f33c9ac479902c74861e95fb93880a9fa6ccc2efe38c7dcf5f0721cc", + version: 1, + block_id: "17044958519703434496", + sequence: 4, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "D5pVkhZbSb4UNXvfmF6j7zdau8yGxfKwSv", + type: 0, + vendor_field_hex: null, + amount: "400000000", + fee: "10000000", + serializedHex: + "ff011e0017f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff8096980000000000000084d71700000000000000001e077399dbb7a117a4956017b154c0be9cc98ac3bf30450221008e1ae52f2ff48b2fb3d647f828f5ef765127dbe4b66a06a07a00e73ae15a7c6402201302bf455f3f1e6a8541e61ed2f7b3ad9007fef1674b7859199d27e014ab9a31", + }, + { + id: "30baf8c7d428d64b5ac9696d76d229fce56077b557fd4e9633fc385676d36e18", + version: 1, + block_id: "17044958519703434496", + sequence: 5, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "D7u9gSS3KsykEoRys7DxsRNwHjpYoG8mqS", + type: 0, + vendor_field_hex: null, + amount: "4500000000", + fee: "10000000", + serializedHex: + "ff011e0019f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff809698000000000000008d380c01000000000000001e1e452d07ec1ca63b73aa33c4cb96a17026e1dbd83045022100df0948af0c8b81b0e9b1768c7e2cdfe0c52ac3560441186e2d3d08ca317367a002205888dfce34be3794d736dfa9897f366fd0b6d4595aea02eaa97898a9c8c575e4", + }, + { + id: "3246edce737a748021213d52ae14d14b73e8025c46c5a06a32fe6b314941a885", + version: 1, + block_id: "17044958519703434496", + sequence: 6, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "DSQhC4JyfWaxsZKzF9Kfq5knVifCZA5jLK", + type: 0, + vendor_field_hex: null, + amount: "2300000000", + fee: "10000000", + serializedHex: + "ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff8096980000000000000037178900000000000000001ee94dce82a20f9e89f88b93933678e7da3bace92a3044022065deb0b2a580825d3204b04990d43b7ccf065876e4c34cf7125920f7b61187a702207cb76e6b2c06fc81c571e6b878bb6584814847dcdf583ea451848f94ce8c2fea", + }, + { + id: "3314b72bced9456955f06d85b29951c9d3cd976154cd4b08c528d5993b3b3a01", + version: 1, + block_id: "17044958519703434496", + sequence: 7, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "DFjnYr6USjvtaz9VZv7agd144W77WUiWtN", + type: 0, + vendor_field_hex: null, + amount: "1900000000", + fee: "10000000", + serializedHex: + "ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff80969800000000000000b33f7100000000000000001e7440cca101de7e748b6dd53f78aa90d7e97f555d3044022038a76be2094b1fb3341ad74f8a1954aafce1c7a1cbf894c814eaaf2ac111708a02205df57912d98935cf9901451b4c7926c1a54db93f7534863f52e8b03551ef63d0", + }, + { + id: "362ea02651f336167da9e4a7fae1de7591ebee680ed53426ac553d57de351a32", + version: 1, + block_id: "17044958519703434496", + sequence: 8, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "DHaJZBGNrWLzbWbVTc5TnHMJXKeT6comq9", + type: 0, + vendor_field_hex: null, + amount: "2000000000", + fee: "10000000", + serializedHex: + "ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff8096980000000000000094357700000000000000001e8865f053b768370ab3f8335d625b14fa5d5bdf3e3044022016862ef508260e749e88f66c08d34212786742e78cd62315b965394b0a63580102202e1496c03e4d8534d281cd549ebcdc92b32689d9d66f1f5c78920e1b5b14bd3b", + }, + { + id: "3fd3df05b8a2ed82c4bd149a313d6747af66d5db878de0b50cb1b9819ee0a029", + version: 1, + block_id: "17044958519703434496", + sequence: 9, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "DQUtJHQToFo9Kbgm6R9afGamvXptDzc2mi", + type: 0, + vendor_field_hex: null, + amount: "3000000000", + fee: "10000000", + serializedHex: + "ff011e0018f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff809698000000000000005ed0b200000000000000001ed4287dabba34a279f51e0aa6292671893e75d3103045022100958c0ca8a7027b37dc6ad000566f9fe1593943f4219bafa62d878301cbe5060c02201e890ca5ccdd2fc9fda33ba7bf021f7f492d2a875a6942b97711d03feabf7327", + }, + { + id: "420103ba4c64f615032eb4a8e83f39242904afa4e66a6c74e4d409aecc14766d", + version: 1, + block_id: "17044958519703434496", + sequence: 10, + timestamp: 45021218, + sender_public_key: "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + recipient_id: "DGKgCQ1srb8HZyr47RqQqMvGZ4cDyr4eMo", + type: 0, + vendor_field_hex: null, + amount: "4600000000", + fee: "10000000", + serializedHex: + "ff011e0019f8ae0203d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff809698000000000000006e2e1201000000000000001e7aa9a3a05f7730f1e9dc8d0636a1f1514a4e3f8e304502210087cf1800256e95a18031ee4119be2aa3be5933b1cbbd494aa497b414335d3b5002201fa0cfab949cf8c73e99d34e2a97ae305af0b77d6edc4fff034a6ed29c325001", + }, +]; diff --git a/packages/core-snapshots/__tests__/transport/codec/ark/ark.test.js b/packages/core-snapshots/__tests__/transport/codec/ark/ark.test.js deleted file mode 100644 index 408e5d479d..0000000000 --- a/packages/core-snapshots/__tests__/transport/codec/ark/ark.test.js +++ /dev/null @@ -1,103 +0,0 @@ -const pick = require('lodash/pick') -const msgpack = require('msgpack-lite') - -const { blocks } = require('../../../fixtures/blocks') -const { transactions } = require('../../../fixtures/transactions') -const codec = require('../../../../lib/transport/codec').get('ark') - -beforeAll(async () => { - transactions.forEach(transaction => { - transaction.serialized = transaction.serializedHex - }) -}) - -describe('Ark codec testing', () => { - test('Encode/Decode single block', () => { - console.time('singleblock') - const encoded = msgpack.encode(blocks[1], { codec: codec.blocks }) - const decoded = msgpack.decode(encoded, { codec: codec.blocks }) - - // removing helper property - delete decoded.previous_block_hex - - expect(decoded).toEqual(blocks[1]) - console.timeEnd('singleblock') - }) - - test('Encode/Decode blocks', () => { - console.time('blocks') - for (const [index, block] of blocks.entries()) { - // TODO: skipping genesis for now - wrong id calculation - if (index === 0) { - continue - } - - const encoded = msgpack.encode(block, { codec: codec.blocks }) - const decoded = msgpack.decode(encoded, { codec: codec.blocks }) - - // removing helper property - delete decoded.previous_block_hex - - expect(block).toEqual(decoded) - } - console.timeEnd('blocks') - }) - - test('Encode/Decode transfer transactions', () => { - console.time('transactions ark transfer') - const properties = [ - 'id', - 'version', - 'block_id', - 'sequence', - 'sender_public_key', - 'recipient_id', - 'type', - 'vendor_field_hex', - 'amount', - 'fee', - 'serialized', - ] - const transferTransactions = transactions.filter(trx => trx.type === 0) - for (let i = 0; i < 100; i++) { - for (const transaction of transferTransactions) { - const encoded = msgpack.encode(transaction, { - codec: codec.transactions, - }) - const decoded = msgpack.decode(encoded, { codec: codec.transactions }) - - const source = pick(transaction, properties) - const dest = pick(decoded, properties) - expect(dest).toEqual(source) - } - } - console.timeEnd('transactions ark transfer') - }) - - test('Encode/Decode transactions other than transfer', () => { - console.time('transactions') - const properties = [ - 'id', - 'version', - 'block_id', - 'sequence', - 'sender_public_key', - 'type', - 'vendor_field_hex', - 'amount', - 'fee', - 'serialized', - ] - - const otherTransactions = transactions.filter(trx => trx.type > 0) - for (const transaction of otherTransactions) { - const encoded = msgpack.encode(transaction, { codec: codec.transactions }) - const decoded = msgpack.decode(encoded, { codec: codec.transactions }) - - const source = pick(transaction, properties) - const dest = pick(decoded, properties) - expect(dest).toEqual(source) - } - console.timeEnd('transactions') - }) -}) diff --git a/packages/core-snapshots/__tests__/transport/codec/core/core.test.ts b/packages/core-snapshots/__tests__/transport/codec/core/core.test.ts new file mode 100644 index 0000000000..67e0aad829 --- /dev/null +++ b/packages/core-snapshots/__tests__/transport/codec/core/core.test.ts @@ -0,0 +1,110 @@ +/* tslint:disable:no-console */ + +import pick from "lodash/pick"; +import msgpack from "msgpack-lite"; + +import { blocks } from "../../../fixtures/blocks"; +import { transactions } from "../../../fixtures/transactions"; + +import { CoreCodec } from "../../../../dist/transport/codecs/core-codec"; + +const codec = new CoreCodec(); + +beforeAll(async () => { + transactions.forEach((transaction: any) => { + transaction.serialized = transaction.serializedHex; + }); +}); + +describe("Ark codec testing", () => { + test("Encode/Decode single block", () => { + console.time("singleblock"); + const encoded = msgpack.encode(blocks[1], { codec: codec.blocks }); + const decoded = msgpack.decode(encoded, { codec: codec.blocks }); + + // removing helper property + delete decoded.previous_block_hex; + delete decoded.id_hex; + + expect(decoded).toEqual(blocks[1]); + console.timeEnd("singleblock"); + }); + + test("Encode/Decode blocks", () => { + console.time("blocks"); + for (const [index, block] of blocks.entries()) { + // TODO: skipping genesis for now - wrong id calculation + if (index === 0) { + continue; + } + + const encoded = msgpack.encode(block, { codec: codec.blocks }); + const decoded = msgpack.decode(encoded, { codec: codec.blocks }); + + // removing helper property + delete decoded.previous_block_hex; + delete decoded.id_hex; + + expect(block).toEqual(decoded); + } + console.timeEnd("blocks"); + }); + + test("Encode/Decode transfer transactions", () => { + console.time("transactions ark transfer"); + const properties = [ + "id", + "version", + "block_id", + "sequence", + "sender_public_key", + "recipient_id", + "type", + "vendor_field_hex", + "amount", + "fee", + "serialized", + ]; + const transferTransactions = transactions.filter(trx => trx.type === 0); + for (let i = 0; i < 100; i++) { + for (const transaction of transferTransactions) { + const encoded = msgpack.encode(transaction, { + codec: codec.transactions, + }); + const decoded = msgpack.decode(encoded, { codec: codec.transactions }); + + const source = pick(transaction, properties); + const dest = pick(decoded, properties); + expect(dest).toEqual(source); + } + } + console.timeEnd("transactions ark transfer"); + }); + + test("Encode/Decode transactions other than transfer", () => { + console.time("transactions"); + const properties = [ + "id", + "version", + "block_id", + "sequence", + "sender_public_key", + "type", + "vendor_field_hex", + "amount", + "fee", + "serialized", + ]; + + const otherTransactions = transactions.filter(trx => trx.type > 0); + for (const transaction of otherTransactions) { + const encoded = msgpack.encode(transaction, { codec: codec.transactions }); + const decoded = msgpack.decode(encoded, { codec: codec.transactions }); + + const source = pick(transaction, properties); + const dest = pick(decoded, properties); + expect(dest).toEqual(source); + } + console.timeEnd("transactions"); + }); +}); diff --git a/packages/core-snapshots/__tests__/transport/codec/lite/lite.test.js b/packages/core-snapshots/__tests__/transport/codec/lite/lite.test.js deleted file mode 100644 index cce3f9f27f..0000000000 --- a/packages/core-snapshots/__tests__/transport/codec/lite/lite.test.js +++ /dev/null @@ -1,72 +0,0 @@ -const msgpack = require('msgpack-lite') -const { blocks } = require('../../../fixtures/blocks') -const { transactions } = require('../../../fixtures/transactions') -const codec = require('../../../../lib/transport/codec').get('lite') - -beforeAll(async () => { - transactions.forEach(transaction => { - transaction.serialized = Buffer.from(transaction.serializedHex, 'hex') - }) -}) - -describe('Lite codec testing', () => { - test('Encode/Decode single block', () => { - console.time('singleblock') - const encoded = msgpack.encode(blocks[1], { codec: codec.blocks }) - const decoded = msgpack.decode(encoded, { codec: codec.blocks }) - - // removing helper property - delete decoded.previous_block_hex - - expect(decoded).toEqual(blocks[1]) - console.timeEnd('singleblock') - }) - - test('Encode/Decode blocks', () => { - console.time('blocks') - for (const [index, block] of blocks.entries()) { - // TODO: skipping genesis for now - wrong id calculation - if (index === 0) { - continue - } - - const encoded = msgpack.encode(block, { codec: codec.blocks }) - const decoded = msgpack.decode(encoded, { codec: codec.blocks }) - - // removing helper property - delete decoded.previous_block_hex - - expect(block).toEqual(decoded) - } - console.timeEnd('blocks') - }) - - test('Encode/Decode transactions - all types', () => { - console.time('transactions') - for (const transaction of transactions) { - delete transaction.serializedHex - - const encoded = msgpack.encode(transaction, { codec: codec.transactions }) - const decoded = msgpack.decode(encoded, { codec: codec.transactions }) - - expect(decoded).toEqual(transaction) - } - console.timeEnd('transactions') - }) - - test('Encode/Decode transfer transactions', () => { - console.time('transactions lite transfer') - const transferTransactions = transactions.filter(trx => trx.type === 0) - for (let i = 0; i < 100; i++) { - for (const transaction of transferTransactions) { - const encoded = msgpack.encode(transaction, { - codec: codec.transactions, - }) - const decoded = msgpack.decode(encoded, { codec: codec.transactions }) - - expect(decoded).toEqual(transaction) - } - } - console.timeEnd('transactions lite transfer') - }) -}) diff --git a/packages/core-snapshots/__tests__/transport/codec/lite/lite.test.ts b/packages/core-snapshots/__tests__/transport/codec/lite/lite.test.ts new file mode 100644 index 0000000000..8c598d9b5d --- /dev/null +++ b/packages/core-snapshots/__tests__/transport/codec/lite/lite.test.ts @@ -0,0 +1,76 @@ +/* tslint:disable:no-console */ + +import msgpack from "msgpack-lite"; +import { LiteCodec } from "../../../../dist/transport/codecs/lite-codec"; +import { blocks } from "../../../fixtures/blocks"; +import { transactions } from "../../../fixtures/transactions"; + +const codec = new LiteCodec(); + +beforeAll(async () => { + transactions.forEach((transaction: any) => { + transaction.serialized = Buffer.from(transaction.serializedHex, "hex"); + }); +}); + +describe("Lite codec testing", () => { + test("Encode/Decode single block", () => { + console.time("singleblock"); + const encoded = msgpack.encode(blocks[1], { codec: codec.blocks }); + const decoded = msgpack.decode(encoded, { codec: codec.blocks }); + + // removing helper property + delete decoded.previous_block_hex; + + expect(decoded).toEqual(blocks[1]); + console.timeEnd("singleblock"); + }); + + test("Encode/Decode blocks", () => { + console.time("blocks"); + for (const [index, block] of blocks.entries()) { + // TODO: skipping genesis for now - wrong id calculation + if (index === 0) { + continue; + } + + const encoded = msgpack.encode(block, { codec: codec.blocks }); + const decoded = msgpack.decode(encoded, { codec: codec.blocks }); + + // removing helper property + delete decoded.previous_block_hex; + + expect(block).toEqual(decoded); + } + console.timeEnd("blocks"); + }); + + test("Encode/Decode transactions - all types", () => { + console.time("transactions"); + for (const transaction of transactions) { + delete transaction.serializedHex; + + const encoded = msgpack.encode(transaction, { codec: codec.transactions }); + const decoded = msgpack.decode(encoded, { codec: codec.transactions }); + + expect(decoded).toEqual(transaction); + } + console.timeEnd("transactions"); + }); + + test("Encode/Decode transfer transactions", () => { + console.time("transactions lite transfer"); + const transferTransactions = transactions.filter(trx => trx.type === 0); + for (let i = 0; i < 100; i++) { + for (const transaction of transferTransactions) { + const encoded = msgpack.encode(transaction, { + codec: codec.transactions, + }); + const decoded = msgpack.decode(encoded, { codec: codec.transactions }); + + expect(decoded).toEqual(transaction); + } + } + console.timeEnd("transactions lite transfer"); + }); +}); diff --git a/packages/core-snapshots/jest.config.js b/packages/core-snapshots/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-snapshots/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-snapshots/lib/db/index.js b/packages/core-snapshots/lib/db/index.js deleted file mode 100644 index cefd17da4b..0000000000 --- a/packages/core-snapshots/lib/db/index.js +++ /dev/null @@ -1,153 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const promise = require('bluebird') -const { migrations } = require('@arkecosystem/core-database-postgres') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const queries = require('./queries') -const { rawQuery } = require('./utils') -const columns = require('./utils/column-set') - -class Database { - async make(database) { - if (database) { - this.db = database.db - this.pgp = database.pgp - this.__createColumnSets() - this.isSharedConnection = true - logger.info( - 'Snapshots: reusing core-database-postgres connection from running core', - ) - return this - } - - try { - const pgp = require('pg-promise')({ promiseLib: promise }) - this.pgp = pgp - const options = app.resolveOptions('database').connection - options.idleTimeoutMillis = 100 - this.db = pgp(options) - this.__createColumnSets() - await this.__runMigrations() - logger.info('Snapshots: Database connected') - this.isSharedConnection = false - return this - } catch (error) { - app.forceExit('Error while connecting to postgres', error) - } - } - - async getLastBlock() { - return this.db.oneOrNone(queries.blocks.latest) - } - - async getBlockByHeight(height) { - return this.db.oneOrNone(queries.blocks.findByHeight, { height }) - } - - async truncateChain() { - const tables = ['wallets', 'rounds', 'transactions', 'blocks'] - logger.info('Truncating tables: wallets, rounds, transactions, blocks') - try { - for (const table of tables) { - await this.db.none(queries.truncate(table)) - } - - return this.getLastBlock() - } catch (error) { - app.forceExit('Truncate chain error', error) - } - } - - async rollbackChain(height) { - const config = app.resolvePlugin('config') - const maxDelegates = config.getConstants(height).activeDelegates - const currentRound = Math.floor(height / maxDelegates) - const lastBlockHeight = currentRound * maxDelegates - const lastRemainingBlock = await this.getBlockByHeight(lastBlockHeight) - - try { - if (lastRemainingBlock) { - await Promise.all([ - this.db.none(queries.truncate('wallets')), - this.db.none(queries.transactions.deleteFromTimestamp, { - timestamp: lastRemainingBlock.timestamp, - }), - this.db.none(queries.blocks.deleteFromHeight, { - height: lastRemainingBlock.height, - }), - this.db.none(queries.rounds.deleteFromRound, { round: currentRound }), - ]) - } - } catch (error) { - logger.error(error) - } - - return this.getLastBlock() - } - - async getExportQueries(startHeight, endHeight) { - const startBlock = await this.getBlockByHeight(startHeight) - const endBlock = await this.getBlockByHeight(endHeight) - - if (!startBlock || !endBlock) { - app.forceExit( - 'Wrong input height parameters for building export queries. Blocks at height not found in db.', - ) - } - return { - blocks: rawQuery(this.pgp, queries.blocks.heightRange, { - start: startBlock.height, - end: endBlock.height, - }), - transactions: rawQuery(this.pgp, queries.transactions.timestampRange, { - start: startBlock.timestamp, - end: endBlock.timestamp, - }), - } - } - - getTransactionsBackupQuery(startTimestamp) { - return rawQuery(this.pgp, queries.transactions.timestampHigher, { - start: startTimestamp, - }) - } - - getColumnSet(tableName) { - switch (tableName) { - case 'blocks': - return this.blocksColumnSet - case 'transactions': - return this.transactionsColumnSet - default: - throw new Error('Invalid table name') - } - } - - close() { - if (!this.isSharedConnection) { - logger.debug('Closing snapshots-cli database connection') - this.db.$pool.end() - this.pgp.end() - } - } - - __createColumnSets() { - this.blocksColumnSet = new this.pgp.helpers.ColumnSet(columns.blocks, { - table: 'blocks', - }) - this.transactionsColumnSet = new this.pgp.helpers.ColumnSet( - columns.transactions, - { table: 'transactions' }, - ) - } - - async __runMigrations() { - for (const migration of migrations) { - await this.db.none(migration) - } - } -} - -module.exports = new Database() diff --git a/packages/core-snapshots/lib/db/queries/index.js b/packages/core-snapshots/lib/db/queries/index.js deleted file mode 100644 index 32c8c718ac..0000000000 --- a/packages/core-snapshots/lib/db/queries/index.js +++ /dev/null @@ -1,31 +0,0 @@ -const { loadQueryFile } = require('../utils') - -module.exports = { - blocks: { - heightRange: loadQueryFile(__dirname, './blocks/height-range.sql'), - latest: loadQueryFile(__dirname, './blocks/latest.sql'), - findByHeight: loadQueryFile(__dirname, './blocks/find-by-height.sql'), - deleteFromHeight: loadQueryFile( - __dirname, - './blocks/delete-from-height.sql', - ), - }, - transactions: { - timestampRange: loadQueryFile( - __dirname, - './transactions/timestamp-range.sql', - ), - timestampHigher: loadQueryFile( - __dirname, - './transactions/timestamp-higher.sql', - ), - deleteFromTimestamp: loadQueryFile( - __dirname, - './transactions/delete-from-timestamp.sql', - ), - }, - rounds: { - deleteFromRound: loadQueryFile(__dirname, './rounds/delete-from-round.sql'), - }, - truncate: table => `TRUNCATE TABLE ${table} RESTART IDENTITY`, -} diff --git a/packages/core-snapshots/lib/db/utils/column-set.js b/packages/core-snapshots/lib/db/utils/column-set.js deleted file mode 100644 index a97d0ece8d..0000000000 --- a/packages/core-snapshots/lib/db/utils/column-set.js +++ /dev/null @@ -1,34 +0,0 @@ -/* Column sets. - * If you modify the order you must adapt the sql files export orders also - */ -module.exports = { - blocks: [ - 'id', - 'version', - 'timestamp', - 'previous_block', - 'height', - 'number_of_transactions', - 'total_amount', - 'total_fee', - 'reward', - 'payload_length', - 'payload_hash', - 'generator_public_key', - 'block_signature', - ], - transactions: [ - 'id', - 'version', - 'block_id', - 'sequence', - 'timestamp', - 'sender_public_key', - 'recipient_id', - 'type', - 'vendor_field_hex', - 'amount', - 'fee', - 'serialized', - ], -} diff --git a/packages/core-snapshots/lib/db/utils/index.js b/packages/core-snapshots/lib/db/utils/index.js deleted file mode 100644 index 914fc8e62a..0000000000 --- a/packages/core-snapshots/lib/db/utils/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const QueryFile = require('pg-promise').QueryFile -const path = require('path') - -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') - -module.exports = { - loadQueryFile: (directory, file) => { - const fullPath = path.join(directory, file) - - const options = { - minify: true, - params: { - schema: 'public', - }, - } - - const query = new QueryFile(fullPath, options) - - if (query.error) { - logger.error(query.error) - } - - return query - }, - - rawQuery: (pgp, queryFile, parameters) => pgp.as.format(queryFile, parameters), -} diff --git a/packages/core-snapshots/lib/defaults.js b/packages/core-snapshots/lib/defaults.js deleted file mode 100644 index 2487bacbf5..0000000000 --- a/packages/core-snapshots/lib/defaults.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - codec: 'lite', - chunkSize: 50000, -} diff --git a/packages/core-snapshots/lib/index.js b/packages/core-snapshots/lib/index.js deleted file mode 100644 index e15ccc6638..0000000000 --- a/packages/core-snapshots/lib/index.js +++ /dev/null @@ -1,16 +0,0 @@ -const SnapshotManager = require('./manager') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'snapshots', - async register(container, options) { - const manager = new SnapshotManager(options) - - return manager.make(container.resolvePlugin('database')) - }, -} diff --git a/packages/core-snapshots/lib/manager.js b/packages/core-snapshots/lib/manager.js deleted file mode 100644 index e2efd62c2f..0000000000 --- a/packages/core-snapshots/lib/manager.js +++ /dev/null @@ -1,184 +0,0 @@ -/* eslint max-len: "off" */ - -const pick = require('lodash/pick') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const database = require('./db') -const utils = require('./utils') -const { - exportTable, - importTable, - verifyTable, - backupTransactionsToJSON, -} = require('./transport') - -module.exports = class SnapshotManager { - constructor(options) { - this.options = options - } - - async make(connection) { - this.database = await database.make(connection) - - return this - } - - async exportData(options) { - const params = await this.__init(options, true) - - if (params.skipExportWhenNoChange) { - logger.info( - `Skipping export of snapshot, because ${ - params.meta.folder - } is already up to date.`, - ) - return - } - - const metaInfo = { - blocks: await exportTable('blocks', params), - transactions: await exportTable('transactions', params), - folder: params.meta.folder, - codec: options.codec, - skipCompression: params.meta.skipCompression, - } - - this.database.close() - utils.writeMetaFile(metaInfo) - } - - async importData(options) { - const params = await this.__init(options) - - if (params.truncate) { - params.lastBlock = await this.database.truncateChain() - } - - await importTable('blocks', params) - await importTable('transactions', params) - - const lastBlock = await this.database.getLastBlock() - logger.info( - `Import from folder ${ - params.meta.folder - } completed. Last block in database: ${lastBlock.height.toLocaleString()} :+1:`, - ) - if (!params.skipRestartRound) { - const newLastBlock = await this.database.rollbackChain(lastBlock.height) - logger.info( - `Rolling back chain to last finished round with last block height ${ - newLastBlock.height.toLocaleString() - }`, - ) - } - - this.database.close() - } - - async verifyData(options) { - const params = await this.__init(options) - - await Promise.all([ - verifyTable('blocks', params), - verifyTable('transactions', params), - ]) - } - - async truncateChain() { - await this.database.truncateChain() - - this.database.close() - } - - async rollbackChain(height) { - const lastBlock = await this.database.getLastBlock() - const config = app.resolvePlugin('config') - const maxDelegates = config.getConstants(lastBlock.height).activeDelegates - - const rollBackHeight = height === -1 ? lastBlock.height : height - if (rollBackHeight >= lastBlock.height || rollBackHeight < 1) { - app.forceExit( - `Specified rollback block height: ${ - rollBackHeight.toLocaleString() - } is not valid. Current database height: ${ - lastBlock.height.toLocaleString() - }. Exiting.`, - ) - } - - if (height) { - const rollBackBlock = await this.database.getBlockByHeight(rollBackHeight) - const qTransactionBackup = await this.database.getTransactionsBackupQuery( - rollBackBlock.timestamp, - ) - await backupTransactionsToJSON( - `rollbackTransactionBackup.${+height + 1}.${lastBlock.height}.json`, - qTransactionBackup, - this.database, - ) - } - - const newLastBlock = await this.database.rollbackChain(rollBackHeight) - logger.info( - `Rolling back chain to last finished round ${ - (newLastBlock.height / maxDelegates).toLocaleString() - } with last block height ${newLastBlock.height.toLocaleString()}`, - ) - - this.database.close() - } - - /** - * Inits the process and creates json with needed paramaters for functions - * @param {JSONObject} from commander or util function {blocks, codec, truncate, signatureVerify, skipRestartRound, start, end} - * @return {JSONObject} with merged parameters, adding {lastBlock, database, meta {startHeight, endHeight, folder}, queries {blocks, transactions}} - */ - async __init(options, exportAction = false) { - const params = pick(options, [ - 'truncate', - 'signatureVerify', - 'blocks', - 'codec', - 'skipRestartRound', - 'start', - 'end', - 'skipCompression', - ]) - - const lastBlock = await this.database.getLastBlock() - params.lastBlock = lastBlock - params.codec = params.codec || this.options.codec - params.chunkSize = this.options.chunkSize || 50000 - - if (exportAction) { - if (!lastBlock) { - app.forceExit('Database is empty. Export not possible.') - } - params.meta = utils.setSnapshotInfo(params, lastBlock) - params.queries = await this.database.getExportQueries( - params.meta.startHeight, - params.meta.endHeight, - ) - - if (params.blocks) { - if (options.blocks === params.meta.folder) { - params.skipExportWhenNoChange = true - return params - } - const sourceSnapshotParams = utils.readMetaJSON(params.blocks) - params.meta.skipCompression = sourceSnapshotParams.skipCompression - params.meta.startHeight = sourceSnapshotParams.blocks.startHeight - utils.copySnapshot(options.blocks, params.meta.folder, params.codec) - } - } else { - params.meta = utils.getSnapshotInfo(options.blocks) - } - if (options.trace) { - console.info(params.meta) - console.info(params.queries) - } - params.database = this.database - return params - } -} diff --git a/packages/core-snapshots/lib/transport/codec/ark-codec.js b/packages/core-snapshots/lib/transport/codec/ark-codec.js deleted file mode 100644 index 4a6e6ca312..0000000000 --- a/packages/core-snapshots/lib/transport/codec/ark-codec.js +++ /dev/null @@ -1,22 +0,0 @@ -const msgpack = require('msgpack-lite') -const arkEncoders = require('./ark') - -class ArkCodec { - get blocks() { - const codec = msgpack.createCodec() - codec.addExtPacker(0x3f, Object, arkEncoders.blockEncode) - codec.addExtUnpacker(0x3f, arkEncoders.blockDecode) - - return codec - } - - get transactions() { - const codec = msgpack.createCodec() - codec.addExtPacker(0x4f, Object, arkEncoders.transactionEncode) - codec.addExtUnpacker(0x4f, arkEncoders.transactionDecode) - - return codec - } -} - -module.exports = new ArkCodec() diff --git a/packages/core-snapshots/lib/transport/codec/ark/index.js b/packages/core-snapshots/lib/transport/codec/ark/index.js deleted file mode 100644 index c7fbfeeccb..0000000000 --- a/packages/core-snapshots/lib/transport/codec/ark/index.js +++ /dev/null @@ -1,53 +0,0 @@ -/* eslint camelcase: "off" */ - -const { camelizeKeys, decamelizeKeys } = require('xcase') -const msgpack = require('msgpack-lite') -const { Block, Transaction } = require('@arkecosystem/crypto').models - -module.exports = { - blockEncode: blockRecord => { - const data = camelizeKeys(blockRecord) - return Block.serialize(data, true) - }, - - blockDecode: bufferData => { - const blockData = Block.deserialize(bufferData.toString('hex'), true) - blockData.id = Block.getIdFromSerialized(bufferData) - - blockData.totalAmount = blockData.totalAmount.toFixed() - blockData.totalFee = blockData.totalFee.toFixed() - blockData.reward = blockData.reward.toFixed() - - return decamelizeKeys(blockData) - }, - - transactionEncode: transaction => - msgpack.encode([ - transaction.id, - transaction.block_id, - transaction.sequence, - transaction.serialized, - ]), - - transactionDecode: bufferData => { - const [id, blockId, sequence, serialized] = msgpack.decode(bufferData) - let transaction = {} - transaction = Transaction.deserialize(serialized.toString('hex')) - - transaction.id = id - transaction.block_id = blockId - transaction.sequence = sequence - transaction.amount = transaction.amount.toFixed() - transaction.fee = transaction.fee.toFixed() - transaction.vendorFieldHex = transaction.vendorFieldHex - ? transaction.vendorFieldHex - : null - transaction.recipientId = transaction.recipientId - ? transaction.recipientId - : null - transaction = decamelizeKeys(transaction) - - transaction.serialized = serialized - return transaction - }, -} diff --git a/packages/core-snapshots/lib/transport/codec/index.js b/packages/core-snapshots/lib/transport/codec/index.js deleted file mode 100644 index e309b3fc00..0000000000 --- a/packages/core-snapshots/lib/transport/codec/index.js +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - get(codec) { - switch (codec) { - case 'ark': - return require('./ark-codec') - case 'lite': - return require('./lite-codec') - case 'msgpack': - return null - default: - return require('./lite-codec') - } - }, -} diff --git a/packages/core-snapshots/lib/transport/codec/lite-codec.js b/packages/core-snapshots/lib/transport/codec/lite-codec.js deleted file mode 100644 index b9b909b3d5..0000000000 --- a/packages/core-snapshots/lib/transport/codec/lite-codec.js +++ /dev/null @@ -1,22 +0,0 @@ -const msgpack = require('msgpack-lite') -const liteEncoder = require('./lite') - -class LiteCodec { - get blocks() { - const codec = msgpack.createCodec() - codec.addExtPacker(0x3f, Object, liteEncoder.blockEncode) - codec.addExtUnpacker(0x3f, liteEncoder.blockDecode) - - return codec - } - - get transactions() { - const codec = msgpack.createCodec() - codec.addExtPacker(0x4f, Object, liteEncoder.transactionEncode) - codec.addExtUnpacker(0x4f, liteEncoder.transactionDecode) - - return codec - } -} - -module.exports = new LiteCodec() diff --git a/packages/core-snapshots/lib/transport/codec/lite/index.js b/packages/core-snapshots/lib/transport/codec/lite/index.js deleted file mode 100644 index 77663b1560..0000000000 --- a/packages/core-snapshots/lib/transport/codec/lite/index.js +++ /dev/null @@ -1,32 +0,0 @@ -const msgpack = require('msgpack-lite') -const columns = require('../../../db/utils/column-set') - -module.exports = { - blockEncode: block => { - const values = Object.values(block) - return msgpack.encode(values) - }, - - blockDecode: bufferData => { - const values = msgpack.decode(bufferData) - const block = {} - columns.blocks.forEach((column, i) => { - block[column] = values[i] - }) - return block - }, - - transactionEncode: transactionRecord => { - const values = Object.values(transactionRecord) - return msgpack.encode(values) - }, - - transactionDecode: bufferData => { - const values = msgpack.decode(bufferData) - const transaction = {} - columns.transactions.forEach((column, i) => { - transaction[column] = values[i] - }) - return transaction - }, -} diff --git a/packages/core-snapshots/lib/transport/index.js b/packages/core-snapshots/lib/transport/index.js deleted file mode 100644 index 623d5d3c45..0000000000 --- a/packages/core-snapshots/lib/transport/index.js +++ /dev/null @@ -1,193 +0,0 @@ -/* eslint max-len: "off" */ - -const fs = require('fs-extra') -const QueryStream = require('pg-query-stream') -const JSONStream = require('JSONStream') -const msgpack = require('msgpack-lite') -const pluralize = require('pluralize') -const zlib = require('zlib') - -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const emitter = app.resolvePlugin('event-emitter') -const utils = require('../utils') -const { verifyData, canImportRecord } = require('./verification') -const codecs = require('./codec') - -module.exports = { - exportTable: async (table, options) => { - const snapFileName = utils.getPath( - table, - options.meta.folder, - options.codec, - ) - const codec = codecs.get(options.codec) - const gzip = zlib.createGzip() - await fs.ensureFile(snapFileName) - - logger.info( - `Starting to export table ${table} to folder ${ - options.meta.folder - }, codec: ${ - options.codec - }, append:${!!options.blocks}, skipCompression: ${ - options.meta.skipCompression - }`, - ) - try { - const snapshotWriteStream = fs.createWriteStream( - snapFileName, - options.blocks ? { flags: 'a' } : {}, - ) - const encodeStream = msgpack.createEncodeStream( - codec ? { codec: codec[table] } : {}, - ) - const qs = new QueryStream(options.queries[table]) - - const data = await options.database.db.stream(qs, s => { - if (options.meta.skipCompression) { - return s.pipe(encodeStream).pipe(snapshotWriteStream) - } - - return s - .pipe(encodeStream) - .pipe(gzip) - .pipe(snapshotWriteStream) - }) - logger.info( - `Snapshot: ${table} done. ==> Total rows processed: ${ - data.processed - }, duration: ${data.duration} ms`, - ) - - return { - count: utils.calcRecordCount(table, data.processed, options.blocks), - startHeight: utils.calcStartHeight( - table, - options.meta.startHeight, - options.blocks, - ), - endHeight: options.meta.endHeight, - } - } catch (error) { - app.forceExit('Error while exporting data via query stream', error) - } - }, - - importTable: async (table, options) => { - const sourceFile = utils.getPath(table, options.meta.folder, options.codec) - const codec = codecs.get(options.codec) - const gunzip = zlib.createGunzip() - const decodeStream = msgpack.createDecodeStream( - codec ? { codec: codec[table] } : {}, - ) - logger.info( - `Starting to import table ${table} from ${sourceFile}, codec: ${ - options.codec - }, skipCompression: ${options.meta.skipCompression}`, - ) - - const readStream = options.meta.skipCompression - ? fs.createReadStream(sourceFile).pipe(decodeStream) - : fs - .createReadStream(sourceFile) - .pipe(gunzip) - .pipe(decodeStream) - - let values = [] - let prevData = null - let counter = 0 - const saveData = async data => { - if (data && data.length > 0) { - const insert = options.database.pgp.helpers.insert( - data, - options.database.getColumnSet(table), - ) - emitter.emit('progress', { value: counter, table }) - values = [] - return options.database.db.none(insert) - } - } - - emitter.emit('start', { count: options.meta[table].count }) - for await (const record of readStream) { - counter++ - if (!verifyData(table, record, prevData, options.signatureVerification)) { - app.forceExit( - `Error verifying data. Payload ${JSON.stringify(record, null, 2)}`, - ) - } - if (canImportRecord(table, record, options.lastBlock)) { - values.push(record) - } - - if (values.length % options.chunkSize === 0) { - await saveData(values) - } - prevData = record - } - - if (values.length > 0) { - await saveData(values) - } - emitter.emit('complete') - }, - - verifyTable: async (table, options) => { - const sourceFile = utils.getPath(table, options.meta.folder, options.codec) - const codec = codecs.get(options.codec) - const gunzip = zlib.createGunzip() - const decodeStream = msgpack.createDecodeStream( - codec ? { codec: codec[table] } : {}, - ) - const readStream = options.meta.skipCompression - ? fs.createReadStream(sourceFile).pipe(decodeStream) - : fs - .createReadStream(sourceFile) - .pipe(gunzip) - .pipe(decodeStream) - - logger.info(`Starting to verify snapshot file ${sourceFile}`) - let prevData = null - - decodeStream.on('data', data => { - if (!verifyData(table, data, prevData, options.signatureVerification)) { - app.forceExit( - `Error verifying data. Payload ${JSON.stringify(data, null, 2)}`, - ) - } - prevData = data - }) - - readStream.on('finish', () => { - logger.info(`Snapshot file ${sourceFile} succesfully verified :+1:`) - }) - }, - - backupTransactionsToJSON: async (snapFileName, query, database) => { - const transactionBackupPath = utils.getFilePath( - snapFileName, - 'rollbackTransactions', - ) - await fs.ensureFile(transactionBackupPath) - const snapshotWriteStream = fs.createWriteStream(transactionBackupPath) - const qs = new QueryStream(query) - - try { - const data = await database.db.stream(qs, s => - s.pipe(JSONStream.stringify()).pipe(snapshotWriteStream), - ) - logger.info( - `${pluralize( - 'transaction', - data.processed, - true, - )} from rollbacked blocks safely exported to file ${snapFileName}`, - ) - return data - } catch (error) { - app.forceExit('Error while exporting data via query stream', error) - } - }, -} diff --git a/packages/core-snapshots/lib/transport/verification.js b/packages/core-snapshots/lib/transport/verification.js deleted file mode 100644 index 9e7159e7e1..0000000000 --- a/packages/core-snapshots/lib/transport/verification.js +++ /dev/null @@ -1,99 +0,0 @@ -const { camelizeKeys } = require('xcase') -const createHash = require('create-hash') -const { crypto } = require('@arkecosystem/crypto') -const { Block, Transaction } = require('@arkecosystem/crypto').models -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') - -module.exports = { - verifyData: (context, data, prevData, signatureVerification) => { - const verifyTransaction = () => { - if (!signatureVerification) { - return true - } - - const transaction = new Transaction( - Buffer.from(data.serialized).toString('hex'), - ) - return transaction.verified - } - - const isBlockChained = () => { - if (!prevData) { - return true - } - // genesis payload different as block.serialize stores - // block.previous_block with 00000 instead of null - // it fails on height 2 - chain check - // hardcoding for now - // TODO: check to improve ser/deser for genesis, add mainnet - if ( - data.height === 2 && - data.previous_block === '13114381566690093367' && - prevData.id === '12760288562212273414' - ) { - return true - } - return ( - data.height - prevData.height === 1 && - data.previous_block === prevData.id - ) - } - - const verifyBlock = () => { - if (!isBlockChained(data)) { - logger.error( - `Blocks are not chained. Current block: ${JSON.stringify( - data, - )}, previous block: ${JSON.stringify(prevData)}`, - ) - return false - } - - // TODO: manually calculate block ID and compare to existing - if (signatureVerification) { - const bytes = Block.serialize(camelizeKeys(data), false) - const hash = createHash('sha256') - .update(bytes) - .digest() - - const signatureVerify = crypto.verifyHash( - hash, - data.block_signature, - data.generator_public_key, - ) - if (!signatureVerify) { - logger.error(`Failed to verify signature: ${JSON.stringify(data)}`) - } - return signatureVerify - } - - return true - } - - switch (context) { - case 'blocks': - return verifyBlock() - case 'transactions': - return verifyTransaction() - default: - return false - } - }, - - canImportRecord: (context, data, lastBlock) => { - if (!lastBlock) { - // empty db - return true - } - switch (context) { - case 'blocks': - return data.height > lastBlock.height - case 'transactions': - return data.timestamp > lastBlock.timestamp - default: - return false - } - }, -} diff --git a/packages/core-snapshots/lib/utils/index.js b/packages/core-snapshots/lib/utils/index.js deleted file mode 100644 index 0e9e75987e..0000000000 --- a/packages/core-snapshots/lib/utils/index.js +++ /dev/null @@ -1,108 +0,0 @@ -const fs = require('fs-extra') -const app = require('@arkecosystem/core-container') - -exports.getPath = (table, folder, codec) => { - const filename = `${table}.${codec}` - return this.getFilePath(filename, folder) -} - -exports.writeMetaFile = snapshotInfo => { - const path = `${process.env.ARK_PATH_DATA}/snapshots/${ - process.env.ARK_NETWORK_NAME - }/${snapshotInfo.folder}/meta.json` - fs.writeFileSync(path, JSON.stringify(snapshotInfo, 'utf8')) -} - -exports.getFilePath = (filename, folder) => `${process.env.ARK_PATH_DATA}/snapshots/${ - process.env.ARK_NETWORK_NAME -}/${folder}/${filename}` - -exports.copySnapshot = (sourceFolder, destFolder, codec) => { - const logger = app.resolvePlugin('logger') - logger.info( - `Copying snapshot from ${sourceFolder} to a new file ${destFolder} for appending of data`, - ) - - const paths = { - source: { - blocks: this.getPath('blocks', sourceFolder, codec), - transactions: this.getPath('transactions', sourceFolder, codec), - }, - dest: { - blocks: this.getPath('blocks', destFolder, codec), - transactions: this.getPath('transactions', destFolder, codec), - }, - } - - fs.ensureFileSync(paths.dest.blocks) - fs.ensureFileSync(paths.dest.transactions) - - if ( - !fs.existsSync(paths.source.blocks) - || !fs.existsSync(paths.source.transactions) - ) { - app.forceExit( - `Unable to copy snapshot from ${sourceFolder} as it doesn't exist :bomb:`, - ) - } - - fs.copyFileSync(paths.source.blocks, paths.dest.blocks) - fs.copyFileSync(paths.source.transactions, paths.dest.transactions) -} - -exports.calcRecordCount = (table, currentCount, sourceFolder) => { - if (sourceFolder) { - const snapshotInfo = this.readMetaJSON(sourceFolder) - return +snapshotInfo[table].count + currentCount - } - - return currentCount -} - -exports.calcStartHeight = (table, currentHeight, sourceFolder) => { - if (sourceFolder) { - const snapshotInfo = this.readMetaJSON(sourceFolder) - return +snapshotInfo[table].startHeight - } - - return currentHeight -} - -exports.getSnapshotInfo = folder => { - const snapshotInfo = this.readMetaJSON(folder) - return { - startHeight: +snapshotInfo.blocks.startHeight, - endHeight: +snapshotInfo.blocks.endHeight, - folder: snapshotInfo.folder, - blocks: snapshotInfo.blocks, - transactions: snapshotInfo.transactions, - skipCompression: snapshotInfo.skipCompression, - } -} - -exports.readMetaJSON = folder => { - const metaFileInfo = this.getFilePath('meta.json', folder) - if (!fs.existsSync(metaFileInfo)) { - app.forceExit('Meta file meta.json not found. Exiting :bomb:') - } - - return fs.readJSONSync(metaFileInfo) -} - -exports.setSnapshotInfo = (options, lastBlock) => { - const meta = { - startHeight: options.start !== -1 ? options.start : 1, - endHeight: options.end !== -1 ? options.end : lastBlock.height, - codec: options.codec, - skipCompression: options.skipCompression || false, - } - meta.folder = `${meta.startHeight}-${meta.endHeight}` - - if (options.blocks) { - const oldMeta = this.getSnapshotInfo(options.blocks) - meta.startHeight = oldMeta.endHeight + 1 - meta.folder = `${oldMeta.startHeight}-${meta.endHeight}` - } - - return meta -} diff --git a/packages/core-snapshots/package.json b/packages/core-snapshots/package.json index 4de91324e6..ebba08e4a7 100644 --- a/packages/core-snapshots/package.json +++ b/packages/core-snapshots/package.json @@ -1,39 +1,65 @@ { - "name": "@arkecosystem/core-snapshots", - "description": "Provides live local streamed snapshots functionality for ARK Core", - "version": "0.1.0", - "contributors": [ - "Kristjan Košič " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand --watch", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-database-postgres": "~0.2", - "@arkecosystem/crypto": "~0.2", - "JSONStream": "^1.3.5", - "bluebird": "^3.5.3", - "create-hash": "^1.2.0", - "fs-extra": "^7.0.1", - "lodash.pick": "^4.4.0", - "msgpack-lite": "^0.1.26", - "pg-promise": "^8.5.2", - "pg-query-stream": "^1.1.2", - "pluralize": "^7.0.0", - "xcase": "^2.0.1" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-snapshots", + "description": "Provides live local streamed snapshots functionality for ARK Core", + "version": "2.1.0", + "contributors": [ + "Kristjan Košič " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn copy && yarn compile", + "build:watch": "yarn clean && yarn copy && yarn compile -w", + "clean": "del dist", + "copy": "cd src/ && cpy './**/*.sql' --parents ../dist/ && cd ../", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand --watch", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-database-postgres": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/bluebird": "^3.5.25", + "@types/create-hash": "^1.2.0", + "@types/fs-extra": "^5.0.4", + "@types/lodash.pick": "^4.4.4", + "@types/msgpack-lite": "^0.1.6", + "@types/pluralize": "^0.0.29", + "JSONStream": "^1.3.5", + "bluebird": "^3.5.3", + "cpy-cli": "^2.0.0", + "create-hash": "^1.2.0", + "fs-extra": "^7.0.1", + "lodash.pick": "^4.4.0", + "msgpack-lite": "^0.1.26", + "pg-promise": "^8.5.4", + "pg-query-stream": "^1.1.2", + "pluralize": "^7.0.0", + "xcase": "^2.0.1" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + }, + "devDependencies": { + "@types/pg-query-stream": "^1.0.2" + } } diff --git a/packages/core-snapshots/src/db/index.ts b/packages/core-snapshots/src/db/index.ts new file mode 100644 index 0000000000..029cf0223a --- /dev/null +++ b/packages/core-snapshots/src/db/index.ts @@ -0,0 +1,157 @@ +import { app } from "@arkecosystem/core-container"; +import { migrations, plugin, PostgresConnection } from "@arkecosystem/core-database-postgres"; +import { Logger } from "@arkecosystem/core-interfaces"; +import promise from "bluebird"; + +import { queries } from "./queries"; +import { rawQuery } from "./utils"; +import { columns } from "./utils/column-set"; + +const logger = app.resolvePlugin("logger"); + +class Database { + public db: any; + public pgp: any; + public isSharedConnection: boolean; + public blocksColumnSet: any; + public transactionsColumnSet: any; + + public async make(connection : PostgresConnection) { + if (connection) { + this.db = connection.db; + this.pgp = (connection as any).pgp; + this.__createColumnSets(); + this.isSharedConnection = true; + logger.info("Snapshots: reusing core-database-postgres connection from running core"); + return this; + } + + try { + const pgp = require("pg-promise")({ promiseLib: promise }); + this.pgp = pgp; + + const options: any = plugin.defaults.connection; + options.idleTimeoutMillis = 100; + + this.db = pgp(options); + this.__createColumnSets(); + await this.__runMigrations(); + logger.info("Snapshots: Database connected"); + this.isSharedConnection = false; + return this; + } catch (error) { + app.forceExit("Error while connecting to postgres", error); + return null; + } + } + + public async getLastBlock() { + return this.db.oneOrNone(queries.blocks.latest); + } + + public async getBlockByHeight(height) { + return this.db.oneOrNone(queries.blocks.findByHeight, { height }); + } + + public async truncateChain() { + const tables = ["wallets", "rounds", "transactions", "blocks"]; + logger.info("Truncating tables: wallets, rounds, transactions, blocks"); + try { + for (const table of tables) { + await this.db.none(queries.truncate(table)); + } + + return this.getLastBlock(); + } catch (error) { + app.forceExit("Truncate chain error", error); + } + } + + public async rollbackChain(height) { + const config = app.getConfig(); + const maxDelegates = config.getMilestone(height).activeDelegates; + const currentRound = Math.floor(height / maxDelegates); + const lastBlockHeight = currentRound * maxDelegates; + const lastRemainingBlock = await this.getBlockByHeight(lastBlockHeight); + + try { + if (lastRemainingBlock) { + await Promise.all([ + this.db.none(queries.truncate("wallets")), + this.db.none(queries.transactions.deleteFromTimestamp, { + timestamp: lastRemainingBlock.timestamp, + }), + this.db.none(queries.blocks.deleteFromHeight, { + height: lastRemainingBlock.height, + }), + this.db.none(queries.rounds.deleteFromRound, { round: currentRound }), + ]); + } + } catch (error) { + logger.error(error); + } + + return this.getLastBlock(); + } + + public async getExportQueries(startHeight, endHeight) { + const startBlock = await this.getBlockByHeight(startHeight); + const endBlock = await this.getBlockByHeight(endHeight); + + if (!startBlock || !endBlock) { + app.forceExit( + "Wrong input height parameters for building export queries. Blocks at height not found in db.", + ); + } + return { + blocks: rawQuery(this.pgp, queries.blocks.heightRange, { + start: startBlock.height, + end: endBlock.height, + }), + transactions: rawQuery(this.pgp, queries.transactions.timestampRange, { + start: startBlock.timestamp, + end: endBlock.timestamp, + }), + }; + } + + public getTransactionsBackupQuery(startTimestamp) { + return rawQuery(this.pgp, queries.transactions.timestampHigher, { + start: startTimestamp, + }); + } + + public getColumnSet(tableName) { + switch (tableName) { + case "blocks": + return this.blocksColumnSet; + case "transactions": + return this.transactionsColumnSet; + default: + throw new Error("Invalid table name"); + } + } + + public close() { + if (!this.isSharedConnection) { + logger.debug("Closing snapshots-cli database connection"); + this.db.$pool.end(); + this.pgp.end(); + } + } + + public __createColumnSets() { + this.blocksColumnSet = new this.pgp.helpers.ColumnSet(columns.blocks, { + table: "blocks", + }); + this.transactionsColumnSet = new this.pgp.helpers.ColumnSet(columns.transactions, { table: "transactions" }); + } + + public async __runMigrations() { + for (const migration of migrations) { + await this.db.none(migration); + } + } +} + +export const database = new Database(); diff --git a/packages/core-snapshots/lib/db/queries/blocks/delete-from-height.sql b/packages/core-snapshots/src/db/queries/blocks/delete-from-height.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/blocks/delete-from-height.sql rename to packages/core-snapshots/src/db/queries/blocks/delete-from-height.sql diff --git a/packages/core-snapshots/lib/db/queries/blocks/find-by-height.sql b/packages/core-snapshots/src/db/queries/blocks/find-by-height.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/blocks/find-by-height.sql rename to packages/core-snapshots/src/db/queries/blocks/find-by-height.sql diff --git a/packages/core-snapshots/lib/db/queries/blocks/height-range.sql b/packages/core-snapshots/src/db/queries/blocks/height-range.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/blocks/height-range.sql rename to packages/core-snapshots/src/db/queries/blocks/height-range.sql diff --git a/packages/core-snapshots/lib/db/queries/blocks/latest.sql b/packages/core-snapshots/src/db/queries/blocks/latest.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/blocks/latest.sql rename to packages/core-snapshots/src/db/queries/blocks/latest.sql diff --git a/packages/core-snapshots/src/db/queries/index.ts b/packages/core-snapshots/src/db/queries/index.ts new file mode 100644 index 0000000000..846bf8fbdd --- /dev/null +++ b/packages/core-snapshots/src/db/queries/index.ts @@ -0,0 +1,19 @@ +import { loadQueryFile } from "../utils"; + +export const queries = { + blocks: { + heightRange: loadQueryFile(__dirname, "./blocks/height-range.sql"), + latest: loadQueryFile(__dirname, "./blocks/latest.sql"), + findByHeight: loadQueryFile(__dirname, "./blocks/find-by-height.sql"), + deleteFromHeight: loadQueryFile(__dirname, "./blocks/delete-from-height.sql"), + }, + transactions: { + timestampRange: loadQueryFile(__dirname, "./transactions/timestamp-range.sql"), + timestampHigher: loadQueryFile(__dirname, "./transactions/timestamp-higher.sql"), + deleteFromTimestamp: loadQueryFile(__dirname, "./transactions/delete-from-timestamp.sql"), + }, + rounds: { + deleteFromRound: loadQueryFile(__dirname, "./rounds/delete-from-round.sql"), + }, + truncate: table => `TRUNCATE TABLE ${table} RESTART IDENTITY`, +}; diff --git a/packages/core-snapshots/lib/db/queries/rounds/delete-from-round.sql b/packages/core-snapshots/src/db/queries/rounds/delete-from-round.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/rounds/delete-from-round.sql rename to packages/core-snapshots/src/db/queries/rounds/delete-from-round.sql diff --git a/packages/core-snapshots/lib/db/queries/transactions/delete-from-timestamp.sql b/packages/core-snapshots/src/db/queries/transactions/delete-from-timestamp.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/transactions/delete-from-timestamp.sql rename to packages/core-snapshots/src/db/queries/transactions/delete-from-timestamp.sql diff --git a/packages/core-snapshots/lib/db/queries/transactions/timestamp-higher.sql b/packages/core-snapshots/src/db/queries/transactions/timestamp-higher.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/transactions/timestamp-higher.sql rename to packages/core-snapshots/src/db/queries/transactions/timestamp-higher.sql diff --git a/packages/core-snapshots/lib/db/queries/transactions/timestamp-range.sql b/packages/core-snapshots/src/db/queries/transactions/timestamp-range.sql similarity index 100% rename from packages/core-snapshots/lib/db/queries/transactions/timestamp-range.sql rename to packages/core-snapshots/src/db/queries/transactions/timestamp-range.sql diff --git a/packages/core-snapshots/src/db/utils/column-set.ts b/packages/core-snapshots/src/db/utils/column-set.ts new file mode 100644 index 0000000000..cf26b38590 --- /dev/null +++ b/packages/core-snapshots/src/db/utils/column-set.ts @@ -0,0 +1,34 @@ +/* Column sets. + * If you modify the order you must adapt the sql files export orders also + */ +export const columns = { + blocks: [ + "id", + "version", + "timestamp", + "previous_block", + "height", + "number_of_transactions", + "total_amount", + "total_fee", + "reward", + "payload_length", + "payload_hash", + "generator_public_key", + "block_signature", + ], + transactions: [ + "id", + "version", + "block_id", + "sequence", + "timestamp", + "sender_public_key", + "recipient_id", + "type", + "vendor_field_hex", + "amount", + "fee", + "serialized", + ], +}; diff --git a/packages/core-snapshots/src/db/utils/index.ts b/packages/core-snapshots/src/db/utils/index.ts new file mode 100644 index 0000000000..629042eb01 --- /dev/null +++ b/packages/core-snapshots/src/db/utils/index.ts @@ -0,0 +1,28 @@ +import { Logger } from "@arkecosystem/core-interfaces"; +import path from "path"; +import { QueryFile } from "pg-promise"; + +import { app } from "@arkecosystem/core-container"; + +const logger = app.resolvePlugin("logger"); + +export const loadQueryFile = (directory, file) => { + const fullPath = path.join(directory, file); + + const options = { + minify: true, + params: { + schema: "public", + }, + }; + + const query = new QueryFile(fullPath, options); + + if (query.error) { + logger.error(query.error.toString()); + } + + return query; +}; + +export const rawQuery = (pgp, queryFile, parameters) => pgp.as.format(queryFile, parameters); diff --git a/packages/core-snapshots/src/defaults.ts b/packages/core-snapshots/src/defaults.ts new file mode 100644 index 0000000000..a7d3c52f51 --- /dev/null +++ b/packages/core-snapshots/src/defaults.ts @@ -0,0 +1,4 @@ +export const defaults = { + codec: "lite", + chunkSize: 50000, +}; diff --git a/packages/core-snapshots/src/index.ts b/packages/core-snapshots/src/index.ts new file mode 100644 index 0000000000..7c4ef41bdd --- /dev/null +++ b/packages/core-snapshots/src/index.ts @@ -0,0 +1,3 @@ +export * from "./defaults"; +export * from "./manager"; +export * from "./plugin"; diff --git a/packages/core-snapshots/src/manager.ts b/packages/core-snapshots/src/manager.ts new file mode 100644 index 0000000000..0e872d2305 --- /dev/null +++ b/packages/core-snapshots/src/manager.ts @@ -0,0 +1,165 @@ +/* tslint:disable:max-line-length */ + +import { app } from "@arkecosystem/core-container"; +import { PostgresConnection } from "@arkecosystem/core-database-postgres"; +import { Logger } from "@arkecosystem/core-interfaces"; +import pick from "lodash/pick"; + +const logger = app.resolvePlugin("logger"); +import { database } from "./db"; +import * as utils from "./utils"; + +import { backupTransactionsToJSON, exportTable, importTable, verifyTable } from "./transport"; + +export class SnapshotManager { + public database: any; + constructor(readonly options) {} + + public async make(connection: PostgresConnection) { + this.database = await database.make(connection); + + return this; + } + + public async exportData(options) { + const params = await this.__init(options, true); + + if (params.skipExportWhenNoChange) { + logger.info(`Skipping export of snapshot, because ${params.meta.folder} is already up to date.`); + return; + } + + const metaInfo = { + blocks: await exportTable("blocks", params), + transactions: await exportTable("transactions", params), + folder: params.meta.folder, + codec: options.codec, + skipCompression: params.meta.skipCompression, + }; + + this.database.close(); + utils.writeMetaFile(metaInfo); + } + + public async importData(options) { + const params = await this.__init(options); + + if (params.truncate) { + params.lastBlock = await this.database.truncateChain(); + } + + await importTable("blocks", params); + await importTable("transactions", params); + + const lastBlock = await this.database.getLastBlock(); + logger.info( + `Import from folder ${ + params.meta.folder + } completed. Last block in database: ${lastBlock.height.toLocaleString()} :+1:`, + ); + if (!params.skipRestartRound) { + const newLastBlock = await this.database.rollbackChain(lastBlock.height); + logger.info( + `Rolling back chain to last finished round with last block height ${newLastBlock.height.toLocaleString()}`, + ); + } + + this.database.close(); + } + + public async verifyData(options) { + const params = await this.__init(options); + + await Promise.all([verifyTable("blocks", params), verifyTable("transactions", params)]); + } + + public async truncateChain() { + await this.database.truncateChain(); + + this.database.close(); + } + + public async rollbackChain(height) { + const lastBlock = await this.database.getLastBlock(); + const config = app.getConfig(); + const maxDelegates = config.getMilestone(lastBlock.height).activeDelegates; + + const rollBackHeight = height === -1 ? lastBlock.height : height; + if (rollBackHeight >= lastBlock.height || rollBackHeight < 1) { + app.forceExit( + `Specified rollback block height: ${rollBackHeight.toLocaleString()} is not valid. Current database height: ${lastBlock.height.toLocaleString()}. Exiting.`, + ); + } + + if (height) { + const rollBackBlock = await this.database.getBlockByHeight(rollBackHeight); + const qTransactionBackup = await this.database.getTransactionsBackupQuery(rollBackBlock.timestamp); + await backupTransactionsToJSON( + `rollbackTransactionBackup.${+height + 1}.${lastBlock.height}.json`, + qTransactionBackup, + this.database, + ); + } + + const newLastBlock = await this.database.rollbackChain(rollBackHeight); + logger.info( + `Rolling back chain to last finished round ${( + newLastBlock.height / maxDelegates + ).toLocaleString()} with last block height ${newLastBlock.height.toLocaleString()}`, + ); + + this.database.close(); + } + + /** + * Inits the process and creates json with needed paramaters for functions + * @param {JSONObject} from commander or util function {blocks, codec, truncate, signatureVerify, skipRestartRound, start, end} + * @return {JSONObject} with merged parameters, adding {lastBlock, database, meta {startHeight, endHeight, folder}, queries {blocks, transactions}} + */ + public async __init(options, exportAction = false) { + const params: any = pick(options, [ + "truncate", + "signatureVerify", + "blocks", + "codec", + "skipRestartRound", + "start", + "end", + "skipCompression", + ]); + + const lastBlock = await this.database.getLastBlock(); + params.lastBlock = lastBlock; + params.codec = params.codec || this.options.codec; + params.chunkSize = this.options.chunkSize || 50000; + + if (exportAction) { + if (!lastBlock) { + app.forceExit("Database is empty. Export not possible."); + } + params.meta = utils.setSnapshotInfo(params, lastBlock); + params.queries = await this.database.getExportQueries(params.meta.startHeight, params.meta.endHeight); + + if (params.blocks) { + if (options.blocks === params.meta.folder) { + params.skipExportWhenNoChange = true; + return params; + } + const sourceSnapshotParams = utils.readMetaJSON(params.blocks); + params.meta.skipCompression = sourceSnapshotParams.skipCompression; + params.meta.startHeight = sourceSnapshotParams.blocks.startHeight; + utils.copySnapshot(options.blocks, params.meta.folder, params.codec); + } + } else { + params.meta = utils.getSnapshotInfo(options.blocks); + } + if (options.trace) { + // tslint:disable-next-line:no-console + console.info(params.meta); + // tslint:disable-next-line:no-console + console.info(params.queries); + } + params.database = this.database; + return params; + } +} diff --git a/packages/core-snapshots/src/plugin.ts b/packages/core-snapshots/src/plugin.ts new file mode 100644 index 0000000000..4ab3891387 --- /dev/null +++ b/packages/core-snapshots/src/plugin.ts @@ -0,0 +1,24 @@ +import { PostgresConnection } from "@arkecosystem/core-database-postgres"; +import { Container, Database } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { SnapshotManager } from "./manager"; + +/** + * The struct used by the plugin container. + * @type {Object} + */ +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "snapshots", + async register(container: Container.IContainer, options) { + const manager = new SnapshotManager(options); + + const databaseService = container.resolvePlugin("database"); + if(!!databaseService) { + const connection = databaseService.connection as any; + return await manager.make(connection as PostgresConnection); + } + return await manager.make(null); + }, +}; diff --git a/packages/core-snapshots/src/transport/codecs/core-codec.ts b/packages/core-snapshots/src/transport/codecs/core-codec.ts new file mode 100644 index 0000000000..777b390eac --- /dev/null +++ b/packages/core-snapshots/src/transport/codecs/core-codec.ts @@ -0,0 +1,20 @@ +import msgpack from "msgpack-lite"; +import * as coreEncoders from "./core"; + +export class CoreCodec { + get blocks() { + const codec: any = msgpack.createCodec(); + codec.addExtPacker(0x3f, Object, coreEncoders.blockEncode); + codec.addExtUnpacker(0x3f, coreEncoders.blockDecode); + + return codec; + } + + get transactions() { + const codec: any = msgpack.createCodec(); + codec.addExtPacker(0x4f, Object, coreEncoders.transactionEncode); + codec.addExtUnpacker(0x4f, coreEncoders.transactionDecode); + + return codec; + } +} diff --git a/packages/core-snapshots/src/transport/codecs/core/index.ts b/packages/core-snapshots/src/transport/codecs/core/index.ts new file mode 100644 index 0000000000..4b3d6e82c8 --- /dev/null +++ b/packages/core-snapshots/src/transport/codecs/core/index.ts @@ -0,0 +1,41 @@ +import { Bignum, models } from "@arkecosystem/crypto"; +import msgpack from "msgpack-lite"; +import { camelizeKeys, decamelizeKeys } from "xcase"; +const { Block, Transaction } = models; + +export const blockEncode = blockRecord => { + const data = camelizeKeys(blockRecord); + return Block.serialize(data, true); +}; + +export const blockDecode = bufferData => { + const blockData = Block.deserialize(bufferData.toString("hex"), true); + blockData.id = Block.getIdFromSerialized(bufferData); + + blockData.totalAmount = (blockData.totalAmount as Bignum).toFixed(); + blockData.totalFee = (blockData.totalFee as Bignum).toFixed(); + blockData.reward = (blockData.reward as Bignum).toFixed(); + + return decamelizeKeys(blockData); +}; + +export const transactionEncode = transaction => + msgpack.encode([transaction.id, transaction.block_id, transaction.sequence, transaction.serialized]); + +export const transactionDecode = bufferData => { + const [id, blockId, sequence, serialized] = msgpack.decode(bufferData); + let transaction: any = {}; + transaction = Transaction.deserialize(serialized.toString("hex")); + + transaction.id = id; + transaction.block_id = blockId; + transaction.sequence = sequence; + transaction.amount = transaction.amount.toFixed(); + transaction.fee = transaction.fee.toFixed(); + transaction.vendorFieldHex = transaction.vendorFieldHex ? transaction.vendorFieldHex : null; + transaction.recipientId = transaction.recipientId ? transaction.recipientId : null; + transaction = decamelizeKeys(transaction); + + transaction.serialized = serialized; + return transaction; +}; diff --git a/packages/core-snapshots/src/transport/codecs/index.ts b/packages/core-snapshots/src/transport/codecs/index.ts new file mode 100644 index 0000000000..a7dcf56f5d --- /dev/null +++ b/packages/core-snapshots/src/transport/codecs/index.ts @@ -0,0 +1,15 @@ +import { CoreCodec } from "./core-codec"; +import { LiteCodec } from "./lite-codec"; + +export function getCodec(codec) { + switch (codec) { + case "core": + return new CoreCodec(); + case "lite": + return new LiteCodec(); + case "msgpack": + return null; + default: + return new LiteCodec(); + } +} diff --git a/packages/core-snapshots/src/transport/codecs/lite-codec.ts b/packages/core-snapshots/src/transport/codecs/lite-codec.ts new file mode 100644 index 0000000000..7d370f217e --- /dev/null +++ b/packages/core-snapshots/src/transport/codecs/lite-codec.ts @@ -0,0 +1,20 @@ +import msgpack from "msgpack-lite"; +import * as liteEncoder from "./lite"; + +export class LiteCodec { + get blocks() { + const codec = msgpack.createCodec(); + codec.addExtPacker(0x3f, Object, liteEncoder.blockEncode); + codec.addExtUnpacker(0x3f, liteEncoder.blockDecode); + + return codec; + } + + get transactions() { + const codec = msgpack.createCodec(); + codec.addExtPacker(0x4f, Object, liteEncoder.transactionEncode); + codec.addExtUnpacker(0x4f, liteEncoder.transactionDecode); + + return codec; + } +} diff --git a/packages/core-snapshots/src/transport/codecs/lite/index.ts b/packages/core-snapshots/src/transport/codecs/lite/index.ts new file mode 100644 index 0000000000..63739fa578 --- /dev/null +++ b/packages/core-snapshots/src/transport/codecs/lite/index.ts @@ -0,0 +1,30 @@ +import msgpack from "msgpack-lite"; +import { columns } from "../../../db/utils/column-set"; + +export const blockEncode = block => { + const values = Object.values(block); + return msgpack.encode(values); +}; + +export const blockDecode = bufferData => { + const values = msgpack.decode(bufferData); + const block = {}; + columns.blocks.forEach((column, i) => { + block[column] = values[i]; + }); + return block; +}; + +export const transactionEncode = transactionRecord => { + const values = Object.values(transactionRecord); + return msgpack.encode(values); +}; + +export const transactionDecode = bufferData => { + const values = msgpack.decode(bufferData); + const transaction = {}; + columns.transactions.forEach((column, i) => { + transaction[column] = values[i]; + }); + return transaction; +}; diff --git a/packages/core-snapshots/src/transport/index.ts b/packages/core-snapshots/src/transport/index.ts new file mode 100644 index 0000000000..8dca3e4d96 --- /dev/null +++ b/packages/core-snapshots/src/transport/index.ts @@ -0,0 +1,158 @@ +import fs from "fs-extra"; +import JSONStream from "JSONStream"; +import msgpack from "msgpack-lite"; +import QueryStream from "pg-query-stream"; +import pluralize from "pluralize"; +import zlib from "zlib"; + +import { app } from "@arkecosystem/core-container"; +import { EventEmitter, Logger } from "@arkecosystem/core-interfaces"; + +import * as utils from "../utils"; +import { getCodec } from "./codecs"; +import { canImportRecord, verifyData } from "./verification"; + +const logger = app.resolvePlugin("logger"); +const emitter = app.resolvePlugin("event-emitter"); + +export const exportTable = async (table, options) => { + const snapFileName = utils.getPath(table, options.meta.folder, options.codec); + const codec = getCodec(options.codec); + const gzip = zlib.createGzip(); + await fs.ensureFile(snapFileName); + + logger.info( + `Starting to export table ${table} to folder ${options.meta.folder}, codec: ${ + options.codec + }, append:${!!options.blocks}, skipCompression: ${options.meta.skipCompression}`, + ); + try { + const snapshotWriteStream = fs.createWriteStream(snapFileName, options.blocks ? { flags: "a" } : {}); + const encodeStream = msgpack.createEncodeStream(codec ? { codec: codec[table] } : {}); + const qs = new QueryStream(options.queries[table]); + + const data = await options.database.db.stream(qs, s => { + if (options.meta.skipCompression) { + return s.pipe(encodeStream).pipe(snapshotWriteStream); + } + + return s + .pipe(encodeStream) + .pipe(gzip) + .pipe(snapshotWriteStream); + }); + logger.info( + `Snapshot: ${table} done. ==> Total rows processed: ${data.processed}, duration: ${data.duration} ms`, + ); + + return { + count: utils.calcRecordCount(table, data.processed, options.blocks), + startHeight: utils.calcStartHeight(table, options.meta.startHeight, options.blocks), + endHeight: options.meta.endHeight, + }; + } catch (error) { + app.forceExit("Error while exporting data via query stream", error); + return null; + } +}; + +export const importTable = async (table, options) => { + const sourceFile = utils.getPath(table, options.meta.folder, options.codec); + const codec = getCodec(options.codec); + const gunzip = zlib.createGunzip(); + const decodeStream = msgpack.createDecodeStream(codec ? { codec: codec[table] } : {}); + logger.info( + `Starting to import table ${table} from ${sourceFile}, codec: ${options.codec}, skipCompression: ${ + options.meta.skipCompression + }`, + ); + + const readStream = options.meta.skipCompression + ? fs.createReadStream(sourceFile).pipe(decodeStream) + : fs + .createReadStream(sourceFile) + .pipe(gunzip) + .pipe(decodeStream); + + let values = []; + let prevData = null; + let counter = 0; + const saveData = async data => { + if (data && data.length > 0) { + const insert = options.database.pgp.helpers.insert(data, options.database.getColumnSet(table)); + emitter.emit("progress", { value: counter, table }); + values = []; + return options.database.db.none(insert); + } + }; + + emitter.emit("start", { count: options.meta[table].count }); + // @ts-ignore + for await (const record of readStream) { + counter++; + if (!verifyData(table, record, prevData, options.signatureVerification)) { + app.forceExit(`Error verifying data. Payload ${JSON.stringify(record, null, 2)}`); + } + if (canImportRecord(table, record, options.lastBlock)) { + values.push(record); + } + + if (values.length % options.chunkSize === 0) { + await saveData(values); + } + prevData = record; + } + + if (values.length > 0) { + await saveData(values); + } + emitter.emit("complete"); +}; + +export const verifyTable = async (table, options) => { + const sourceFile = utils.getPath(table, options.meta.folder, options.codec); + const codec = getCodec(options.codec); + const gunzip = zlib.createGunzip(); + const decodeStream = msgpack.createDecodeStream(codec ? { codec: codec[table] } : {}); + const readStream = options.meta.skipCompression + ? fs.createReadStream(sourceFile).pipe(decodeStream) + : fs + .createReadStream(sourceFile) + .pipe(gunzip) + .pipe(decodeStream); + + logger.info(`Starting to verify snapshot file ${sourceFile}`); + let prevData = null; + + decodeStream.on("data", data => { + if (!verifyData(table, data, prevData, options.signatureVerification)) { + app.forceExit(`Error verifying data. Payload ${JSON.stringify(data, null, 2)}`); + } + prevData = data; + }); + + readStream.on("finish", () => { + logger.info(`Snapshot file ${sourceFile} succesfully verified :+1:`); + }); +}; + +export const backupTransactionsToJSON = async (snapFileName, query, database) => { + const transactionBackupPath = utils.getFilePath(snapFileName, "rollbackTransactions"); + await fs.ensureFile(transactionBackupPath); + const snapshotWriteStream = fs.createWriteStream(transactionBackupPath); + const qs = new QueryStream(query); + + try { + const data = await database.db.stream(qs, s => s.pipe(JSONStream.stringify()).pipe(snapshotWriteStream)); + logger.info( + `${pluralize( + "transaction", + data.processed, + true, + )} from rollbacked blocks safely exported to file ${snapFileName}`, + ); + return data; + } catch (error) { + app.forceExit("Error while exporting data via query stream", error); + } +}; diff --git a/packages/core-snapshots/src/transport/verification.ts b/packages/core-snapshots/src/transport/verification.ts new file mode 100644 index 0000000000..d68e75c561 --- /dev/null +++ b/packages/core-snapshots/src/transport/verification.ts @@ -0,0 +1,89 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { crypto, models } from "@arkecosystem/crypto"; +import createHash from "create-hash"; +import { camelizeKeys } from "xcase"; + +const { Block, Transaction } = models; +const logger = app.resolvePlugin("logger"); + +export const verifyData = (context, data, prevData, signatureVerification) => { + const verifyTransaction = () => { + if (!signatureVerification) { + return true; + } + + const transaction = new Transaction(Buffer.from(data.serialized).toString("hex")); + return transaction.verified; + }; + + const isBlockChained = () => { + if (!prevData) { + return true; + } + // genesis payload different as block.serialize stores + // block.previous_block with 00000 instead of null + // it fails on height 2 - chain check + // hardcoding for now + // TODO: check to improve ser/deser for genesis, add mainnet + if ( + data.height === 2 && + data.previous_block === "13114381566690093367" && + prevData.id === "12760288562212273414" + ) { + return true; + } + return data.height - prevData.height === 1 && data.previous_block === prevData.id; + }; + + const verifyBlock = () => { + if (!isBlockChained()) { + logger.error( + `Blocks are not chained. Current block: ${JSON.stringify(data)}, previous block: ${JSON.stringify( + prevData, + )}`, + ); + return false; + } + + // TODO: manually calculate block ID and compare to existing + if (signatureVerification) { + const bytes: any = Block.serialize(camelizeKeys(data), false); + const hash = createHash("sha256") + .update(bytes) + .digest(); + + const signatureVerify = crypto.verifyHash(hash, data.block_signature, data.generator_public_key); + if (!signatureVerify) { + logger.error(`Failed to verify signature: ${JSON.stringify(data)}`); + } + return signatureVerify; + } + + return true; + }; + + switch (context) { + case "blocks": + return verifyBlock(); + case "transactions": + return verifyTransaction(); + default: + return false; + } +}; + +export const canImportRecord = (context, data, lastBlock) => { + if (!lastBlock) { + // empty db + return true; + } + switch (context) { + case "blocks": + return data.height > lastBlock.height; + case "transactions": + return data.timestamp > lastBlock.timestamp; + default: + return false; + } +}; diff --git a/packages/core-snapshots/src/utils/index.ts b/packages/core-snapshots/src/utils/index.ts new file mode 100644 index 0000000000..6b1559e74c --- /dev/null +++ b/packages/core-snapshots/src/utils/index.ts @@ -0,0 +1,100 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import fs from "fs-extra"; + +export const getPath = (table, folder, codec) => { + const filename = `${table}.${codec}`; + return this.getFilePath(filename, folder); +}; + +export const writeMetaFile = snapshotInfo => { + const path = `${process.env.CORE_PATH_DATA}/snapshots/${snapshotInfo.folder}/meta.json`; + fs.writeFileSync(path, JSON.stringify(snapshotInfo), "utf8"); +}; + +export const getFilePath = (filename, folder) => `${process.env.CORE_PATH_DATA}/snapshots/${folder}/${filename}`; + +export const copySnapshot = (sourceFolder, destFolder, codec) => { + const logger = app.resolvePlugin("logger"); + logger.info(`Copying snapshot from ${sourceFolder} to a new file ${destFolder} for appending of data`); + + const paths = { + source: { + blocks: this.getPath("blocks", sourceFolder, codec), + transactions: this.getPath("transactions", sourceFolder, codec), + }, + dest: { + blocks: this.getPath("blocks", destFolder, codec), + transactions: this.getPath("transactions", destFolder, codec), + }, + }; + + fs.ensureFileSync(paths.dest.blocks); + fs.ensureFileSync(paths.dest.transactions); + + if (!fs.existsSync(paths.source.blocks) || !fs.existsSync(paths.source.transactions)) { + app.forceExit(`Unable to copy snapshot from ${sourceFolder} as it doesn't exist :bomb:`); + } + + fs.copyFileSync(paths.source.blocks, paths.dest.blocks); + fs.copyFileSync(paths.source.transactions, paths.dest.transactions); +}; + +export const calcRecordCount = (table, currentCount, sourceFolder) => { + if (sourceFolder) { + const snapshotInfo = this.readMetaJSON(sourceFolder); + return +snapshotInfo[table].count + currentCount; + } + + return currentCount; +}; + +export const calcStartHeight = (table, currentHeight, sourceFolder) => { + if (sourceFolder) { + const snapshotInfo = this.readMetaJSON(sourceFolder); + return +snapshotInfo[table].startHeight; + } + + return currentHeight; +}; + +export const getSnapshotInfo = folder => { + const snapshotInfo = this.readMetaJSON(folder); + return { + startHeight: +snapshotInfo.blocks.startHeight, + endHeight: +snapshotInfo.blocks.endHeight, + folder: snapshotInfo.folder, + blocks: snapshotInfo.blocks, + transactions: snapshotInfo.transactions, + skipCompression: snapshotInfo.skipCompression, + }; +}; + +export const readMetaJSON = folder => { + const metaFileInfo = this.getFilePath("meta.json", folder); + if (!fs.existsSync(metaFileInfo)) { + app.forceExit("Meta file meta.json not found. Exiting :bomb:"); + } + + return fs.readJSONSync(metaFileInfo); +}; + +export const setSnapshotInfo = (options, lastBlock) => { + const meta = { + startHeight: options.start !== -1 ? options.start : 1, + endHeight: options.end !== -1 ? options.end : lastBlock.height, + codec: options.codec, + skipCompression: options.skipCompression || false, + folder: "", + }; + + meta.folder = `${meta.startHeight}-${meta.endHeight}`; + + if (options.blocks) { + const oldMeta = this.getSnapshotInfo(options.blocks); + meta.startHeight = oldMeta.endHeight + 1; + meta.folder = `${oldMeta.startHeight}-${meta.endHeight}`; + } + + return meta; +}; diff --git a/packages/core-snapshots/tsconfig.json b/packages/core-snapshots/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-snapshots/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-test-utils/CHANGELOG.md b/packages/core-test-utils/CHANGELOG.md deleted file mode 100644 index b3c465c0bf..0000000000 --- a/packages/core-test-utils/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Changed - -- Better default configuration -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-test-utils/LICENSE b/packages/core-test-utils/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-test-utils/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-test-utils/README.md b/packages/core-test-utils/README.md index 87bf721009..f485e09f7c 100644 --- a/packages/core-test-utils/README.md +++ b/packages/core-test-utils/README.md @@ -14,8 +14,10 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Erwann Gentric](https://github.com/air1one) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-test-utils/__tests__/generators/transactions.test.js b/packages/core-test-utils/__tests__/generators/transactions.test.js deleted file mode 100644 index d23ed06426..0000000000 --- a/packages/core-test-utils/__tests__/generators/transactions.test.js +++ /dev/null @@ -1,22 +0,0 @@ -const generateTransactions = require('../../lib/generators/transactions/transaction') -const { TRANSACTION_TYPES } = require('../../../crypto/lib/constants') - -describe('generateTransactions', () => { - it('should be a function', () => { - expect(generateTransactions).toBeFunction() - }) - - it('should create transfer transactions for devnet', () => { - const devnetAddress = 'DJQL8LWj81nRJNv9bbUgNXXELcB3q5qjZH' - const transactions = generateTransactions( - 'devnet', - TRANSACTION_TYPES.TRANSFER, - undefined, - devnetAddress, - ) - - for (let i = 0; i < transactions.length; i++) { - expect(transactions[i]).toMatchObject({ recipientId: devnetAddress }) - } - }) -}) diff --git a/packages/core-test-utils/__tests__/generators/transactions.test.ts b/packages/core-test-utils/__tests__/generators/transactions.test.ts new file mode 100644 index 0000000000..bfec0f01dd --- /dev/null +++ b/packages/core-test-utils/__tests__/generators/transactions.test.ts @@ -0,0 +1,15 @@ +import { constants } from "../../../crypto"; +import { generateTransaction } from "../../src/generators"; + +const { TransactionTypes } = constants; + +describe("generateTransactions", () => { + it("should create transfer transactions for devnet", () => { + const devnetAddress = "DJQL8LWj81nRJNv9bbUgNXXELcB3q5qjZH"; + const transactions = generateTransaction("devnet", TransactionTypes.Transfer, undefined, devnetAddress); + + for (const transaction of transactions) { + expect(transaction).toMatchObject({ recipientId: devnetAddress }); + } + }); +}); diff --git a/packages/core-test-utils/__tests__/generators/transactions/delegate.test.js b/packages/core-test-utils/__tests__/generators/transactions/delegate.test.js deleted file mode 100644 index eea6b56245..0000000000 --- a/packages/core-test-utils/__tests__/generators/transactions/delegate.test.js +++ /dev/null @@ -1,23 +0,0 @@ -const createDelegate = require('../../../lib/generators/transactions/delegate') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -describe('Delegate transaction', () => { - it('should be a function', () => { - expect(createDelegate).toBeFunction() - }) - - const quantity = 4 - const transactions = createDelegate(undefined, undefined, quantity) - - it('should return an array', () => { - expect(transactions).toBeArrayOfSize(quantity) - }) - - it('should return an array of 4 delegate objects', () => { - for (let i = 0; i < transactions.length; i++) { - expect(transactions[i]).toMatchObject({ - type: TRANSACTION_TYPES.DELEGATE_REGISTRATION, - }) - } - }) -}) diff --git a/packages/core-test-utils/__tests__/generators/transactions/delegate.test.ts b/packages/core-test-utils/__tests__/generators/transactions/delegate.test.ts new file mode 100644 index 0000000000..6ddb90a61c --- /dev/null +++ b/packages/core-test-utils/__tests__/generators/transactions/delegate.test.ts @@ -0,0 +1,21 @@ +import { constants } from "../../../../crypto"; +import { generateDelegateRegistration } from "../../../src/generators"; + +const { TransactionTypes } = constants; + +describe("Delegate transaction", () => { + const quantity = 4; + const transactions = generateDelegateRegistration(undefined, undefined, quantity); + + it("should return an array", () => { + expect(transactions).toBeArrayOfSize(quantity); + }); + + it("should return an array of 4 delegate objects", () => { + for (const transaction of transactions) { + expect(transaction).toMatchObject({ + type: TransactionTypes.DelegateRegistration, + }); + } + }); +}); diff --git a/packages/core-test-utils/__tests__/generators/transactions/signature.test.js b/packages/core-test-utils/__tests__/generators/transactions/signature.test.js deleted file mode 100644 index d01f3ce0b7..0000000000 --- a/packages/core-test-utils/__tests__/generators/transactions/signature.test.js +++ /dev/null @@ -1,23 +0,0 @@ -const createSignature = require('../../../lib/generators/transactions/signature') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -describe('Signature transaction', () => { - it('should be a function', () => { - expect(createSignature).toBeFunction() - }) - - const quantity = 4 - const transactions = createSignature(undefined, undefined, quantity) - - it('should return an array', () => { - expect(transactions).toBeArrayOfSize(quantity) - }) - - it('should return an array of 4 signature objects', () => { - for (let i = 0; i < transactions.length; i++) { - expect(transactions[i]).toMatchObject({ - type: TRANSACTION_TYPES.SECOND_SIGNATURE, - }) - } - }) -}) diff --git a/packages/core-test-utils/__tests__/generators/transactions/signature.test.ts b/packages/core-test-utils/__tests__/generators/transactions/signature.test.ts new file mode 100644 index 0000000000..87ae6c2e7b --- /dev/null +++ b/packages/core-test-utils/__tests__/generators/transactions/signature.test.ts @@ -0,0 +1,21 @@ +import { constants } from "../../../../crypto"; +import { generateSecondSignature } from "../../../src/generators"; + +const { TransactionTypes } = constants; + +describe("Signature transaction", () => { + const quantity = 4; + const transactions = generateSecondSignature(undefined, undefined, quantity); + + it("should return an array", () => { + expect(transactions).toBeArrayOfSize(quantity); + }); + + it("should return an array of 4 signature objects", () => { + for (const transaction of transactions) { + expect(transaction).toMatchObject({ + type: TransactionTypes.SecondSignature, + }); + } + }); +}); diff --git a/packages/core-test-utils/__tests__/generators/transactions/transfer.test.js b/packages/core-test-utils/__tests__/generators/transactions/transfer.test.js deleted file mode 100644 index ac35369fcb..0000000000 --- a/packages/core-test-utils/__tests__/generators/transactions/transfer.test.js +++ /dev/null @@ -1,39 +0,0 @@ -const { - Bignum, - constants: { ARKTOSHI, TRANSACTION_TYPES }, -} = require('@arkecosystem/crypto') -const createTransfer = require('../../../lib/generators/transactions/transfer') - -describe('Transfer transaction', () => { - it('should be a function', () => { - expect(createTransfer).toBeFunction() - }) - - const amount = new Bignum(20 * ARKTOSHI) - const quantity = 4 - const transactions = createTransfer( - undefined, - undefined, - undefined, - amount, - quantity, - ) - - it('should return an array', () => { - expect(transactions).toBeArrayOfSize(quantity) - }) - - it('should return an array of 4 transfer objects', () => { - for (let i = 0; i < transactions.length; i++) { - expect(transactions[i]).toMatchObject({ - type: TRANSACTION_TYPES.TRANSFER, - }) - } - }) - - it('should return an array sending 20 ark', () => { - for (let i = 0; i < transactions.length; i++) { - expect(transactions[i]).toMatchObject({ amount }) - } - }) -}) diff --git a/packages/core-test-utils/__tests__/generators/transactions/transfer.test.ts b/packages/core-test-utils/__tests__/generators/transactions/transfer.test.ts new file mode 100644 index 0000000000..d5b72d7a83 --- /dev/null +++ b/packages/core-test-utils/__tests__/generators/transactions/transfer.test.ts @@ -0,0 +1,28 @@ +import { Bignum, constants } from "../../../../crypto"; +import { generateTransfers } from "../../../src/generators"; + +const { TransactionTypes, ARKTOSHI } = constants; + +describe("Transfer transaction", () => { + const amount = new (Bignum as any)(20 * ARKTOSHI); + const quantity = 4; + const transactions = generateTransfers(undefined, undefined, undefined, amount, quantity); + + it("should return an array", () => { + expect(transactions).toBeArrayOfSize(quantity); + }); + + it("should return an array of 4 transfer objects", () => { + for (const transaction of transactions) { + expect(transaction).toMatchObject({ + type: TransactionTypes.Transfer, + }); + } + }); + + it("should return an array sending 20 ark", () => { + for (const transaction of transactions) { + expect(transaction).toMatchObject({ amount }); + } + }); +}); diff --git a/packages/core-test-utils/__tests__/generators/transactions/vote.test.js b/packages/core-test-utils/__tests__/generators/transactions/vote.test.js deleted file mode 100644 index 92b2d4a29b..0000000000 --- a/packages/core-test-utils/__tests__/generators/transactions/vote.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const createVote = require('../../../lib/generators/transactions/vote') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -describe('Vote transaction', () => { - it('should be a function', () => { - expect(createVote).toBeFunction() - }) - - const quantity = 4 - const transactions = createVote(undefined, undefined, undefined, quantity) - - it('should return an array', () => { - expect(transactions).toBeArrayOfSize(quantity) - }) - - it('should return an array of 4 vote objects', () => { - for (let i = 0; i < transactions.length; i++) { - expect(transactions[i]).toMatchObject({ type: TRANSACTION_TYPES.VOTE }) - } - }) -}) diff --git a/packages/core-test-utils/__tests__/generators/transactions/vote.test.ts b/packages/core-test-utils/__tests__/generators/transactions/vote.test.ts new file mode 100644 index 0000000000..6f6f59949f --- /dev/null +++ b/packages/core-test-utils/__tests__/generators/transactions/vote.test.ts @@ -0,0 +1,19 @@ +import { constants } from "../../../../crypto"; +import { generateVote } from "../../../src/generators"; + +const { TransactionTypes } = constants; + +describe("Vote transaction", () => { + const quantity = 4; + const transactions = generateVote(undefined, undefined, undefined, quantity); + + it("should return an array", () => { + expect(transactions).toBeArrayOfSize(quantity); + }); + + it("should return an array of 4 vote objects", () => { + for (const transaction of transactions) { + expect(transaction).toMatchObject({ type: TransactionTypes.Vote }); + } + }); +}); diff --git a/packages/core-test-utils/__tests__/matchers/blockchain/dispatch.test.js b/packages/core-test-utils/__tests__/matchers/blockchain/dispatch.test.js deleted file mode 100644 index 083ba1d9ad..0000000000 --- a/packages/core-test-utils/__tests__/matchers/blockchain/dispatch.test.js +++ /dev/null @@ -1,21 +0,0 @@ -require('../../../lib/matchers/blockchain/dispatch') - -describe('.toDispatch', () => { - const blockchain = { - dispatch(event) { - return event - }, - } - - test('passes when the dispatch method is called with the argument', () => { - expect(() => blockchain.dispatch('EVENT')).toDispatch(blockchain, 'EVENT') - }) - - test('fails when the dispatch method is not called with the argument', () => { - expect(() => {}).not.toDispatch(blockchain, 'FAKE-EVENT') - expect(() => blockchain.dispatch('OTHER-EVENT')).not.toDispatch( - blockchain, - 'EVENT', - ) - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/blockchain/execute-on-entry.test.js b/packages/core-test-utils/__tests__/matchers/blockchain/execute-on-entry.test.js deleted file mode 100644 index 2b2bfbc49f..0000000000 --- a/packages/core-test-utils/__tests__/matchers/blockchain/execute-on-entry.test.js +++ /dev/null @@ -1,35 +0,0 @@ -const Machine = require('xstate').Machine - -require('../../../lib/matchers/blockchain/execute-on-entry') - -describe('.toExecuteOnEntry', () => { - const machine = Machine({ - initial: 'a', - states: { - a: { - onEntry: ['action-a'], - on: { - START: 'b', - }, - }, - b: { - on: { - END: 'a', - }, - }, - }, - }) - - test('passes when the state machine executes all the actions when enters a state', () => { - expect(machine).toExecuteOnEntry({ state: 'a', actions: ['action-a'] }) - }) - - test('fails when the state machine does not execute all the actions when enters a state', () => { - expect(machine).not.toExecuteOnEntry({ state: 'a', actions: ['no-action'] }) - expect(machine).not.toExecuteOnEntry({ state: 'b', actions: ['action-a'] }) - expect(machine).not.toExecuteOnEntry({ - state: 'a', - actions: ['action-a', 'no-action'], - }) - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/blockchain/transition.test.js b/packages/core-test-utils/__tests__/matchers/blockchain/transition.test.js deleted file mode 100644 index 3b232152ae..0000000000 --- a/packages/core-test-utils/__tests__/matchers/blockchain/transition.test.js +++ /dev/null @@ -1,48 +0,0 @@ -const Machine = require('xstate').Machine - -require('../../../lib/matchers/blockchain/transition') - -describe('.toTransition', () => { - const machine = Machine({ - initial: 'a', - states: { - a: { - on: { - START: 'b', - SUB: 'c', - }, - }, - b: { - on: { - END: 'a', - }, - }, - c: { - on: { - END: 'a', - }, - initial: 'c-1', - states: { - 'c-1': { - on: { - BACK: 'b', - }, - }, - }, - }, - }, - }) - - test('passes when the state machine transitions from one state to other on an event', () => { - expect(machine).toTransition({ from: 'a', on: 'START', to: 'b' }) - }) - - test('passes when the state machine transitions from one state to other on an event, even sub-states', () => { - expect(machine).toTransition({ from: 'a', on: 'SUB', to: 'c' }) - }) - - test('fails when the state machine does not transition from one state to other on an event', () => { - expect(machine).not.toTransition({ from: 'a', on: 'FAKE', to: 'b' }) - expect(machine).not.toTransition({ from: 'a', on: 'END', to: 'b' }) - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/fields/address.test.js b/packages/core-test-utils/__tests__/matchers/fields/address.test.js deleted file mode 100644 index 1a50b11775..0000000000 --- a/packages/core-test-utils/__tests__/matchers/fields/address.test.js +++ /dev/null @@ -1,11 +0,0 @@ -require('../../../lib/matchers/fields/address') - -describe('.toBeArkAddress', () => { - test('passes when given a valid address', () => { - expect('DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN').toBeArkAddress() - }) - - test('fails when not given a valid address', () => { - expect('invalid-address').not.toBeArkAddress() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/fields/public-key.test.js b/packages/core-test-utils/__tests__/matchers/fields/public-key.test.js deleted file mode 100644 index d2d2187d4d..0000000000 --- a/packages/core-test-utils/__tests__/matchers/fields/public-key.test.js +++ /dev/null @@ -1,9 +0,0 @@ -require('../../../lib/matchers/fields/public-key') - -describe('.toBeArkPublicKey', () => { - test('passes when given a valid public key', () => { - expect( - '022cca9529ec97a772156c152a00aad155ee6708243e65c9d211a589cb5d43234d', - ).toBeArkPublicKey() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/models/delegate.test.js b/packages/core-test-utils/__tests__/matchers/models/delegate.test.js deleted file mode 100644 index 1f24e73a62..0000000000 --- a/packages/core-test-utils/__tests__/matchers/models/delegate.test.js +++ /dev/null @@ -1,18 +0,0 @@ -require('../../../lib/matchers/models/delegate') - -const delegate = { - username: 'arkxdev', - address: 'DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh', - publicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', -} - -describe('.toBeDelegate', () => { - test('passes when given a valid delegate', () => { - expect(delegate).toBeDelegate() - }) - - test('fails when given an invalid delegate', () => { - expect({ fake: 'news' }).not.toBeDelegate() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/models/transaction.test.js b/packages/core-test-utils/__tests__/matchers/models/transaction.test.js deleted file mode 100644 index 47904003e6..0000000000 --- a/packages/core-test-utils/__tests__/matchers/models/transaction.test.js +++ /dev/null @@ -1,29 +0,0 @@ -require('../../../lib/matchers/models/transaction') - -const transaction = { - version: 1, - network: 23, - type: 0, - timestamp: 35672738, - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - fee: 10000000, - vendorFieldHex: '5449443a2030', - amount: 200000000, - expiration: 0, - recipientId: 'AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5', - signature: - '304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619', - vendorField: 'TID: 0', - id: 'a5e9e6039675563959a783fa672c0ffe65369168a1ecffa3c89bf82961d8dbad', -} - -describe('.toBeTransaction', () => { - test('passes when given a valid transaction', () => { - expect(transaction).toBeTransaction() - }) - - test('fails when given an invalid transaction', () => { - expect({ fake: 'news' }).not.toBeTransaction() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/models/wallet.test.js b/packages/core-test-utils/__tests__/matchers/models/wallet.test.js deleted file mode 100644 index e5d62d951d..0000000000 --- a/packages/core-test-utils/__tests__/matchers/models/wallet.test.js +++ /dev/null @@ -1,17 +0,0 @@ -require('../../../lib/matchers/models/wallet') - -const wallet = { - address: 'DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh', - publicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', -} - -describe('.toBeWallet', () => { - test('passes when given a valid wallet', () => { - expect(wallet).toBeWallet() - }) - - test('fails when given an invalid wallet', () => { - expect({ fake: 'news' }).not.toBeWallet() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/delegate-resignation.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/delegate-resignation.test.js deleted file mode 100644 index efcff5d174..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/delegate-resignation.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { DELEGATE_RESIGNATION } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/delegate-resignation') - -describe('.toBeDelegateResignationType', () => { - test('passes when given a valid transaction', () => { - expect({ type: DELEGATE_RESIGNATION }).toBeDelegateResignationType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeDelegateResignationType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/delegate.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/delegate.test.js deleted file mode 100644 index f7197b342a..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/delegate.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { DELEGATE } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/delegate') - -describe('.toBeDelegateType', () => { - test('passes when given a valid transaction', () => { - expect({ type: DELEGATE }).toBeDelegateType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeDelegateType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/ipfs.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/ipfs.test.js deleted file mode 100644 index 18aa10968a..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/ipfs.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { IPFS } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/ipfs') - -describe('.toBeIpfsType', () => { - test('passes when given a valid transaction', () => { - expect({ type: IPFS }).toBeIpfsType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeIpfsType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/multi-payment.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/multi-payment.test.js deleted file mode 100644 index 4596198e0f..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/multi-payment.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { MULTI_PAYMENT } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/multi-payment') - -describe('.toBeMultiPaymentType', () => { - test('passes when given a valid transaction', () => { - expect({ type: MULTI_PAYMENT }).toBeMultiPaymentType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeMultiPaymentType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/multi-signature.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/multi-signature.test.js deleted file mode 100644 index 0a8de16b79..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/multi-signature.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { MULTI_SIGNATURE } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/multi-signature') - -describe('.toBeMultiSignatureType', () => { - test('passes when given a valid transaction', () => { - expect({ type: MULTI_SIGNATURE }).toBeMultiSignatureType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeMultiSignatureType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/second-signature.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/second-signature.test.js deleted file mode 100644 index d9311fb667..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/second-signature.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { SECOND_SIGNATURE } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/second-signature') - -describe('.toBeSecondSignatureType', () => { - test('passes when given a valid transaction', () => { - expect({ type: SECOND_SIGNATURE }).toBeSecondSignatureType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeSecondSignatureType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/timelock-transfer.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/timelock-transfer.test.js deleted file mode 100644 index 72b4c0808d..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/timelock-transfer.test.js +++ /dev/null @@ -1,15 +0,0 @@ -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/timelock-transfer') - -describe('.toBeTimelockTransferType', () => { - test('passes when given a valid transaction', () => { - expect({ - type: TRANSACTION_TYPES.TIMELOCK_TRANSFER, - }).toBeTimelockTransferType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeTimelockTransferType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/transfer.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/transfer.test.js deleted file mode 100644 index cc82600fb9..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/transfer.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { TRANSFER } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/transfer') - -describe('.toBeTransferType', () => { - test('passes when given a valid transaction', () => { - expect({ type: TRANSFER }).toBeTransferType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeTransferType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/types/vote.test.js b/packages/core-test-utils/__tests__/matchers/transactions/types/vote.test.js deleted file mode 100644 index 935b5f8914..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/types/vote.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const { vote } = require('@arkecosystem/crypto').constants - -require('../../../../lib/matchers/transactions/types/vote') - -describe('.toBeVoteType', () => { - test('passes when given a valid transaction', () => { - expect({ type: vote }).toBeVoteType() - }) - - test('fails when given an invalid transaction', () => { - expect({ type: 'invalid' }).not.toBeVoteType() - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/valid-second-signature.test.js b/packages/core-test-utils/__tests__/matchers/transactions/valid-second-signature.test.js deleted file mode 100644 index 508f6080f7..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/valid-second-signature.test.js +++ /dev/null @@ -1,26 +0,0 @@ -const { NetworkManager } = require('@arkecosystem/crypto') -const { Transaction } = require('@arkecosystem/crypto').models -const genTransfer = require('../../../lib/generators/transactions/transfer') -const genWallets = require('../../../lib/generators/wallets') - -require('../../../lib/matchers/transactions/valid-second-signature') - -const wallets = genWallets('testnet', 2) -const transaction = genTransfer('testnet', wallets.map(w => w.passphrase))[0] - -describe('.toHaveValidSecondSignature', () => { - test('passes when given a valid transaction', () => { - expect(transaction).toHaveValidSecondSignature({ - publicKey: wallets[1].publicKey, - }) - }) - - test('fails when given an invalid transaction', () => { - transaction.secondSignature = 'invalid' - transaction.signSignature = 'invalid' - - expect(transaction).not.toHaveValidSecondSignature({ - publicKey: wallets[1].publicKey, - }) - }) -}) diff --git a/packages/core-test-utils/__tests__/matchers/transactions/valid.test.js b/packages/core-test-utils/__tests__/matchers/transactions/valid.test.js deleted file mode 100644 index da97b83cd8..0000000000 --- a/packages/core-test-utils/__tests__/matchers/transactions/valid.test.js +++ /dev/null @@ -1,30 +0,0 @@ -require('../../../lib/matchers/transactions/valid') - -const transaction = { - version: 1, - network: 23, - type: 0, - timestamp: 35672738, - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - fee: 10000000, - vendorFieldHex: '5449443a2030', - amount: 200000000, - expiration: 0, - recipientId: 'AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5', - signature: - '304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619', - vendorField: 'TID: 0', - id: 'a5e9e6039675563959a783fa672c0ffe65369168a1ecffa3c89bf82961d8dbad', -} - -describe('.toBeValidTransaction', () => { - test('passes when given a valid transaction', () => { - expect(transaction).toBeValidTransaction() - }) - - test('fails when given an invalid transaction', () => { - transaction.fee = 'invalid' - expect(transaction).not.toBeValidTransaction() - }) -}) diff --git a/packages/core-test-utils/config/index.js b/packages/core-test-utils/config/index.js deleted file mode 100644 index e171379977..0000000000 --- a/packages/core-test-utils/config/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - passphrase: - 'prison tobacco acquire stone dignity palace note decade they current lesson robot', -} diff --git a/packages/core-test-utils/config/testnet/delegates.json b/packages/core-test-utils/config/testnet/delegates.json deleted file mode 100644 index 5af0f71488..0000000000 --- a/packages/core-test-utils/config/testnet/delegates.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "secrets": [ - "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", - "venue below waste gather spin cruise title still boost mother flash tuna", - "craft imitate step mixture patch forest volcano business charge around girl confirm", - "fatal hat sail asset chase barrel pluck bag approve coral slab bright", - "flash thank strike stove grain remove match reflect excess present beyond matrix", - "various present shine domain outdoor neck soup diesel limit express genuine tuna", - "hurdle pulse sheriff anchor two hope income pattern hazard bacon book night", - "glow boss party require silk interest pyramid marriage try wisdom snow grab", - "direct palace screen shuffle world fit produce rubber jelly gather river ordinary", - "wall ketchup shed word twist flip knock liar merge rural ill pond", - "measure blue volcano month orphan only cupboard found laugh peasant drama monitor", - "scissors sort pause medal target diesel reveal stock maze party gauge vacant", - "hand anchor hip pyramid taxi vote celery clap tribe damage shrimp brave", - "merge thunder detect stove else bottom favorite doll learn festival basic basic", - "educate attitude rely combine treat balcony west reopen coil west grab depth", - "advance silver advance squeeze load stone middle garden perfect invest field lounge", - "prison tobacco acquire stone dignity palace note decade they current lesson robot", - "team impact stadium year security steak harsh vacant fire pelican until olympic", - "walk intact ice prevent fit trial frog glory monkey once grunt gentle", - "same lens parrot suspect just sunset frown exercise lemon two mistake robust", - "skill insect issue crazy erase okay govern upgrade bounce dress motor athlete", - "peasant alert hard deposit naive follow page fiscal normal awful wedding history", - "resemble abandon same total oppose noise dune order fatal rhythm pink science", - "wide mesh ketchup acquire bright day mountain final below hamster scout drive", - "half weasel poet better rocket fan help left blade soda argue system", - "target sort neutral address language spike measure jaguar glance strong drop zone", - "race total stage trap wool believe twin pudding claim claim eternal miss", - "parade isolate wing vague magic husband acid skin skate path fence rib", - "neither fine dry priority example obtain bread reopen afford coyote milk minor", - "token atom lemon game charge area goose hotel excess endless spice oblige", - "pledge buffalo finish pipe mule popular bind clinic draft salon swamp purpose", - "west hat hold stand unique panther cable extend spell shaft injury reopen", - "van impulse pole install profit excuse give auction expire remain skate input", - "wrist maze potato april survey burden bamboo knee foot carry speak prison", - "three toddler copy owner pencil minimum doctor orange bottom ice detail design", - "ceiling warrior person thing whisper jeans black cricket drift ahead tornado typical", - "obvious mutual tone usual valve credit soccer mention also clown main box", - "valve slot soft green scale menu anxiety live drill legend upgrade chimney", - "twist comfort mule weather print oven cabin seek punch rival prepare sphere", - "say tumble glass argue aware service force caution until grocery hammer fetch", - "idea illegal empty frozen canvas arctic number poet rely track size obscure", - "chalk try large tower shed warfare blade clerk fame second charge tobacco", - "category nice verb fox start able brass climb boss luggage voice whale", - "favorite emotion trumpet visual welcome spend fine lock image review garage opera", - "waste axis humor auction next salmon much margin useful glimpse insect rotate", - "remember rose genuine police guard old flavor parent gain cross twelve first", - "coil tray elder mask circle crush anger electric harbor onion grab will", - "shove airport bus gather radio derive below horse canvas crime tribe adjust", - "retire lend burden cricket able sheriff output grocery empty scorpion flat inquiry", - "agree grain record shift fossil summer hunt mutual net vast behind pilot", - "decide rhythm oyster lady they merry betray jelly coyote solve episode then" - ] -} diff --git a/packages/core-test-utils/config/testnet/genesisBlock.json b/packages/core-test-utils/config/testnet/genesisBlock.json deleted file mode 100644 index 317cd8ed94..0000000000 --- a/packages/core-test-utils/config/testnet/genesisBlock.json +++ /dev/null @@ -1,2312 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", - "timestamp": 0, - "numberOfTransactions": 153, - "payloadLength": 35960, - "previousBlock": null, - "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", - "transactions": [ - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", - "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", - "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", - "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", - "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", - "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", - "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", - "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", - "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", - "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", - "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", - "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", - "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", - "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", - "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", - "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", - "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", - "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", - "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", - "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", - "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", - "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", - "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", - "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", - "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", - "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", - "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", - "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", - "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", - "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", - "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", - "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", - "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", - "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", - "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", - "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", - "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", - "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", - "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", - "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", - "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", - "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", - "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", - "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", - "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", - "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", - "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", - "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", - "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", - "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", - "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245100000000000, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", - "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - } - }, - "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", - "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - } - }, - "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", - "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - } - }, - "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", - "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - } - }, - "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", - "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - } - }, - "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", - "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - } - }, - "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", - "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - } - }, - "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", - "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - } - }, - "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", - "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - } - }, - "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", - "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - } - }, - "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", - "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - } - }, - "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", - "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - } - }, - "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", - "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - } - }, - "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", - "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - } - }, - "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", - "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - } - }, - "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", - "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - } - }, - "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", - "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - } - }, - "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", - "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - } - }, - "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", - "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - } - }, - "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", - "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - } - }, - "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", - "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - } - }, - "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", - "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - } - }, - "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", - "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - } - }, - "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", - "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - } - }, - "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", - "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - } - }, - "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", - "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - } - }, - "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", - "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - } - }, - "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", - "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - } - }, - "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", - "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - } - }, - "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", - "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - } - }, - "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", - "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - } - }, - "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", - "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - } - }, - "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", - "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - } - }, - "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", - "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - } - }, - "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", - "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - } - }, - "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", - "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - } - }, - "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", - "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - } - }, - "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", - "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - } - }, - "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", - "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - } - }, - "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", - "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - } - }, - "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", - "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - } - }, - "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", - "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - } - }, - "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", - "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - } - }, - "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", - "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - } - }, - "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", - "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - } - }, - "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", - "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - } - }, - "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", - "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - } - }, - "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", - "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - } - }, - "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", - "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - } - }, - "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", - "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - } - }, - "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", - "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - } - }, - "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", - "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "votes": [ - "+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - ] - }, - "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", - "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "votes": [ - "+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - ] - }, - "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", - "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "votes": [ - "+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - ] - }, - "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", - "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "votes": [ - "+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - ] - }, - "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", - "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "votes": [ - "+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - ] - }, - "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", - "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "votes": [ - "+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - ] - }, - "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", - "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "votes": [ - "+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - ] - }, - "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", - "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "votes": [ - "+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - ] - }, - "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", - "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "votes": [ - "+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - ] - }, - "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", - "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "votes": [ - "+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - ] - }, - "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", - "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "votes": [ - "+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - ] - }, - "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", - "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "votes": [ - "+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - ] - }, - "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", - "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "votes": [ - "+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - ] - }, - "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", - "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "votes": [ - "+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - ] - }, - "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", - "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "votes": [ - "+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - ] - }, - "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", - "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "votes": [ - "+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - ] - }, - "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", - "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "votes": [ - "+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - ] - }, - "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", - "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "votes": [ - "+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - ] - }, - "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", - "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "votes": [ - "+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - ] - }, - "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", - "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "votes": [ - "+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - ] - }, - "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", - "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "votes": [ - "+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - ] - }, - "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", - "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "votes": [ - "+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - ] - }, - "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", - "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "votes": [ - "+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - ] - }, - "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", - "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "votes": [ - "+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - ] - }, - "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", - "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "votes": [ - "+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - ] - }, - "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", - "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "votes": [ - "+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - ] - }, - "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", - "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "votes": [ - "+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - ] - }, - "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", - "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "votes": [ - "+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - ] - }, - "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", - "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "votes": [ - "+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - ] - }, - "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", - "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "votes": [ - "+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - ] - }, - "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", - "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "votes": [ - "+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - ] - }, - "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", - "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "votes": [ - "+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - ] - }, - "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", - "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "votes": [ - "+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - ] - }, - "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", - "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "votes": [ - "+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - ] - }, - "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", - "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "votes": [ - "+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - ] - }, - "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", - "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "votes": [ - "+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - ] - }, - "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", - "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "votes": [ - "+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - ] - }, - "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", - "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "votes": [ - "+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - ] - }, - "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", - "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "votes": [ - "+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - ] - }, - "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", - "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "votes": [ - "+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - ] - }, - "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", - "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "votes": [ - "+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - ] - }, - "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", - "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "votes": [ - "+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - ] - }, - "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", - "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "votes": [ - "+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - ] - }, - "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", - "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "votes": [ - "+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - ] - }, - "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", - "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "votes": [ - "+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - ] - }, - "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", - "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "votes": [ - "+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - ] - }, - "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", - "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "votes": [ - "+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - ] - }, - "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", - "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "votes": [ - "+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - ] - }, - "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", - "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "votes": [ - "+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - ] - }, - "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", - "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "votes": [ - "+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - ] - }, - "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", - "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "votes": [ - "+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - ] - }, - "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", - "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - } - ], - "height": 1, - "id": "17184958558311101492", - "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" -} diff --git a/packages/core-test-utils/config/testnet/peers.json b/packages/core-test-utils/config/testnet/peers.json deleted file mode 100644 index fe44230ea3..0000000000 --- a/packages/core-test-utils/config/testnet/peers.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "minimumVersion": ">=2.0.0", - "minimumNetworkReach": 5, - "globalTimeout": 5000, - "coldStart": 30, - "whiteList": [], - "blackList": [], - "list": [ - { - "ip": "0.0.0.99", - "port": 4000 - } - ] -} diff --git a/packages/core-test-utils/config/testnet/plugins.js b/packages/core-test-utils/config/testnet/plugins.js deleted file mode 100644 index 2dd60a3832..0000000000 --- a/packages/core-test-utils/config/testnet/plugins.js +++ /dev/null @@ -1,74 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: process.env.ARK_DB_DATABASE || 'ark_development', - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: !process.env.ARK_TRANSACTION_POOL_DISABLED, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - // 100+ years in the future to avoid our hardcoded transactions used in the - // tests to expire immediately - maxTransactionAge: 4036608000, - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4000, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: !process.env.ARK_API_DISABLED, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4003, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4005, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4000}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, -} diff --git a/packages/core-test-utils/fixtures/testnet/blocks.101-155.js b/packages/core-test-utils/fixtures/testnet/blocks.101-155.js deleted file mode 100644 index eed2da2090..0000000000 --- a/packages/core-test-utils/fixtures/testnet/blocks.101-155.js +++ /dev/null @@ -1,1047 +0,0 @@ -module.exports = [ - { - id: '16380709717848284005', - version: 0, - timestamp: 46584522, - height: 101, - reward: '0', - previousBlock: '6161515163793239359', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f', - blockSignature: - '304402204d1e50daa970881aa600a19e4f785fe3f74ffedede6134889c86bb7df5f3103a02206e623a242ffb5297ae09185f1f46cbcc9e49b0324bc0aac0bb30e4b0dff7b20f', - createdAt: '2018-09-11T17:08:42.241Z', - }, - { - id: '15490212522027991751', - version: 0, - timestamp: 46584530, - height: 102, - reward: '0', - previousBlock: '16380709717848284005', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2', - blockSignature: - '304402204ca739a7c99d035d01cb27a18e2989110196490f4d2e17293a6bc5f9d5240afd02200e49f9901aa1dc129866b98290958baf0fdbca41f3c9114f864228089859dffd', - createdAt: '2018-09-11T17:08:50.564Z', - }, - { - id: '7619316577889665171', - version: 0, - timestamp: 46584538, - height: 103, - reward: '0', - previousBlock: '15490212522027991751', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d', - blockSignature: - '3045022100cab8276fe2cebafefb6fcd0f895b9cadf5e38829d14ea8e0f1b1365e9481dc7202205395c196242699efd143548d9cf783607e7e4772bf572c3564ab8bb7c0a3e7cd', - createdAt: '2018-09-11T17:08:58.400Z', - }, - { - id: '14306710738176000705', - version: 0, - timestamp: 46584546, - height: 104, - reward: '0', - previousBlock: '7619316577889665171', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0', - blockSignature: - '304402202702687d7607151039ebddeb69715ec72effe5458a7f02004ee84124c8482b480220625a2d2fd5214d820464f960240e3f98b45174cab5c2d043025c1d61509f8e38', - createdAt: '2018-09-11T17:09:06.414Z', - }, - { - id: '16285816300440069381', - version: 0, - timestamp: 46584554, - height: 105, - reward: '0', - previousBlock: '14306710738176000705', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb', - blockSignature: - '304402202b45aaa43e3b0801389f4ed4bca834ef83d7f1160a25d7063bb5434fa07a1384022054e14371cfcb448dd46cecd84981b33a35a9aca892f4aab4c8495c6ffbbaeaf6', - createdAt: '2018-09-11T17:09:14.346Z', - }, - { - id: '16505099800747927529', - version: 0, - timestamp: 46584562, - height: 106, - reward: '0', - previousBlock: '16285816300440069381', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252', - blockSignature: - '3045022100b1d9bd1a77eb5d9ddd94a45b5f83168438d6f09ab7da068eeaee457aed79e6c502207a5423a3c7e3cae99d4b656e7c62ac2672030fde9ccd618c3ccc2388c203bdcd', - createdAt: '2018-09-11T17:09:22.407Z', - }, - { - id: '16506832204032304009', - version: 0, - timestamp: 46584570, - height: 107, - reward: '0', - previousBlock: '16505099800747927529', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95', - blockSignature: - '3045022100f25903940fae22d28d95c7232d5220ef5ce599abdd773bea1b6942e1c331f0b50220726a691c87a99645fc3eb756a3914aa2b0cd36cf48c48f76f2f266132e3d3dff', - createdAt: '2018-09-11T17:09:30.531Z', - }, - { - id: '9467089070361350584', - version: 0, - timestamp: 46584578, - height: 108, - reward: '0', - previousBlock: '16506832204032304009', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2', - blockSignature: - '304402205d1b9fe34514316e70682743dd35e328d3b8424747e3951b93f6c7373442b67c022064fce5e5569a7a7a8a4cb7f914c5eb217bd71e9254a5934f05802705d3b0205f', - createdAt: '2018-09-11T17:09:38.454Z', - }, - { - id: '8391119528927829411', - version: 0, - timestamp: 46584586, - height: 109, - reward: '0', - previousBlock: '9467089070361350584', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a', - blockSignature: - '30440220617749a2d215693ac015b19285cda89ac7b9c5d383f2a416d2e975d66084dbb5022058baac9686c5096e7f77172d3e66f036bfd43564c8388365437f874efd68f146', - createdAt: '2018-09-11T17:09:46.375Z', - }, - { - id: '16820242876782994066', - version: 0, - timestamp: 46584594, - height: 110, - reward: '0', - previousBlock: '8391119528927829411', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93', - blockSignature: - '304402206c0a3dd633fd14dfafaa3b4b35d196485dc0b0c9ef8006ed5ae412510170d28a02203b0caef052c8c0b25fae1af1661bb06c2a212dfabaf87a6689528e897b0938a5', - createdAt: '2018-09-11T17:09:54.417Z', - }, - { - id: '17811063050605622774', - version: 0, - timestamp: 46584602, - height: 111, - reward: '0', - previousBlock: '16820242876782994066', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751', - blockSignature: - '304502210091c76111ecfb88d30071d4c850a63899cfebfb0ab03d3dd9e2b14bf921fd444e02204c48b0a02acb3fdf9d76d6ede553c1b98580145135801e27c803b38fce79de95', - createdAt: '2018-09-11T17:10:02.366Z', - }, - { - id: '17705208927947214042', - version: 0, - timestamp: 46584610, - height: 112, - reward: '0', - previousBlock: '17811063050605622774', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca', - blockSignature: - '3044022066f31a536c2bc098c6ee7f74591b6564cd619d2f00734b2a5a5491cde33f9a740220085c9e8a5b1457215f33a23d4b94f671c1f0f5f8127682b5713d1a44633ab598', - createdAt: '2018-09-11T17:10:10.335Z', - }, - { - id: '3794170631479398111', - version: 0, - timestamp: 46584618, - height: 113, - reward: '0', - previousBlock: '17705208927947214042', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28', - blockSignature: - '304402204ccdf577d8cbec4ef62ce2ef68752915de0344093e27057ab05895aa8871e73a02202f27f5d537e0a21911e574c21bc1b80936ea98a5d0b49c693dd42fec41f6401f', - createdAt: '2018-09-11T17:10:18.285Z', - }, - { - id: '2000919467344312301', - version: 0, - timestamp: 46584626, - height: 114, - reward: '0', - previousBlock: '3794170631479398111', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e', - blockSignature: - '30440220479043956e097c3bafad3529b8335eadf295e0c9fd71e2a9ab04c151247743cd02201ebf345c07fc9c7be440ed0989a84b841a2bdaa032c2adee34534be2a67d6ac8', - createdAt: '2018-09-11T17:10:26.304Z', - }, - { - id: '17040727982508304752', - version: 0, - timestamp: 46584634, - height: 115, - reward: '0', - previousBlock: '2000919467344312301', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294', - blockSignature: - '304402202fa24bd38c39d4884fee63b739b1650358804a1fc703290133c5cc6b6269c39f0220105daf68cb3745f788c01cbd9d3e365e8ed9168e73974d168ae4968689ccd813', - createdAt: '2018-09-11T17:10:34.422Z', - }, - { - id: '9164793200684835761', - version: 0, - timestamp: 46584642, - height: 116, - reward: '0', - previousBlock: '17040727982508304752', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f', - blockSignature: - '3045022100e5b85d062e3fb760fb05e45ae32f8bdca3cf44541f3b2609011e99a5dd39595d022069af2fd27969ff7ade169d59e2384e10d59f316c3660beb7417e83bf5544e2be', - createdAt: '2018-09-11T17:10:42.265Z', - }, - { - id: '9702240278422456718', - version: 0, - timestamp: 46584650, - height: 117, - reward: '0', - previousBlock: '9164793200684835761', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1', - blockSignature: - '3045022100e3aec30648fb8443d1330d8934c116b7e063885e7ce4589b3a9a356b9788efd9022050f042f95dd7462bf6a01cf1c08193903f08c6fd0ba3b8b109a5f94ca2f044ee', - createdAt: '2018-09-11T17:10:50.387Z', - }, - { - id: '2372729513640015242', - version: 0, - timestamp: 46584658, - height: 118, - reward: '0', - previousBlock: '9702240278422456718', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689', - blockSignature: - '3045022100ecb8acd0440f7e7f4b4220c4e3e6a70d61c4e04d05a87642767f466facb6250302207707505aaa1863ec12966b183b421688e5d5195b51b21b2979246796b249b5b0', - createdAt: '2018-09-11T17:10:58.441Z', - }, - { - id: '3675127542764096771', - version: 0, - timestamp: 46584666, - height: 119, - reward: '0', - previousBlock: '2372729513640015242', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564', - blockSignature: - '30440220406326a74f91b99f9a3979de11e29ebd39df74e20d0dc5bcd666a9720b9121af02207103c201be49d9b93bdf286679fbc852410df35d3e1242b53d4115645f107914', - createdAt: '2018-09-11T17:11:06.309Z', - }, - { - id: '5358667162203289341', - version: 0, - timestamp: 46584674, - height: 120, - reward: '0', - previousBlock: '3675127542764096771', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd', - blockSignature: - '3045022100f1aa88f80f0ca725337174df11b6c38d1019c6920154c7884fcc2d853f7c2b6f02206ff94a9cfc11455e8a1d277ea3f705505fbaded49b3a53a297b062b4eeda670e', - createdAt: '2018-09-11T17:11:14.247Z', - }, - { - id: '18004288728431909564', - version: 0, - timestamp: 46584682, - height: 121, - reward: '0', - previousBlock: '5358667162203289341', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565', - blockSignature: - '304402200f52bafe85aa27cceac9de07660146a417745b3764ef3d8edc5566efd2a2137602200d589d6af456297a6c1e0af569f60c7c87bc33ca788fca0afc7aeaf5b937b42c', - createdAt: '2018-09-11T17:11:22.473Z', - }, - { - id: '6595564574956816872', - version: 0, - timestamp: 46584690, - height: 122, - reward: '0', - previousBlock: '18004288728431909564', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e', - blockSignature: - '3045022100eaf2e2a97742659b160ff43d5fecd82f8de7ce8e498d6692189a45225484e47a022011749a641345e689a26905037abe4cb19bd5cc1ead4b1d962a5df139f26c2d7c', - createdAt: '2018-09-11T17:11:30.413Z', - }, - { - id: '10066431236273363611', - version: 0, - timestamp: 46584698, - height: 123, - reward: '0', - previousBlock: '6595564574956816872', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964', - blockSignature: - '3044022027996c930ee580299cf4e506d913ee97ec3e33efe6a5311222a8e173cd8576b5022024601fd7cdbb0e0e6c34283a04dd66c11d4dd55518bf72f254d8bb5e0e808495', - createdAt: '2018-09-11T17:11:38.340Z', - }, - { - id: '4889016410282212954', - version: 0, - timestamp: 46584706, - height: 124, - reward: '0', - previousBlock: '10066431236273363611', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd', - blockSignature: - '3045022100cbb02b436b77d7d52e559b9e38e514188327fe6fd2a33285a6e46c0244b5adca02201cd4a6460a7fb29326625d68b31b6067de4739176066056fcd15339948c7847e', - createdAt: '2018-09-11T17:11:46.316Z', - }, - { - id: '1479697744155789665', - version: 0, - timestamp: 46584714, - height: 125, - reward: '0', - previousBlock: '4889016410282212954', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a', - blockSignature: - '3045022100caae762a80796c84e0d58e137b01494fe3520721b306917a3cfcd3bc6ddd19a5022026a294ad3d974a9b4e9eeae88967a79432ca559ca8f0273fd245469554e9de8e', - createdAt: '2018-09-11T17:11:54.512Z', - }, - { - id: '3211613713464451417', - version: 0, - timestamp: 46584722, - height: 126, - reward: '0', - previousBlock: '1479697744155789665', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d', - blockSignature: - '304402202d1b567d6c0141dfb2d7a55eadca8077be71337f351a4525f2ccf7a06314874a02204db6782f98f2827a7bad420a40b2a4bd00a7713e8f02a8b407d8f11bb32aee44', - createdAt: '2018-09-11T17:12:02.313Z', - }, - { - id: '14953374716924761457', - version: 0, - timestamp: 46584730, - height: 127, - reward: '0', - previousBlock: '3211613713464451417', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9', - blockSignature: - '3045022100efca475f4241c545a6e3f59547e0b4843999468ef803e940d00accc84886df5a022035536116d39f2ca3f0651cdc97b6304e66cef55c20dc07e5d3620fce85ece5fe', - createdAt: '2018-09-11T17:12:10.410Z', - }, - { - id: '3086909897235569982', - version: 0, - timestamp: 46584738, - height: 128, - reward: '0', - previousBlock: '14953374716924761457', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b', - blockSignature: - '3045022100a7d81f46a37cda7f99da390156371e7acdec498de689cc22395abef28285450f022001d1c2aabb1023b12074172d60c1c86deb7b324c3bf29e8c8c74a1597006c2d9', - createdAt: '2018-09-11T17:12:18.323Z', - }, - { - id: '11858031216720080832', - version: 0, - timestamp: 46584746, - height: 129, - reward: '0', - previousBlock: '3086909897235569982', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d', - blockSignature: - '3045022100a46abcfe70ca5e95daee7fc4120e75009501e15b7f63cac47e30aac256f2c4d102201c8c0b6e7f7f86459dfcde1fa58b7ee7503bace7a9d3681dfb169180b93c67d5', - createdAt: '2018-09-11T17:12:26.281Z', - }, - { - id: '10670801688829958309', - version: 0, - timestamp: 46584754, - height: 130, - reward: '0', - previousBlock: '11858031216720080832', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5', - blockSignature: - '3045022100d6c6fa51b3394aeac44552369cf4f26331435dc839c8ba7690ae551f55edf05102207bbbef4210e7b27c760957d2d72515e5561bb7478d1f6fba0ab5c616d5beb95c', - createdAt: '2018-09-11T17:12:34.345Z', - }, - { - id: '17447880863052605705', - version: 0, - timestamp: 46584762, - height: 131, - reward: '0', - previousBlock: '10670801688829958309', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80', - blockSignature: - '304402202df02cdcedadb9c34c5303ccb1ec572774a37d6649d869641499831c4455658e02200a92d9d63e6b8c3dfc6c97ca536cadb5d9100e0f834ba4bb252ecc1dda404b78', - createdAt: '2018-09-11T17:12:42.409Z', - }, - { - id: '14743301194148498313', - version: 0, - timestamp: 46584770, - height: 132, - reward: '0', - previousBlock: '17447880863052605705', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883', - blockSignature: - '3045022100f68768e22ec1b31f693ee723218cf382bd552903ea81d0ce5263582785039dab02201e89eb48a668b34c96d08f34ce84aa171aee87f860f4303b80c0bbeac2cdfe43', - createdAt: '2018-09-11T17:12:50.302Z', - }, - { - id: '16518342502287211733', - version: 0, - timestamp: 46584778, - height: 133, - reward: '0', - previousBlock: '14743301194148498313', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c', - blockSignature: - '3045022100f0732f4d73a509abe0fcf7ad950bed566a6387b51f77c3013647acd7058e5343022063ebff98016117241ddaa962eb17dd1ead55dd13323cb89bfbcd35294b5086a8', - createdAt: '2018-09-11T17:12:58.314Z', - }, - { - id: '3353127138299058636', - version: 0, - timestamp: 46584786, - height: 134, - reward: '0', - previousBlock: '16518342502287211733', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983', - blockSignature: - '3045022100fbe535970ab538c2e595c29d61e5efc288032bd663d4abf635c789b6a131632602207cca6065f02369324aad13744baedcd7a382243cff5d14beb66b72f58d43dd9a', - createdAt: '2018-09-11T17:13:06.404Z', - }, - { - id: '13175423574809505282', - version: 0, - timestamp: 46584794, - height: 135, - reward: '0', - previousBlock: '3353127138299058636', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904', - blockSignature: - '304402200896963614fba6bc8973e8c8be928c967e79c51f424ac42fc8ed7a77dc9211ec02205193a9cf0f0de3aa649d233c4fe23d33047beefdab31c73d28c3c906b17a3a10', - createdAt: '2018-09-11T17:13:14.314Z', - }, - { - id: '3055219214450264731', - version: 0, - timestamp: 46584802, - height: 136, - reward: '0', - previousBlock: '13175423574809505282', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a', - blockSignature: - '304402204f32800096d690cc2619fd971dce261c95c785647d8a577a231de9556dfb87ab0220227c64529eca59adbf5c744a13248b51bab87759543dfeb2bcd12cba79d5dec7', - createdAt: '2018-09-11T17:13:22.356Z', - }, - { - id: '2571351432366343511', - version: 0, - timestamp: 46584810, - height: 137, - reward: '0', - previousBlock: '3055219214450264731', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a', - blockSignature: - '304402200c238364bb210d0193429add99316ae42bdba0b5b8335480ea5c018ceae23b7802204fd755e41d4110a84837de79769c2cd95aefbe3dc3a8da18361119f01d176ea0', - createdAt: '2018-09-11T17:13:30.363Z', - }, - { - id: '6241265622100638768', - version: 0, - timestamp: 46584818, - height: 138, - reward: '0', - previousBlock: '2571351432366343511', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b', - blockSignature: - '3045022100b9fa19345ed45ac93c0eb6f6b939ec78b73eda64946da8a2f25c0618265df1ba0220147bca759e832db436f7240bde45938d1f43b65e8e11d46c0c7e4e499fd5153f', - createdAt: '2018-09-11T17:13:38.346Z', - }, - { - id: '18124529744536436230', - version: 0, - timestamp: 46584826, - height: 139, - reward: '0', - previousBlock: '6241265622100638768', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e', - blockSignature: - '3044022064c5588cc5cbed09662f7850cb610d940e8ebc54ae2607ea47f5a03c6d0b419b022040a2765191b7b2df155cf560d1841390e399919ac02b15782893c9c76bb79d40', - createdAt: '2018-09-11T17:13:46.397Z', - }, - { - id: '10444106797405289101', - version: 0, - timestamp: 46584834, - height: 140, - reward: '0', - previousBlock: '18124529744536436230', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17', - blockSignature: - '304402206a5e1d6d97dcf9ee1b6c37bfcd297f5e531a67890a36e3b73ba91861614ff4e00220637bfac65bc7c5156a06af632eab4dbeb76ba26a4cdb35d967d0e869138fd0fd', - createdAt: '2018-09-11T17:13:54.389Z', - }, - { - id: '9550675986057608428', - version: 0, - timestamp: 46584842, - height: 141, - reward: '0', - previousBlock: '10444106797405289101', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374', - blockSignature: - '3045022100cf7c530e0df3a6d78f5329e21c2c54febd6a6e040260df5c2f740dbc7bf497b8022021f498737f154a979ce29812e58bab3e04abc3e18296f75ee3c56dfa8e5de574', - createdAt: '2018-09-11T17:14:02.315Z', - }, - { - id: '14001735896815839031', - version: 0, - timestamp: 46584850, - height: 142, - reward: '0', - previousBlock: '9550675986057608428', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c', - blockSignature: - '3045022100873e93f03cdaa3ca9cee3d93075af754a5599bb54652b5bde42600811bbff21202206a8d479d2c3a7ed95575034321621acdfd377fd805c7d265945dec6962e180f5', - createdAt: '2018-09-11T17:14:10.260Z', - }, - { - id: '10633857440088301351', - version: 0, - timestamp: 46584858, - height: 143, - reward: '0', - previousBlock: '14001735896815839031', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f', - blockSignature: - '3044022067a82ec75db9a587e6f8890baa93037b647c6093e42de0cdf6703a3198bee78f02201b1083c9bd74488d37bbec3ff3f1b3e9c684d4e0e1dd3fe60ee2547d228eed3c', - createdAt: '2018-09-11T17:14:18.337Z', - }, - { - id: '10947047468505212355', - version: 0, - timestamp: 46584866, - height: 144, - reward: '0', - previousBlock: '10633857440088301351', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24', - blockSignature: - '3043022055bf8dea517e636fa46f0826706996307b49d0935891e58eec9d363bdade8fbe021f433b6ea919156f75084b4a502185d3000e91d378aae8ffabfebed7a8c5ca0d', - createdAt: '2018-09-11T17:14:26.275Z', - }, - { - id: '7416108013584846374', - version: 0, - timestamp: 46584874, - height: 145, - reward: '0', - previousBlock: '10947047468505212355', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12', - blockSignature: - '304402207500b2890ff54361434c2abf11f5aa587bd528d78756f40cc072a4b5260ec817022066558ab1b5d969992880d64c9e4f0966775b373c33903c03eca61220ff64d1fb', - createdAt: '2018-09-11T17:14:34.388Z', - }, - { - id: '4808775828130615656', - version: 0, - timestamp: 46584882, - height: 146, - reward: '0', - previousBlock: '7416108013584846374', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - blockSignature: - '3044022039efce2d80b670403fbb0f3a2011cff6e207178cc4f46cb0c6267f89528586f102205a6658c235e31de7781be8d248f12568699f17c72408946aa0c7bde43ece1688', - createdAt: '2018-09-11T17:14:42.542Z', - }, - { - id: '6351137783535009353', - version: 0, - timestamp: 46584890, - height: 147, - reward: '0', - previousBlock: '4808775828130615656', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37', - blockSignature: - '3045022100996f089968b240a3e72321b1a7f6f32d210c28aebbcf3978842a9a5d7beddf2d02201ae019ab437bdfddb416c234ca4364eda1658cf31c4da9bf2ac5c2c9a4c1b5bb', - createdAt: '2018-09-11T17:14:50.391Z', - }, - { - id: '12627548292318554118', - version: 0, - timestamp: 46584898, - height: 148, - reward: '0', - previousBlock: '6351137783535009353', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647', - blockSignature: - '304502210085c920053b6aa11fdcfc54ac7d2b73799e9c0a5a5a853115ed4186d5b420d14f0220127e87b4911deddeaf4a88af43cc6a2ae41a36a3109ed9942c3c88940d052154', - createdAt: '2018-09-11T17:14:58.246Z', - }, - { - id: '2084097592015010038', - version: 0, - timestamp: 46584906, - height: 149, - reward: '0', - previousBlock: '12627548292318554118', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055', - blockSignature: - '3045022100d0fe7aa7bd1b5ec46fcf82eff1a23f632e2fd8658df8a2cfaabb4bbb89a5bc63022041a11d99c92dfcb51108888e3b1fd80b4d169b605f1f177758a8dc875567b3ae', - createdAt: '2018-09-11T17:15:06.428Z', - }, - { - id: '289908998694171445', - version: 0, - timestamp: 46584914, - height: 150, - reward: '0', - previousBlock: '2084097592015010038', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe', - blockSignature: - '304502210095e6c2904ea13151400e946d07c2d993be0c5f25f85e7325bd0607f952d3f450022034fd2236a2973559844ce3ac2dda58f0d0a17a3f8df6d46627a15809ec2c1ec3', - createdAt: '2018-09-11T17:15:14.427Z', - }, - { - id: '8005210879399134536', - version: 0, - timestamp: 46584922, - height: 151, - reward: '0', - previousBlock: '289908998694171445', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4', - blockSignature: - '3045022100a6adbabb46d2a78ff68ecd4249460e42a60c5194f85fefe3997300af70c36e460220654d30777cd3f362067d6be444c3fc6c28610f32cda43414ae27dd945b7cb6f2', - createdAt: '2018-09-11T17:15:22.323Z', - }, - { - id: '16154614440238332956', - version: 0, - timestamp: 46584930, - height: 152, - reward: '0', - previousBlock: '8005210879399134536', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc', - blockSignature: - '304402207087465aa8fcd2ead5bb853c57d09f1722c140ebe8bd6f8eba3018f95dea98070220617a9722465e67d9e7ecb52f7a124daf7fb7edb0dbe1a3d817c20bbbe5bc3cb6', - createdAt: '2018-09-11T17:15:30.312Z', - }, - { - id: '4655700423809570268', - version: 0, - timestamp: 46584938, - height: 153, - reward: '0', - previousBlock: '16154614440238332956', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a', - blockSignature: - '3045022100d7441a6c9193dde35da5240454733a724ec0eff34c820a5c0d34308ab798e3c902200de18c320188f0c0f1844b3ec0f2eb3ced452410ddc04913610a8bc33c12c14d', - createdAt: '2018-09-11T17:15:38.549Z', - }, - { - id: '1945298362228106487', - version: 0, - timestamp: 46584946, - height: 154, - reward: '0', - previousBlock: '4655700423809570268', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252', - blockSignature: - '304402200dffa8d365fdca32e75598ca5ee84750f2dd606e3dc20a1da18cbb84cf80262402206ce23e4fa379fe1c9bce8810ddbaf700d04a27bf5249e8ffe99323be9ecd9989', - createdAt: '2018-09-11T17:15:46.313Z', - }, - { - id: '8368457960814709674', - version: 0, - timestamp: 46584954, - height: 155, - reward: '0', - previousBlock: '1945298362228106487', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d', - blockSignature: - '3045022100b7e6c2c4797d307ea7e66a7bd6b0008bfe871ad77dc525e13b3bf0da9ebb06d6022052c11d46e8238f1979dee6d3e4b6ccb1ad3be7c38a59632d21bae58e045ebb0c', - createdAt: '2018-09-11T17:15:54.342Z', - }, -] diff --git a/packages/core-test-utils/fixtures/testnet/blocks.2-100.js b/packages/core-test-utils/fixtures/testnet/blocks.2-100.js deleted file mode 100644 index 456dd8436c..0000000000 --- a/packages/core-test-utils/fixtures/testnet/blocks.2-100.js +++ /dev/null @@ -1,1883 +0,0 @@ -module.exports = [ - { - id: '17882607875259085966', - version: 0, - timestamp: 46583330, - height: 2, - reward: '0', - previousBlock: '17184958558311101492', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565', - blockSignature: - '3045022100e7385c6ea42bd950f7f6ab8c8619cf2f66a41d8f8f185b0bc99af032cb25f30d02200b6210176a6cedfdcbe483167fd91c21d740e0e4011d24d679c601fdd46b0de9', - createdAt: '2018-09-11T16:48:50.550Z', - }, - { - id: '7242383292164246617', - version: 0, - timestamp: 46583338, - height: 3, - reward: '0', - previousBlock: '17882607875259085966', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17', - blockSignature: - '304402204087bb1d2c82b9178b02b9b3f285de260cdf0778643064fe6c7aef27321d49520220594c57009c1fca543350126d277c6adeb674c00685a464c3e4bf0d634dc37e39', - createdAt: '2018-09-11T16:48:58.431Z', - }, - { - id: '6799129462450431489', - version: 0, - timestamp: 46583346, - height: 4, - reward: '0', - previousBlock: '7242383292164246617', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95', - blockSignature: - '304402201edab51a52d06b8c2e30fe334699239db1ff198172c11600b62fa063b7f74ef9022047649d94df2342707c1aeefc635260c6b3f642735588f4e04965c01db820df44', - createdAt: '2018-09-11T16:49:06.496Z', - }, - { - id: '12118504647305813914', - version: 0, - timestamp: 46583354, - height: 5, - reward: '0', - previousBlock: '6799129462450431489', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24', - blockSignature: - '3045022100e5a098f4a5b83c3550eeebfd65b04099b70f6cc1eb66a1f0e9cc8f516d6d229b02200610a07cd2fa908b7dbc19743f73f26623f7e9c36d46020bc1605653cb211bce', - createdAt: '2018-09-11T16:49:14.403Z', - }, - { - id: '15175173194595918016', - version: 0, - timestamp: 46583362, - height: 6, - reward: '0', - previousBlock: '12118504647305813914', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294', - blockSignature: - '304402201b82f934764e43f7ff45091e014ca3bd6c10d4300553818b63b6e80719625d6002207e4af15383c836835a5f61f79b8f50ff104cbd339bf92283107759a007c2a93f', - createdAt: '2018-09-11T16:49:22.314Z', - }, - { - id: '4904019637861251674', - version: 0, - timestamp: 46583370, - height: 7, - reward: '0', - previousBlock: '15175173194595918016', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883', - blockSignature: - '3044022056993f85cd7876109aa724f3c665323be14584c83457483c07ea36d6c7b75ebc02200c710bd09883471452fec45570bf6f5bbb06266feef3f8f2a9921cb4d8e13a37', - createdAt: '2018-09-11T16:49:30.295Z', - }, - { - id: '7856584295950436953', - version: 0, - timestamp: 46583378, - height: 8, - reward: '0', - previousBlock: '4904019637861251674', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e', - blockSignature: - '3045022100e45ec5b032e7762711c3727f30004fffe631605e9c323d1cd278b079714a9e30022002eea2c2c5234079e52326a69944c5f064f329ca163cbd6ef6a1272ec70f8524', - createdAt: '2018-09-11T16:49:38.366Z', - }, - { - id: '9519090140760236724', - version: 0, - timestamp: 46583386, - height: 9, - reward: '0', - previousBlock: '7856584295950436953', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - blockSignature: - '304402200cc7bb74b97d8fcabf46f638ae3272143fdf6aa752df7ec5a3f850c976dfd2580220344cf0dea89111b7a455d95266b541753c40e7560d65af256d7bc3c97971e10a', - createdAt: '2018-09-11T16:49:46.409Z', - }, - { - id: '16953205951793869073', - version: 0, - timestamp: 46583394, - height: 10, - reward: '0', - previousBlock: '9519090140760236724', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d', - blockSignature: - '3045022100ba9cf471fad1103fa05c92038dd7f930edcfe526dfb4a68160ce068aa30e207802207b4ba0383953948c1fc7ec9125e8845cc3dd1df5f25727c9165e85c35c81d3c6', - createdAt: '2018-09-11T16:49:54.277Z', - }, - { - id: '8196750727867107014', - version: 0, - timestamp: 46583402, - height: 11, - reward: '0', - previousBlock: '16953205951793869073', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c', - blockSignature: - '304402200f93d59646c5e0bbc5b1d59bc9630bdee2669646c21d4ddf4db80c0b8afc1c0302200aaa36850b010ef9395f0a1f63652d236ef0265794953816e714a659f0a77283', - createdAt: '2018-09-11T16:50:02.246Z', - }, - { - id: '11789312660453212991', - version: 0, - timestamp: 46583410, - height: 12, - reward: '0', - previousBlock: '8196750727867107014', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd', - blockSignature: - '3045022100bac34bc173c7b473db162b72e2b3a14c51d6b1e9132530e759d6312a16241d6f022027328c6c69907bd67dcd23163fbbb5a5fff817d2beb62f67f1b61e2c202405db', - createdAt: '2018-09-11T16:50:10.268Z', - }, - { - id: '883541050685133383', - version: 0, - timestamp: 46583418, - height: 13, - reward: '0', - previousBlock: '11789312660453212991', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80', - blockSignature: - '30450221008574bfed61362c0a89a27e168d2ea83c9221e60e65d45471063d9ee295342dc502203d07ae5cdc6f04c6cb32e9569c3d57a2918a20ecb7b1e821075764518a269257', - createdAt: '2018-09-11T16:50:18.367Z', - }, - { - id: '2576147034160793253', - version: 0, - timestamp: 46583426, - height: 14, - reward: '0', - previousBlock: '883541050685133383', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37', - blockSignature: - '30440220125c9e17f524e4153853449c9f86961751f3225c1f3fbe4c0bb8eb3e66d9a3d802202d7cef058d1b3f5ce7ac7840d23aaefa595228ecff9aa30be41c6b809c5e7997', - createdAt: '2018-09-11T16:50:26.340Z', - }, - { - id: '15862421549550122994', - version: 0, - timestamp: 46583434, - height: 15, - reward: '0', - previousBlock: '2576147034160793253', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a', - blockSignature: - '30440220224f65b1190ab981691879b3d6861e11703b5fe78bceb36f5f51a8170228634b02203a8262131cd75cd2f6d56db8f5f5e370fcb635c84ca7a4b319d534a85476e543', - createdAt: '2018-09-11T16:50:34.426Z', - }, - { - id: '4688274543361472830', - version: 0, - timestamp: 46583442, - height: 16, - reward: '0', - previousBlock: '15862421549550122994', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a', - blockSignature: - '304402201a3aa0ebb176ff0ccc8c876142cada83cf8c12dc8a45de36eaf139c7d4d78e05022064b5f4b4da27a1e625236db9dcec89788fb417c88a928e193dcf816ad71117db', - createdAt: '2018-09-11T16:50:42.534Z', - }, - { - id: '5883773764321361785', - version: 0, - timestamp: 46583450, - height: 17, - reward: '0', - previousBlock: '4688274543361472830', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe', - blockSignature: - '3045022100f7ed8590c1c31e40b41d44c9321fb03d2864ef6f651ce518bec5a0eda5343bb302205bc757186589556a30d53abb9469c387011b49634494108b688d75bf63f07761', - createdAt: '2018-09-11T16:50:50.441Z', - }, - { - id: '7698647406843990550', - version: 0, - timestamp: 46583458, - height: 18, - reward: '0', - previousBlock: '5883773764321361785', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d', - blockSignature: - '304402201519d3e2c7d9221d8efcb532bccad9e5ac538751e7a43fb0bee7e736361483c802205b0e58959f9047c1a5de8af79d89a43828f85457653cbf732e164bdeef429c7b', - createdAt: '2018-09-11T16:50:58.430Z', - }, - { - id: '17299323387642470917', - version: 0, - timestamp: 46583466, - height: 19, - reward: '0', - previousBlock: '7698647406843990550', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28', - blockSignature: - '304402201bc616ef4dce8261b202df53a00555452d3dbe67be0a1959d06b269a189f0b7a02202d742310a55b693694f3f47ccd143629199ffe62ad0636d2fad52b8fad077249', - createdAt: '2018-09-11T16:51:06.342Z', - }, - { - id: '13814549369794209203', - version: 0, - timestamp: 46583474, - height: 20, - reward: '0', - previousBlock: '17299323387642470917', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983', - blockSignature: - '3045022100efc8192c34bcc4ac7bd74ea784a3a590eb9293e8923ff5cbb3eea74589c6a62d02206359a30fa492e1725655333b769ab6543fed69ba3ba6a5066a26cced31bf4192', - createdAt: '2018-09-11T16:51:14.337Z', - }, - { - id: '2277077266570141420', - version: 0, - timestamp: 46583482, - height: 21, - reward: '0', - previousBlock: '13814549369794209203', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f', - blockSignature: - '3044022054ad1575cc1ee09284f3f5252f80e1d48a59a9a3db740d8053784699824692ef022060ee4c78ea07e5612d86d74a70f6e1d5a94de5c2ed12a41111630caeee30b081', - createdAt: '2018-09-11T16:51:22.244Z', - }, - { - id: '5168238890100617525', - version: 0, - timestamp: 46583490, - height: 22, - reward: '0', - previousBlock: '2277077266570141420', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd', - blockSignature: - '30450221008ccfb6b5c8cbf8831c82f8a86afa88c2a27eb63d42c0cbd5eb9abe729fda314b02203df3fc50902656cce3bab7d5eb0a46d67be4eaab65fd41b1788b3676301cd005', - createdAt: '2018-09-11T16:51:30.339Z', - }, - { - id: '13736455109678252775', - version: 0, - timestamp: 46583498, - height: 23, - reward: '0', - previousBlock: '5168238890100617525', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb', - blockSignature: - '304402206ea489d925646a1253e78df0c9e761dbb58a173da5c34911301e262bc141127202201072cd4af50e72b5d34ad04708712b658d9ebb22fb00cc86132b0759fe04deba', - createdAt: '2018-09-11T16:51:38.469Z', - }, - { - id: '4282003522259720405', - version: 0, - timestamp: 46583506, - height: 24, - reward: '0', - previousBlock: '13736455109678252775', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c', - blockSignature: - '304402200593032068e5c67d38f4bd6cda8f4f64675f7b4d0bd07c77bf6c401c43c7e98e0220621eb05a51f1e7799af8fb45846fc75aa2e91246ba9db732976511a2bf0b8c0d', - createdAt: '2018-09-11T16:51:46.331Z', - }, - { - id: '11402948775542429021', - version: 0, - timestamp: 46583514, - height: 25, - reward: '0', - previousBlock: '4282003522259720405', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b', - blockSignature: - '3045022100e523b1c3ca604d62cf8bcf7e59b5eb22ad05ee1170619e07ac6bf90c204901bf0220214c72bd57067526672a57ca71147c4de05072311aa0962e1c9ae2e3ee89d413', - createdAt: '2018-09-11T16:51:54.309Z', - }, - { - id: '10423041071728627937', - version: 0, - timestamp: 46583522, - height: 26, - reward: '0', - previousBlock: '11402948775542429021', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a', - blockSignature: - '3045022100934248e71acba0b36fa40872ef2c36c8d807a23108f6d741d53422b48456e63b0220763a120b4eb8a25aa7a3bac5345cdccde50864a1fc6a9334a7c73ec4e548aded', - createdAt: '2018-09-11T16:52:02.508Z', - }, - { - id: '8610162720362054175', - version: 0, - timestamp: 46583530, - height: 27, - reward: '0', - previousBlock: '10423041071728627937', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1', - blockSignature: - '304402203fee457c035eb6df8abd0116a2726f8cf9a14758a5a7719589696cc338eee379022002722dd5064c308d1c875c99f7b6fe2376a7b987b6e4762ef669ff36d86a195c', - createdAt: '2018-09-11T16:52:10.336Z', - }, - { - id: '12492391117174185461', - version: 0, - timestamp: 46583706, - height: 28, - reward: '0', - previousBlock: '8610162720362054175', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc', - blockSignature: - '304502210085a248b96899c9443795857d3ce47f0ca7af2b97b9331e0e2a89ba715f98d79c022022e2839597e282d49ca9afbab66d417d754cc7acfb9e501a9db18506e5bfe1d4', - createdAt: '2018-09-11T16:55:06.505Z', - }, - { - id: '16807996944516641692', - version: 0, - timestamp: 46583714, - height: 29, - reward: '0', - previousBlock: '12492391117174185461', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2', - blockSignature: - '304402204d71ed66ddada97c2c38cac4650b5cd162b3fee65875e943ed41a253aa3d5ac4022013a00da3750cf1760208d78ae3cea524cc02c5909e806643b2907ee98ac2017a', - createdAt: '2018-09-11T16:55:14.296Z', - }, - { - id: '13831680932126032361', - version: 0, - timestamp: 46583722, - height: 30, - reward: '0', - previousBlock: '16807996944516641692', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689', - blockSignature: - '304402204e3aaa4af5776b85fe79120ddd37ebba8389cf18ef56df0b48d7ce6b3897e25b0220146ec521a9d32284eb2cfa9010b3379ba5a65020e25639ad644586ce272d99b8', - createdAt: '2018-09-11T16:55:22.414Z', - }, - { - id: '12836676775100447088', - version: 0, - timestamp: 46583730, - height: 31, - reward: '0', - previousBlock: '13831680932126032361', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751', - blockSignature: - '3044022058ffb2ceae3807c1beb244ba3f45c64f19f774de94b9f496b115da7eb0f877490220760d4d2f6048f7e694aa8cd5460a59fac64d671affec0cfddb2b4062ef3fe023', - createdAt: '2018-09-11T16:55:30.537Z', - }, - { - id: '16288754112931452950', - version: 0, - timestamp: 46583738, - height: 32, - reward: '0', - previousBlock: '12836676775100447088', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565', - blockSignature: - '3045022100c1ab5f864e444ae93537967f1ff485fe491496cef9404a1152d79cdf4244527102201246d40ba8e7038911403da19f6a685e6e0a86703697cc52c79d62d665bc4af0', - createdAt: '2018-09-11T16:55:38.270Z', - }, - { - id: '13854880431048519276', - version: 0, - timestamp: 46583746, - height: 33, - reward: '0', - previousBlock: '16288754112931452950', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17', - blockSignature: - '3045022100ca3177eba7928227e814ec37d9f0f4926add2020aa6b6de5f91e3121a64e4a60022067d2b87d4d7657de5c826985c494db50eb4b9a60ba30b722bf47a4bf708d3286', - createdAt: '2018-09-11T16:55:46.255Z', - }, - { - id: '13840357199384695281', - version: 0, - timestamp: 46583754, - height: 34, - reward: '0', - previousBlock: '13854880431048519276', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95', - blockSignature: - '304402205de0e8135717890ba90b31b60bc47b896dadbe5f9eb1b3eff084e0fb75afa8cf02200a1ddd0121786658972b495a3f812a4cf61afd99df7c10c93cd21ff74e1c361e', - createdAt: '2018-09-11T16:55:54.380Z', - }, - { - id: '11244772664213279648', - version: 0, - timestamp: 46583762, - height: 35, - reward: '0', - previousBlock: '13840357199384695281', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24', - blockSignature: - '3045022100e9d6a88ed3f96c5c33be6487159c3fdbdac0497f4958e7a6956332943482891c02202ecffc19b8d8cafe66c1e32a2e032d8f105ff9a4cd2ad46304b18fa168708d5e', - createdAt: '2018-09-11T16:56:02.434Z', - }, - { - id: '15443428733962171330', - version: 0, - timestamp: 46583770, - height: 36, - reward: '0', - previousBlock: '11244772664213279648', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294', - blockSignature: - '3044022070f7635302affb1ca9f515ddf14d0357e21846a5fd2094c4c27879b93a9c8b9702204e79875847238f560c932354a067167c74823da5a85eb45b7fbdb6a7265f33e1', - createdAt: '2018-09-11T16:56:10.294Z', - }, - { - id: '7454266686990589007', - version: 0, - timestamp: 46583778, - height: 37, - reward: '0', - previousBlock: '15443428733962171330', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883', - blockSignature: - '3044022057258edc364989cda08dc60c7059813db1fcf71004519691301226c08457a3b20220750dbad6b2734e2c66d53a39f9a90bf11d13e7f597440911c120b01803b17b7d', - createdAt: '2018-09-11T16:56:18.498Z', - }, - { - id: '4597925663050872611', - version: 0, - timestamp: 46583786, - height: 38, - reward: '0', - previousBlock: '7454266686990589007', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e', - blockSignature: - '304402202004341ca61b0580246d06c77f1099ea50e264d3bed1005568e0ededa850250b0220472ec5c11c57f6510ef95cd54dcd124e6272569907e2765ee9cf1b237a2dd612', - createdAt: '2018-09-11T16:56:26.323Z', - }, - { - id: '4238729528436398513', - version: 0, - timestamp: 46583794, - height: 39, - reward: '0', - previousBlock: '4597925663050872611', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - blockSignature: - '3045022100abad03f7fb997fa8fed0830720e0cf1d0c84de4c9315adbc1440896b9b4d0ea302201e190b801e0945a6e7921d407d073a066f4e4b0eeba68cefc44135e08fb08dcc', - createdAt: '2018-09-11T16:56:34.513Z', - }, - { - id: '9197895153416047617', - version: 0, - timestamp: 46583802, - height: 40, - reward: '0', - previousBlock: '4238729528436398513', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d', - blockSignature: - '304402204a0d98b25ecf80f6bcc17474c62cbd1fa61988fb4aad4d7cd5e6eb9f3440446202206ec758d73ad3bc2fd612bb0d6dcc2b7b20f6dadf46afa968b32631aff013bb07', - createdAt: '2018-09-11T16:56:42.416Z', - }, - { - id: '924154762496380167', - version: 0, - timestamp: 46583816, - height: 41, - reward: '0', - previousBlock: '9197895153416047617', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd', - blockSignature: - '3045022100b81be618931cddee9ea14e40a96fa142c7954daf78b43924a9487f94eea71053022032de4cbc65040c40a859f2bd8abe82f6c2683607aac54bd7022129da178ed1c4', - createdAt: '2018-09-11T16:56:56.352Z', - }, - { - id: '15177934353618302368', - version: 0, - timestamp: 46583826, - height: 42, - reward: '0', - previousBlock: '924154762496380167', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80', - blockSignature: - '3045022100e461ebcdf96f5da7847d05de62a12503fee7fb3bcceb0dfdb142bc32c66dbab4022054a8f0b75d0ffdf46e167a44427bc5b931200f5fadbdd06d30002314434344aa', - createdAt: '2018-09-11T16:57:06.383Z', - }, - { - id: '924392217041273716', - version: 0, - timestamp: 46583834, - height: 43, - reward: '0', - previousBlock: '15177934353618302368', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37', - blockSignature: - '3045022100d978d3336898294151c583d7c2b8f4b578a9af41eba08ae399d02b16815c61da02204ce7f53790358a8bf48e9bbc9019de7e6f827fe0a7bf015fb7ded4579b3229b5', - createdAt: '2018-09-11T16:57:14.417Z', - }, - { - id: '16477448205939004004', - version: 0, - timestamp: 46583842, - height: 44, - reward: '0', - previousBlock: '924392217041273716', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a', - blockSignature: - '304402202d10704a95ce21dfb3e11956a29cf692e2d6220a5f7ad1521691e2e3cdd37c9b02205b670adf2e1c2961669f31f70d6dea35aa0cfa13ab04adf36ff80a4f47b60615', - createdAt: '2018-09-11T16:57:22.490Z', - }, - { - id: '12838684505709123370', - version: 0, - timestamp: 46583850, - height: 45, - reward: '0', - previousBlock: '16477448205939004004', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a', - blockSignature: - '3045022100f93f4b968f294e8bbe3d0cfe004a493749e9c8cb9dedc1f72a7bf7a351032e8002201b3564c0d996ec08d80069de99d88ac5d0db3cd6ea5f275ec4099b0e7ff7f88e', - createdAt: '2018-09-11T16:57:30.496Z', - }, - { - id: '5132859035237431345', - version: 0, - timestamp: 46583858, - height: 46, - reward: '0', - previousBlock: '12838684505709123370', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe', - blockSignature: - '3045022100cb32d6cb514ea08308a37a4c0fc9557d4b8380004a61a54cdda3ed8c143327ab022075578c0afa5ceb4c0ac3ca277efb1cede2d332168e7849b4c99e3ae63ee5da05', - createdAt: '2018-09-11T16:57:38.441Z', - }, - { - id: '7963682794246037175', - version: 0, - timestamp: 46583866, - height: 47, - reward: '0', - previousBlock: '5132859035237431345', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d', - blockSignature: - '304402200c65cac8594f02135f9c073e9786b8202f41b3fee67c32a50baf31d7cc90014e02207c4398cb34bcbbeeb25eef195e467a74bd3fa459a7e24e48aea3f8d7e56abe6b', - createdAt: '2018-09-11T16:57:46.440Z', - }, - { - id: '13070232553444551324', - version: 0, - timestamp: 46583874, - height: 48, - reward: '0', - previousBlock: '7963682794246037175', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28', - blockSignature: - '3045022100c7cd9708728c454006f1df0cb1de64c62a82e2df1d94b3fc2d8319fb78e7240d022066ce9e73f7039f949e412d19cb257c1ea818fb84d64d89d63409508ed802eacc', - createdAt: '2018-09-11T16:57:54.296Z', - }, - { - id: '625974805128499213', - version: 0, - timestamp: 46583882, - height: 49, - reward: '0', - previousBlock: '13070232553444551324', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983', - blockSignature: - '3045022100f12e139ee66c5936775bad938e1749117c8882e392c06596b1fc24959695a3c102203b708a0bcd11f51c7739ed22758a6e5235ea510756b285281f8e93a486609e04', - createdAt: '2018-09-11T17:01:38.244Z', - }, - { - id: '15904513475939308775', - version: 0, - timestamp: 46584112, - height: 50, - reward: '0', - previousBlock: '625974805128499213', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc', - blockSignature: - '304402202e0ae3e498efaf903740cedd72790f8d62aa7010ae5aa8e8382353a2d8577e940220714efec6305271e3c8b3f8076cf967ffd4167084bd9567a9f24300a8246bf197', - createdAt: '2018-09-11T17:01:52.512Z', - }, - { - id: '15510607002956264823', - version: 0, - timestamp: 46584122, - height: 51, - reward: '0', - previousBlock: '15904513475939308775', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2', - blockSignature: - '3044022078ec98d6895f1bc2a09623f060fd479bb4f75c37706d6c46c36209d84d54568402202c2a09000caafa25580e5b884fc973453574d80d7ee700517094e05f73f92d70', - createdAt: '2018-09-11T17:02:02.491Z', - }, - { - id: '17227544728161997595', - version: 0, - timestamp: 46584130, - height: 52, - reward: '0', - previousBlock: '15510607002956264823', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689', - blockSignature: - '30450221009a94f6cab02143fd03db3745d714dcafcddbfa28e440bc74a27fcef409d3069902207526c6626410f71224f89b7f7dd5136d92fd06aa0f95d714fd8ca62aa29867b1', - createdAt: '2018-09-11T17:02:10.367Z', - }, - { - id: '8285956439623855556', - version: 0, - timestamp: 46584138, - height: 53, - reward: '0', - previousBlock: '17227544728161997595', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb', - blockSignature: - '30450221008c8be43703dd3b513e2fab89ccbb307be40a2015c060b37f34252cb3c4463d82022071d817add1e17f5097850e905c9963bb87e56055fe69fa8e5f897f8e83dc98b7', - createdAt: '2018-09-11T17:02:18.360Z', - }, - { - id: '6896439417802463255', - version: 0, - timestamp: 46584146, - height: 54, - reward: '0', - previousBlock: '8285956439623855556', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294', - blockSignature: - '3044022041e2d1d9a3215a1cee4e6f24f2d0bf9ab8e9c8aad85306c01de38925b997d6da022004bceb2844ac828ce07d572238910be51fe5e9508f2c8a0c36719db5d954a0c7', - createdAt: '2018-09-11T17:02:26.339Z', - }, - { - id: '2902372601932188608', - version: 0, - timestamp: 46584154, - height: 55, - reward: '0', - previousBlock: '6896439417802463255', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d', - blockSignature: - '3045022100eaf1cd0c296484c50fa507fd8f375891b1acc6c0434cee6f93ce45f40a181ad60220460c1fce332bdf297c4675a806899187cd6c86337d9e2b03ed972c3fe7009b74', - createdAt: '2018-09-11T17:02:34.363Z', - }, - { - id: '10825219670382518509', - version: 0, - timestamp: 46584162, - height: 56, - reward: '0', - previousBlock: '2902372601932188608', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12', - blockSignature: - '304502210087bb4b53db33adb4de3f21412882f30e6e5391466d23a2f6940b2a4493d9123702201d581122e703af1552c3750d4c6a4cff0d82b16647e4edadd0c4e1dfc52291fc', - createdAt: '2018-09-11T17:02:42.403Z', - }, - { - id: '8203927523221479932', - version: 0, - timestamp: 46584170, - height: 57, - reward: '0', - previousBlock: '10825219670382518509', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd', - blockSignature: - '304402203be65a7c74ca18ac8661a447ab209fce85f76d8c87a30db642342d9c41d138a602201667db0713ad5977cd6b42a045707c19147d28dc9c86ef5e6f974ab68c1d3e28', - createdAt: '2018-09-11T17:02:50.411Z', - }, - { - id: '16715427056889121751', - version: 0, - timestamp: 46584178, - height: 58, - reward: '0', - previousBlock: '8203927523221479932', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4', - blockSignature: - '3045022100b35dec76883908f5a91be19b2d31bc55b0486cb9f9350ecd5d565a7614a2df0702203219fb7439379cd1ff82377d8dcfdc6bda6bda99d2cc4c2050925c064f71aba0', - createdAt: '2018-09-11T17:02:58.302Z', - }, - { - id: '16305880676178938140', - version: 0, - timestamp: 46584186, - height: 59, - reward: '0', - previousBlock: '16715427056889121751', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c', - blockSignature: - '3045022100e5896815ae064ca2244dcce1fbc3651527f0d7a8e09bad13fb37a8d5001bf1f302207b3d1b61dee0ff0c9c435a794b65d66d9bdaf982ac20c5eff1f718fbf95351d6', - createdAt: '2018-09-11T17:03:06.354Z', - }, - { - id: '7799778404770723520', - version: 0, - timestamp: 46584194, - height: 60, - reward: '0', - previousBlock: '16305880676178938140', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e', - blockSignature: - '30440220375cd7cde93652b17926414f4ec24dbd4bac9e8ec961d242d475aa1edf0e74240220341048d58153b1055daf632ede7d2fcc9b57a5fafbc9d60495c25f9e7f68f752', - createdAt: '2018-09-11T17:03:14.404Z', - }, - { - id: '5758761688279180444', - version: 0, - timestamp: 46584202, - height: 61, - reward: '0', - previousBlock: '7799778404770723520', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - blockSignature: - '30450221009c87a22235797fcfa108f318de14ec99d6491545f6161280bab17e225ad381860220682d3a0a367c77915768b8ef7524379644c283cf2297b34d619c03d8d361d2ff', - createdAt: '2018-09-11T17:03:22.394Z', - }, - { - id: '300976438207115612', - version: 0, - timestamp: 46584210, - height: 62, - reward: '0', - previousBlock: '5758761688279180444', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a', - blockSignature: - '3045022100de8fc5c21168063ffae3a319c94de8029d02b4fbec0e6128c2ec8c7b18ade08402201250ffc4ef5ba71f3b8f0d8e9b935c39779066b313f57051d3e51f013831f6ca', - createdAt: '2018-09-11T17:03:30.381Z', - }, - { - id: '2019154568349929038', - version: 0, - timestamp: 46584218, - height: 63, - reward: '0', - previousBlock: '300976438207115612', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e', - blockSignature: - '304402206d927629fde40bc224ba6c044d3b5966193ccc578998dd42cbf2c526871d55c302201f83ed237a60531f7bb02ce26085069839d42f3dafd4b6581095000d01ecefa8', - createdAt: '2018-09-11T17:03:38.403Z', - }, - { - id: '10330603764729699281', - version: 0, - timestamp: 46584226, - height: 64, - reward: '0', - previousBlock: '2019154568349929038', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a', - blockSignature: - '3045022100e7194d853550abc79c507b2f6338c9013d528e0fb031ca345f5c1cbdd153c82302205b556d61d9a91399f6e7128657dbd279ec715bca0d0912ca0cc9ad87da0753a9', - createdAt: '2018-09-11T17:03:46.516Z', - }, - { - id: '16855224566886101894', - version: 0, - timestamp: 46584234, - height: 65, - reward: '0', - previousBlock: '10330603764729699281', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a', - blockSignature: - '304402204ece7e930f42e06e457da426fc59a0dedf29c04d76126e94167b50225c179850022010104fe54b125e341f888656d997ece2092e5bf599877d3a7d295d4ea5e4faf5', - createdAt: '2018-09-11T17:03:54.345Z', - }, - { - id: '3100095040675947327', - version: 0, - timestamp: 46584242, - height: 66, - reward: '0', - previousBlock: '16855224566886101894', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93', - blockSignature: - '3045022100e5623db11c1e683ea406c8f0688fa569481b126a3422c3c3f13e4b5dbafa021f0220172d01bbb12544d17877b99a3548efaffc8fa6692fe4f5b0e8e11bf57ebbc345', - createdAt: '2018-09-11T17:04:02.407Z', - }, - { - id: '9113535137970315837', - version: 0, - timestamp: 46584250, - height: 67, - reward: '0', - previousBlock: '3100095040675947327', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e', - blockSignature: - '3045022100a1110d53b5b0750bc0e4631df4c77d6cb4c7e31bfeb9d3a296b02ea6a5749671022008fe9533a58542903619deeda2a84c9c4542bd837c5920eeb959cee668be0c7e', - createdAt: '2018-09-11T17:04:10.390Z', - }, - { - id: '13522109433438488118', - version: 0, - timestamp: 46584258, - height: 68, - reward: '0', - previousBlock: '9113535137970315837', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0', - blockSignature: - '3045022100a39ab29adf9c2606d634766b2094ff8c069aa0f3fed8e3379554ff083f1e6183022056addd110d11f7d053e14741c67bb0fc573ddc3ded85589b422768a50cede4c0', - createdAt: '2018-09-11T17:04:18.376Z', - }, - { - id: '4836027030925388062', - version: 0, - timestamp: 46584266, - height: 69, - reward: '0', - previousBlock: '13522109433438488118', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751', - blockSignature: - '304402206286621e85e27d2f0c79b7a85da3da5465b0b8737cf506155b702fcbf542ee51022013e5a747e4b34c28dfefe2cb2ed47dc81dc15c04cc9d1aeab8590af4ed6ee3a9', - createdAt: '2018-09-11T17:04:26.540Z', - }, - { - id: '1124208886475980765', - version: 0, - timestamp: 46584274, - height: 70, - reward: '0', - previousBlock: '4836027030925388062', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1', - blockSignature: - '304402204095cf9589d899c6cc10ec0b391a5d3d1a3c94247e91680a6ecdf8c69c2e8519022062386adfd6bd42d825cd903a91882d98bf7dcce7c6d9f5d9a6172c95e9f1b870', - createdAt: '2018-09-11T17:04:34.421Z', - }, - { - id: '2300021073741981330', - version: 0, - timestamp: 46584282, - height: 71, - reward: '0', - previousBlock: '1124208886475980765', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5', - blockSignature: - '3045022100b02997cd91021d9a06dd77079aabb04e01de890176e6544e724e9563552082dc022053adc616e133e010aab3aa138548aa36e554decb899374a6c60c36d8d92525a4', - createdAt: '2018-09-11T17:04:42.337Z', - }, - { - id: '1435082221279493221', - version: 0, - timestamp: 46584290, - height: 72, - reward: '0', - previousBlock: '2300021073741981330', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd', - blockSignature: - '3045022100d3badcd7c113fd44d3ae22b502e45f1e9d15bbf7b5fc8a81aff765cf326199320220126e625a1b021f640cb79e3ea833c733e88d0eafe6c8947f7639f205431eefa8', - createdAt: '2018-09-11T17:04:50.375Z', - }, - { - id: '5781105193399022760', - version: 0, - timestamp: 46584298, - height: 73, - reward: '0', - previousBlock: '1435082221279493221', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37', - blockSignature: - '304402200fd5424d5304c669c1a8455a92490f1aab3005ad435ba4efda443b28ef6fe34902200c38c4b23213b42022ba551cdf6b26b8e29a4239ab88fd939e72f2f096821ea7', - createdAt: '2018-09-11T17:04:58.343Z', - }, - { - id: '7306295778722296358', - version: 0, - timestamp: 46584306, - height: 74, - reward: '0', - previousBlock: '5781105193399022760', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883', - blockSignature: - '304402202d794978935d04f48f1bbca967242b764d0ad19529dbf72fd8f08a14e300e39202205d960169ae3c18f983c2034cf387bd6bbd7cf4c30043179fd8da05c3dfa25e21', - createdAt: '2018-09-11T17:05:06.468Z', - }, - { - id: '11621508727770821942', - version: 0, - timestamp: 46584314, - height: 75, - reward: '0', - previousBlock: '7306295778722296358', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95', - blockSignature: - '3045022100c8395b5aa7a8f70cc5f8af5571efa3ac61906f0ca38ecd929ba1b7ba80ba3ab80220212acf9dd8b793f5b06e6e58a4a9791d37f28f431af88bd7cc53867e453de8e7', - createdAt: '2018-09-11T17:05:14.510Z', - }, - { - id: '3633426813146358866', - version: 0, - timestamp: 46584322, - height: 76, - reward: '0', - previousBlock: '11621508727770821942', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252', - blockSignature: - '304402200430ed30340bdf3dadc437db984547a7159f2fe716b7f607ce793353be2ceefc02201995468fe1cf73e0257f6db115f6de6bef1bee3de01222dd55fcfc243bc96fd4', - createdAt: '2018-09-11T17:05:22.467Z', - }, - { - id: '10585574832161348147', - version: 0, - timestamp: 46584330, - height: 77, - reward: '0', - previousBlock: '3633426813146358866', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b', - blockSignature: - '3045022100a69a370fcf30a8e80b36916fe17c4404c63ff8f5f0f654d662656db662be95a4022061dece06170f0e59a63abb078c5809488b6a3548470a699434fd769eb3e2ae66', - createdAt: '2018-09-11T17:05:30.462Z', - }, - { - id: '16732819090621444724', - version: 0, - timestamp: 46584338, - height: 78, - reward: '0', - previousBlock: '10585574832161348147', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c', - blockSignature: - '30450221008e6bdd7ea4189ea751e02157ab0449651a70405e560b68efb489f36b90b3c20702203d5764e527965e8f0fffa192bd3ccc4fa10649674e9c52cc361accf0d0c53541', - createdAt: '2018-09-11T17:05:38.369Z', - }, - { - id: '13535887590163426042', - version: 0, - timestamp: 46584346, - height: 79, - reward: '0', - previousBlock: '16732819090621444724', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055', - blockSignature: - '3044022052171485017323ec5ee011d4fa110a9e9137b2043ee6d0ec5f6da56bbf713f1102203744e54783bf400353b0221b2454b09ebdbddebe30a3d1c62fd7a1fc4cd5dd7f', - createdAt: '2018-09-11T17:05:46.419Z', - }, - { - id: '7447301067999335250', - version: 0, - timestamp: 46584354, - height: 80, - reward: '0', - previousBlock: '13535887590163426042', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a', - blockSignature: - '3045022100c729ce8b683f619368f91e782009b2319babacdc5ddc6d51fcaaaf76042c09e90220278ff4db42f02241a05bd26e2ad6c1ba71f0396a4c7c66b015756854dc9119e0', - createdAt: '2018-09-11T17:05:54.345Z', - }, - { - id: '9843400248178032761', - version: 0, - timestamp: 46584362, - height: 81, - reward: '0', - previousBlock: '7447301067999335250', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80', - blockSignature: - '3045022100858015fd991a4c318801217c8051572d2f0ac2657ce9676d612f3bdc4ed9a46a0220523af034dd7b11c31843615ec670675782e2e1549a260ada819ae9c453481da7', - createdAt: '2018-09-11T17:06:02.454Z', - }, - { - id: '14466873879302081211', - version: 0, - timestamp: 46584370, - height: 82, - reward: '0', - previousBlock: '9843400248178032761', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d', - blockSignature: - '30440220532e47a63b905269436a28b24c12da027f822ecbeddfb02f10ae9f6f7d05eba60220468a4e4f91c7b613292a937c4e7297590e0e2c66f1cffef628041f77c82b8e92', - createdAt: '2018-09-11T17:06:10.415Z', - }, - { - id: '14557757316112343772', - version: 0, - timestamp: 46584378, - height: 83, - reward: '0', - previousBlock: '14466873879302081211', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d', - blockSignature: - '30440220332a364f25ef135fbf1b6329778e8a0f0484d900b98e2ea883a5d9381a39e50b02204b4b0fb1d2f6aac5f2b61204782f20335d99e71a854fb0b17a5799dd862dbeca', - createdAt: '2018-09-11T17:06:18.358Z', - }, - { - id: '15094374440231126484', - version: 0, - timestamp: 46584386, - height: 84, - reward: '0', - previousBlock: '14557757316112343772', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24', - blockSignature: - '304402207fb74f4b2665307dced45c14025b1583c95d718841c4a320f60b67f749731bb302206716b2733bec550c86db5669ece70790ccbc225dcd2745c8007772b39bd5b9e9', - createdAt: '2018-09-11T17:06:26.235Z', - }, - { - id: '5714424774144793592', - version: 0, - timestamp: 46584394, - height: 85, - reward: '0', - previousBlock: '15094374440231126484', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b', - blockSignature: - '3045022100988030c41476d2e52b6e303e0a3ac6b9c56fe10d33f9dd8396d1c911e14eb2b702200997ac462d60095fd1e157a13986da7b4dd079796806865d63b7278763ae6fbc', - createdAt: '2018-09-11T17:06:34.371Z', - }, - { - id: '464843749694128591', - version: 0, - timestamp: 46584402, - height: 86, - reward: '0', - previousBlock: '5714424774144793592', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565', - blockSignature: - '3045022100e47ede694b10ed629f7da387151154c80f3a8fd434fdc3ca1bfdbf509bc8830e02201f834cf3a0167e33ec1aec83cacb3294046dc93e1a7a847bf043ece5583fd13e', - createdAt: '2018-09-11T17:06:42.321Z', - }, - { - id: '1723867044168113856', - version: 0, - timestamp: 46584410, - height: 87, - reward: '0', - previousBlock: '464843749694128591', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe', - blockSignature: - '3045022100c2b565bf8049d85b8ea0354efb0d94bc620aab6dfb973b09cad100195e6d511802200b7e9fd6a3173eeeeb8d0fa39e646ee099d141601aaf52df6241e11d759f02fb', - createdAt: '2018-09-11T17:06:50.473Z', - }, - { - id: '7628284931710202890', - version: 0, - timestamp: 46584418, - height: 88, - reward: '0', - previousBlock: '1723867044168113856', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a', - blockSignature: - '3044022003d908ed5e60f42501ad80303275096c21c5a41c5f2405511dac6d9e441d7d010220297dfc8f90a868827fc224e4df4fdf53c5c39a8cfaf63b7b904af69882b08475', - createdAt: '2018-09-11T17:06:58.368Z', - }, - { - id: '3531996231287449620', - version: 0, - timestamp: 46584426, - height: 89, - reward: '0', - previousBlock: '7628284931710202890', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564', - blockSignature: - '3045022100c52c316937e914f5adfb68efa31fa4edec3fa742009d59b2b2e02593f8f7728a02203d17f18f07f67d7ca23044b98552030ac547c4b741648ef8bdeee043ae0b34cd', - createdAt: '2018-09-11T17:07:06.264Z', - }, - { - id: '7435167259407493217', - version: 0, - timestamp: 46584434, - height: 90, - reward: '0', - previousBlock: '3531996231287449620', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28', - blockSignature: - '304402204b65ae72b90172be9b257300ddef0342c6817823c006bdb38b257d0b0ef1db8d0220429eaa801b85b63c858ace7f0440ff43c51157141282bca446133cca96159dff', - createdAt: '2018-09-11T17:07:14.284Z', - }, - { - id: '5656841191275027942', - version: 0, - timestamp: 46584442, - height: 91, - reward: '0', - previousBlock: '7435167259407493217', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647', - blockSignature: - '3045022100f500a0d127dd995bf238ebf15450155fa4be6ba59ebda61e2699282dff81c88702205d44d5e6dee2715239b11865bdc2d1482536f5600ce8a4ea5229f04781322045', - createdAt: '2018-09-11T17:07:22.266Z', - }, - { - id: '6646555502142403115', - version: 0, - timestamp: 46584450, - height: 92, - reward: '0', - previousBlock: '5656841191275027942', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f', - blockSignature: - '304402202b22d9186ba9b9c7fc020ca4425d3fa93a8e937683396b84fd22d1085bdb586d02201e7d474fa56e1af239d9e5e5dcb1dd53500194ee58602792bb1e6bd312777a87', - createdAt: '2018-09-11T17:07:30.377Z', - }, - { - id: '8618021774665230437', - version: 0, - timestamp: 46584458, - height: 93, - reward: '0', - previousBlock: '6646555502142403115', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904', - blockSignature: - '3045022100d0daa10ce4872a19b6b4bb3b3d1ed5d41221102e4aa362dfd277b59bb86e560702207316be26ab1213b808ac439dae5d1708290b2d76fba92d55ccd6afeb1523f91b', - createdAt: '2018-09-11T17:07:38.423Z', - }, - { - id: '6558209391920917685', - version: 0, - timestamp: 46584466, - height: 94, - reward: '0', - previousBlock: '8618021774665230437', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964', - blockSignature: - '304402205e134c3b7e2f3766e9c6d021f06e3ab537c589d27694c877aea302900c0a20e3022015813d2f698cce19a4f6d86eb2499a70f5266156f5fde9fc0edc7a8df696ec59', - createdAt: '2018-09-11T17:07:46.451Z', - }, - { - id: '18098650501718304452', - version: 0, - timestamp: 46584474, - height: 95, - reward: '0', - previousBlock: '6558209391920917685', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17', - blockSignature: - '3045022100d890951e03cd628ad22fea648c4e190099a730d66190b59aaa0990f77a0e6db2022075c11de03bb0e0010a51cdaca740636f54a8e74078821a7b30ec8e3ba86f89cb', - createdAt: '2018-09-11T17:07:54.409Z', - }, - { - id: '3612743398896107056', - version: 0, - timestamp: 46584482, - height: 96, - reward: '0', - previousBlock: '18098650501718304452', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca', - blockSignature: - '304402206de9d9ece51e025039379235e3e39ec01f9b1951b9d435c898a11fcc0e31fca10220572172f3d678ffa04f13c8c3857c315e404d3e6e7cb073406713cdf7369a20aa', - createdAt: '2018-09-11T17:08:02.374Z', - }, - { - id: '3590346281108682583', - version: 0, - timestamp: 46584490, - height: 97, - reward: '0', - previousBlock: '3612743398896107056', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc', - blockSignature: - '3045022100ab8b12df4b19fa8a93544fbf452dc68d6804ea6ab8e88edfa58d6d939af109f4022003554d5524d26c12a11f3328dca2045a8c562d90f26f9d28f7647bdabc928936', - createdAt: '2018-09-11T17:08:10.443Z', - }, - { - id: '5940078448948565481', - version: 0, - timestamp: 46584498, - height: 98, - reward: '0', - previousBlock: '3590346281108682583', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9', - blockSignature: - '30450221008f89c040a9f9eefa7e0eda2866318336d14b39ead910e4ddd697312fcbde9f22022040e86e88a39ae0546d240b1000d0962fa5ed9b7da65e13ae06bfa078abb435b6', - createdAt: '2018-09-11T17:08:18.326Z', - }, - { - id: '2958406944863963956', - version: 0, - timestamp: 46584506, - height: 99, - reward: '0', - previousBlock: '5940078448948565481', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374', - blockSignature: - '3044022046573056dca01fc73b635e3e33592c8c180bb0ee676414b11aeb0deac20250d302203ee93fb91ebb50d22314b059cff09c2fc367557b9b0f51fd494e9f0495abe3f3', - createdAt: '2018-09-11T17:08:26.381Z', - }, - { - id: '6161515163793239359', - version: 0, - timestamp: 46584514, - height: 100, - reward: '0', - previousBlock: '2958406944863963956', - numberOfTransactions: 0, - totalAmount: '0', - totalFee: '0', - payloadLength: 0, - payloadHash: - 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', - generatorPublicKey: - '02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983', - blockSignature: - '3045022100e32d06b7e3d4ae078cb2cb7093e2502a71732173099cb2881d91b5c4e49459d3022063df0dc42d2c3cd2854cd68881662be767b192c34ad7d4e1a50c00f34750d248', - createdAt: '2018-09-11T17:08:34.469Z', - }, -] diff --git a/packages/core-test-utils/fixtures/testnet/delegates.js b/packages/core-test-utils/fixtures/testnet/delegates.js deleted file mode 100644 index bdc0fd6ec7..0000000000 --- a/packages/core-test-utils/fixtures/testnet/delegates.js +++ /dev/null @@ -1,28 +0,0 @@ -const { client, crypto } = require('@arkecosystem/crypto') - -/** - * Get the testnet genesis delegates information - * @return {Array} array of objects like { secret, publicKey, address, balance } - */ - -client.getConfigManager().setFromPreset('ark', 'testnet') - -const delegatesConfig = require('../../config/testnet/delegates.json') -const genesisTransactions = require('../../config/testnet/genesisBlock.json') - .transactions - -module.exports = delegatesConfig.secrets.map(secret => { - const publicKey = crypto.getKeys(secret).publicKey - const address = crypto.getAddress(publicKey) - const balance = genesisTransactions.find( - transaction => - transaction.recipientId === address && transaction.type === 0, - ).amount - return { - secret, - passphrase: secret, // just an alias for delegate secret - publicKey, - address, - balance, - } -}) diff --git a/packages/core-test-utils/fixtures/testnet/passphrases.js b/packages/core-test-utils/fixtures/testnet/passphrases.js deleted file mode 100644 index e639a9f670..0000000000 --- a/packages/core-test-utils/fixtures/testnet/passphrases.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../config/testnet/delegates.json').secrets diff --git a/packages/core-test-utils/jest.config.js b/packages/core-test-utils/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-test-utils/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-test-utils/lib/generators/transactions/delegate.js b/packages/core-test-utils/lib/generators/transactions/delegate.js deleted file mode 100644 index 6422903e97..0000000000 --- a/packages/core-test-utils/lib/generators/transactions/delegate.js +++ /dev/null @@ -1,14 +0,0 @@ -const generateTransactions = require('./transaction') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -module.exports = (network, passphrase, quantity = 10, getStruct = false, fee) => - generateTransactions( - network, - TRANSACTION_TYPES.DELEGATE_REGISTRATION, - passphrase, - undefined, - undefined, - quantity, - getStruct, - fee, - ) diff --git a/packages/core-test-utils/lib/generators/transactions/index.js b/packages/core-test-utils/lib/generators/transactions/index.js deleted file mode 100644 index ae4b4ced1b..0000000000 --- a/packages/core-test-utils/lib/generators/transactions/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - transfer: require('./transfer'), - delegate: require('./delegate'), - signature: require('./signature'), - vote: require('./vote'), -} diff --git a/packages/core-test-utils/lib/generators/transactions/signature.js b/packages/core-test-utils/lib/generators/transactions/signature.js deleted file mode 100644 index 6909b81dc0..0000000000 --- a/packages/core-test-utils/lib/generators/transactions/signature.js +++ /dev/null @@ -1,14 +0,0 @@ -const generateTransactions = require('./transaction') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -module.exports = (network, passphrase, quantity = 10, getStruct = false, fee) => - generateTransactions( - network, - TRANSACTION_TYPES.SECOND_SIGNATURE, - passphrase, - undefined, - undefined, - quantity, - getStruct, - fee, - ) diff --git a/packages/core-test-utils/lib/generators/transactions/transaction.js b/packages/core-test-utils/lib/generators/transactions/transaction.js deleted file mode 100644 index 488ab1aac3..0000000000 --- a/packages/core-test-utils/lib/generators/transactions/transaction.js +++ /dev/null @@ -1,100 +0,0 @@ -const superheroes = require('superheroes') -const { client, crypto } = require('@arkecosystem/crypto') -const { - TRANSFER, - SECOND_SIGNATURE, - DELEGATE_REGISTRATION, - VOTE, -} = require('@arkecosystem/crypto').constants.TRANSACTION_TYPES -const defaultPassphrase = require('../../../fixtures/testnet/passphrases')[0] - -module.exports = ( - network, - type, - passphrase, - addressOrPublicKey, - amount = 2, - quantity = 10, - getStruct = false, - fee, -) => { - network = network || 'testnet' - type = type || TRANSFER - passphrase = passphrase || defaultPassphrase - - if (!['mainnet', 'devnet', 'testnet'].includes(network)) { - throw new Error('Invalid network') - } - - if ( - ![TRANSFER, SECOND_SIGNATURE, DELEGATE_REGISTRATION, VOTE].includes(type) - ) { - throw new Error('Invalid transaction type') - } - - let secondPassphrase - if (Array.isArray(passphrase)) { - secondPassphrase = passphrase[1] - passphrase = passphrase[0] - } - - client.getConfigManager().setFromPreset('ark', network) - - const transactions = [] - for (let i = 0; i < quantity; i++) { - let builder = client.getBuilder() - switch (type) { - case TRANSFER: { - if (!addressOrPublicKey) { - addressOrPublicKey = crypto.getAddress( - crypto.getKeys(passphrase).publicKey, - ) - } - builder = builder - .transfer() - .recipientId(addressOrPublicKey) - .amount(amount) - .vendorField(`Test Transaction ${i + 1}`) - break - } - case SECOND_SIGNATURE: { - builder = builder.secondSignature().signatureAsset(passphrase) - break - } - case DELEGATE_REGISTRATION: { - const username = superheroes - .random() - .toLowerCase() - .replace(/[^a-z0-9]/g, '_') - .substring(0, 20) - builder = builder.delegateRegistration().usernameAsset(username) - break - } - case VOTE: { - if (!addressOrPublicKey) { - addressOrPublicKey = crypto.getKeys(passphrase).publicKey - } - builder = builder.vote().votesAsset([`+${addressOrPublicKey}`]) - break - } - default: { - throw new Error('Invalid transaction type') - } - } - - if (fee) { - builder = builder.fee(fee) - } - - builder = builder.sign(passphrase) - - if (secondPassphrase) { - builder = builder.secondSign(secondPassphrase) - } - const transaction = getStruct ? builder.getStruct() : builder.build() - - transactions.push(transaction) - } - - return transactions -} diff --git a/packages/core-test-utils/lib/generators/transactions/transfer.js b/packages/core-test-utils/lib/generators/transactions/transfer.js deleted file mode 100644 index 2eeb9c82d4..0000000000 --- a/packages/core-test-utils/lib/generators/transactions/transfer.js +++ /dev/null @@ -1,22 +0,0 @@ -const generateTransactions = require('./transaction') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -module.exports = ( - network, - passphrase, - address, - amount = 2, - quantity = 10, - getStruct = false, - fee, -) => - generateTransactions( - network, - TRANSACTION_TYPES.TRANSFER, - passphrase, - address, - amount, - quantity, - getStruct, - fee, - ) diff --git a/packages/core-test-utils/lib/generators/transactions/vote.js b/packages/core-test-utils/lib/generators/transactions/vote.js deleted file mode 100644 index 754f7e1c90..0000000000 --- a/packages/core-test-utils/lib/generators/transactions/vote.js +++ /dev/null @@ -1,21 +0,0 @@ -const generateTransactions = require('./transaction') -const { TRANSACTION_TYPES } = require('../../../../crypto/lib/constants') - -module.exports = ( - network, - passphrase, - publicKey, - quantity = 10, - getStruct = false, - fee, -) => - generateTransactions( - network, - TRANSACTION_TYPES.VOTE, - passphrase, - publicKey, - undefined, - quantity, - getStruct, - fee, - ) diff --git a/packages/core-test-utils/lib/generators/wallets.js b/packages/core-test-utils/lib/generators/wallets.js deleted file mode 100644 index d01b1c2f3b..0000000000 --- a/packages/core-test-utils/lib/generators/wallets.js +++ /dev/null @@ -1,22 +0,0 @@ -const bip39 = require('bip39') -const { client, crypto } = require('@arkecosystem/crypto') - -module.exports = (network, quantity = 10) => { - network = network || 'testnet' - if (!['testnet', 'mainnet', 'devnet'].includes(network)) { - throw new Error('Invalid network') - } - - client.getConfigManager().setFromPreset('ark', network) - - const wallets = [] - for (let i = 0; i < quantity; i++) { - const passphrase = bip39.generateMnemonic() - const publicKey = crypto.getKeys(passphrase).publicKey - const address = crypto.getAddress(publicKey) - - wallets.push({ address, passphrase, publicKey }) - } - - return wallets -} diff --git a/packages/core-test-utils/lib/helpers/api.js b/packages/core-test-utils/lib/helpers/api.js deleted file mode 100644 index 7afa616b39..0000000000 --- a/packages/core-test-utils/lib/helpers/api.js +++ /dev/null @@ -1,57 +0,0 @@ -class ApiHelpers { - async request(server, method, url, headers, params = {}) { - // Build URL params from _params_ object for GET / DELETE requests - const getParams = Object.entries(params) - .map(([key, val]) => `${key}=${val}`) - .join('&') - - // Injecting the request into Hapi server instead of using axios - const injectOptions = { - method, - url: ['GET', 'DELETE'].includes(method) ? `${url}?${getParams}` : url, - headers, - payload: ['GET', 'DELETE'].includes(method) ? {} : params, - } - - const response = await server.inject(injectOptions) - const data = typeof response.result === 'string' - ? JSON.parse(response.result) - : response.result - Object.assign(response, { data, status: response.statusCode }) - return response - } - - expectJson(response) { - expect(response.data).toBeObject() - } - - expectStatus(response, code) { - expect(response.status).toBe(code) - } - - expectResource(response) { - expect(response.data.data).toBeObject() - } - - expectCollection(response) { - expect(Array.isArray(response.data.data)).toBe(true) - } - - expectSuccessful(response, statusCode = 200) { - this.expectStatus(response, statusCode) - this.expectJson(response) - } - - expectError(response, statusCode = 404) { - this.expectStatus(response, statusCode) - this.expectJson(response) - expect(response.data.statusCode).toBeNumber() - expect(response.data.error).toBeString() - expect(response.data.message).toBeString() - } -} - -/** - * @type {Helpers} - */ -module.exports = new ApiHelpers() diff --git a/packages/core-test-utils/lib/helpers/blockchain.js b/packages/core-test-utils/lib/helpers/blockchain.js deleted file mode 100644 index a07b748da0..0000000000 --- a/packages/core-test-utils/lib/helpers/blockchain.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - resetBlockchain: async () => { - // Resets everything so that it can be used in beforeAll to start clean a test suite - // Now resets: blocks (remove blocks other than genesis), transaction pool - // TODO: reset rounds, transactions in db... - const app = require('@arkecosystem/core-container') - - // reset to block height 1 - const blockchain = app.resolvePlugin('blockchain') - const height = blockchain.getLastBlock().data.height - if (height) { - await blockchain.removeBlocks(height - 1) - } - - const transactionPool = app.resolvePlugin('transactionPool') - transactionPool.flush() - }, -} diff --git a/packages/core-test-utils/lib/helpers/container.js b/packages/core-test-utils/lib/helpers/container.js deleted file mode 100644 index e373f6c005..0000000000 --- a/packages/core-test-utils/lib/helpers/container.js +++ /dev/null @@ -1,18 +0,0 @@ -const path = require('path') -const app = require('@arkecosystem/core-container') - -module.exports = { - setUp: async options => - app.setUp( - '2.0.0', - { - data: options.data || '~/.ark', - config: options.config - ? options.config - : path.resolve(__dirname, '../../config/testnet'), - token: options.token || 'ark', - network: options.network || 'testnet', - }, - options, - ), -} diff --git a/packages/core-test-utils/lib/matchers/api/block.js b/packages/core-test-utils/lib/matchers/api/block.js deleted file mode 100644 index 84a667aba2..0000000000 --- a/packages/core-test-utils/lib/matchers/api/block.js +++ /dev/null @@ -1,53 +0,0 @@ -const isEqual = require('lodash/isEqual') -const sortBy = require('lodash/sortBy') - -function isValidBlock(block) { - const allowedKeys = sortBy([ - 'blockSignature', - 'createdAt', - 'generatorPublicKey', - 'height', - 'id', - 'numberOfTransactions', - 'payloadHash', - 'payloadLength', - 'previousBlock', - 'reward', - 'timestamp', - 'totalAmount', - 'totalFee', - 'transactions', - 'updatedAt', - 'version', - ]) - const actualKeys = Object.keys(block).filter(key => allowedKeys.includes(key)) - - return isEqual(sortBy(actualKeys), allowedKeys) -} - -const toBeValidBlock = (actual, expected) => ({ - message: () => `Expected ${JSON.stringify(actual)} to be a valid block`, - pass: isValidBlock(actual), -}) - -const toBeValidArrayOfBlocks = (actual, expected) => { - const message = () => - `Expected ${JSON.stringify(actual)} to be a valid array of blocks` - - if (!Array.isArray(actual)) { - return { message, pass: false } - } - - actual.forEach(peer => { - if (!isValidBlock(peer)) { - return { message, pass: false } - } - }) - - return { message, pass: true } -} - -expect.extend({ - toBeValidBlock, - toBeValidArrayOfBlocks, -}) diff --git a/packages/core-test-utils/lib/matchers/api/peer.js b/packages/core-test-utils/lib/matchers/api/peer.js deleted file mode 100644 index 1f3ba6a26c..0000000000 --- a/packages/core-test-utils/lib/matchers/api/peer.js +++ /dev/null @@ -1,36 +0,0 @@ -const isEqual = require('lodash/isEqual') -const sortBy = require('lodash/sortBy') - -function isValidPeer(peer) { - const allowedKeys = sortBy(['ip', 'port']) - const actualKeys = Object.keys(peer).filter(key => allowedKeys.includes(key)) - - return isEqual(sortBy(actualKeys), allowedKeys) -} - -const toBeValidPeer = (actual, expected) => ({ - message: () => `Expected ${JSON.stringify(actual)} to be a valid peer`, - pass: isValidPeer(actual), -}) - -const toBeValidArrayOfPeers = (actual, expected) => { - const message = () => - `Expected ${JSON.stringify(actual)} to be a valid array of peers` - - if (!Array.isArray(actual)) { - return { message, pass: false } - } - - actual.forEach(peer => { - if (!isValidPeer(peer)) { - return { message, pass: false } - } - }) - - return { message, pass: true } -} - -expect.extend({ - toBeValidPeer, - toBeValidArrayOfPeers, -}) diff --git a/packages/core-test-utils/lib/matchers/api/response.js b/packages/core-test-utils/lib/matchers/api/response.js deleted file mode 100644 index d74d52f0a9..0000000000 --- a/packages/core-test-utils/lib/matchers/api/response.js +++ /dev/null @@ -1,32 +0,0 @@ -const toBeSuccessfulResponse = (actual, expected) => ({ - message: () => `Expected ${JSON.stringify({ - data: actual.data, - status: actual.status, - headers: actual.headers, - })} to be a successful response`, - pass: actual.status === 200 && typeof actual.data === 'object', -}) - -const toBePaginated = (actual, expected) => ({ - message: () => `Expected ${JSON.stringify({ - data: actual.data, - status: actual.status, - headers: actual.headers, - })} to be a paginated response`, - pass: - actual.data.meta - && [ - 'pageCount', - 'totalCount', - 'next', - 'previous', - 'self', - 'first', - 'last', - ].every(property => Object.keys(actual.data.meta).includes(property)), -}) - -expect.extend({ - toBeSuccessfulResponse, - toBePaginated, -}) diff --git a/packages/core-test-utils/lib/matchers/api/transaction.js b/packages/core-test-utils/lib/matchers/api/transaction.js deleted file mode 100644 index 14a300ada3..0000000000 --- a/packages/core-test-utils/lib/matchers/api/transaction.js +++ /dev/null @@ -1,32 +0,0 @@ -const isEqual = require('lodash/isEqual') -const sortBy = require('lodash/sortBy') - -const toBeApiTransaction = (actual, expected) => { - // TODO based on type - const allowedKeys = sortBy([ - 'id', - 'blockid', - 'type', - 'timestamp', - 'amount', - 'fee', - 'senderId', - 'senderPublicKey', - 'signature', - 'asset', - 'confirmations', - ]) - const actualKeys = Object.keys(actual).filter(key => - allowedKeys.includes(key), - ) - - return { - message: () => - `Expected ${JSON.stringify(actual)} to be a valid transaction`, - pass: isEqual(sortBy(actualKeys), allowedKeys), - } -} - -expect.extend({ - toBeApiTransaction, -}) diff --git a/packages/core-test-utils/lib/matchers/blockchain/dispatch.js b/packages/core-test-utils/lib/matchers/blockchain/dispatch.js deleted file mode 100644 index b90735d508..0000000000 --- a/packages/core-test-utils/lib/matchers/blockchain/dispatch.js +++ /dev/null @@ -1,20 +0,0 @@ -const toDispatch = (received, dispatcher, arg) => { - const mock = jest.fn() - - dispatcher.dispatch = mock - received() - - const calls = dispatcher.dispatch.mock.calls - const pass = calls && calls[0] ? Object.is(calls[0][0], arg) : false - - return { - // FIXME isNot is necessary to write the right message - // @see https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers - message: () => `Expected "${arg}" to ${this.isNot ? 'not' : ''} be dispatched`, - pass, - } -} - -expect.extend({ - toDispatch, -}) diff --git a/packages/core-test-utils/lib/matchers/blockchain/execute-on-entry.js b/packages/core-test-utils/lib/matchers/blockchain/execute-on-entry.js deleted file mode 100644 index 229b73e70d..0000000000 --- a/packages/core-test-utils/lib/matchers/blockchain/execute-on-entry.js +++ /dev/null @@ -1,28 +0,0 @@ -const { isEqual, get } = require('lodash') - -const toExecuteOnEntry = (machine, transition) => { - let path = transition.state - - // For nested states, but only works 1 level deep - if (transition.state.indexOf('.') !== -1) { - const slugs = path.split('.') - path = `${slugs[0]}.states.${slugs[1]}` - } - - const state = get(machine.states, path) - - const actions = transition.actions.map(action => `"${action}"`).join(', ') - - return { - // FIXME isNot is necessary to write the right message - // @see https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers - message: () => `Expected machine to ${ - this.isNot ? 'not ' : '' - } call actions ${actions} on state "${transition.state}"`, - pass: isEqual(state.onEntry.map(action => action.type), transition.actions), - } -} - -expect.extend({ - toExecuteOnEntry, -}) diff --git a/packages/core-test-utils/lib/matchers/blockchain/transition.js b/packages/core-test-utils/lib/matchers/blockchain/transition.js deleted file mode 100644 index 42838566cd..0000000000 --- a/packages/core-test-utils/lib/matchers/blockchain/transition.js +++ /dev/null @@ -1,18 +0,0 @@ -const { matchesState } = require('xstate') - -const toTransition = (machine, transition) => { - const state = machine.transition(transition.from, transition.on) - - return { - // FIXME isNot is necessary to write the right message - // @see https://facebook.github.io/jest/docs/en/expect.html#expectextendmatchers - message: () => `Expected machine to ${this.isNot ? 'not' : ''} transition to "${ - transition.to - }" from "${transition.from}" on "${transition.on}"`, - pass: matchesState(transition.to, state.value), - } -} - -expect.extend({ - toTransition, -}) diff --git a/packages/core-test-utils/lib/matchers/fields/address.js b/packages/core-test-utils/lib/matchers/fields/address.js deleted file mode 100644 index 3d10fdf6ef..0000000000 --- a/packages/core-test-utils/lib/matchers/fields/address.js +++ /dev/null @@ -1,16 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') - -/** - * Verify if the given value is an ark address. - * @param {String} received - * @param {String} argument - * @return {Boolean} - */ -const toBeArkAddress = (received, argument) => ({ - message: () => 'Expected value to be a valid address', - pass: crypto.validateAddress(received, argument), -}) - -expect.extend({ - toBeArkAddress, -}) diff --git a/packages/core-test-utils/lib/matchers/fields/public-key.js b/packages/core-test-utils/lib/matchers/fields/public-key.js deleted file mode 100644 index 4429791035..0000000000 --- a/packages/core-test-utils/lib/matchers/fields/public-key.js +++ /dev/null @@ -1,10 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') - -const toBeArkPublicKey = received => ({ - message: () => 'Expected value to be a valid public key', - pass: crypto.validatePublicKey(received), -}) - -expect.extend({ - toBeArkPublicKey, -}) diff --git a/packages/core-test-utils/lib/matchers/index.js b/packages/core-test-utils/lib/matchers/index.js deleted file mode 100644 index a5187e10f3..0000000000 --- a/packages/core-test-utils/lib/matchers/index.js +++ /dev/null @@ -1,26 +0,0 @@ -// TODO put together similar matchers (for example all 'types' matchers) -// so that we can require() a collection of coherent matchers - -require('./fields/address') -require('./fields/public-key') -require('./api/transaction') -require('./api/response') -require('./api/peer') -require('./api/block') -require('./models/delegate') -require('./models/transaction') -require('./models/wallet') -require('./transactions/valid') -require('./transactions/valid-second-signature') -require('./transactions/types/delegate-resignation') -require('./transactions/types/delegate') -require('./transactions/types/ipfs') -require('./transactions/types/multi-payment') -require('./transactions/types/multi-signature') -require('./transactions/types/second-signature') -require('./transactions/types/timelock-transfer') -require('./transactions/types/transfer') -require('./transactions/types/vote') -require('./blockchain/dispatch') -require('./blockchain/execute-on-entry') -require('./blockchain/transition') diff --git a/packages/core-test-utils/lib/matchers/models/delegate.js b/packages/core-test-utils/lib/matchers/models/delegate.js deleted file mode 100644 index c1cf6d0212..0000000000 --- a/packages/core-test-utils/lib/matchers/models/delegate.js +++ /dev/null @@ -1,15 +0,0 @@ -const isEqual = require('lodash/isEqual') -const sortBy = require('lodash/sortBy') - -const toBeDelegate = actual => ({ - message: () => 'Expected value to be a valid delegate', - pass: isEqual(sortBy(Object.keys(actual)), [ - 'address', - 'publicKey', - 'username', - ]), -}) - -expect.extend({ - toBeDelegate, -}) diff --git a/packages/core-test-utils/lib/matchers/models/transaction.js b/packages/core-test-utils/lib/matchers/models/transaction.js deleted file mode 100644 index 62ff70141e..0000000000 --- a/packages/core-test-utils/lib/matchers/models/transaction.js +++ /dev/null @@ -1,26 +0,0 @@ -const isEqual = require('lodash/isEqual') -const sortBy = require('lodash/sortBy') - -const toBeTransaction = actual => { - // TODO based on type - const allowedKeys = sortBy([ - 'id', - 'type', - 'amount', - 'fee', - 'timestamp', - 'signature', - ]) - const actualKeys = Object.keys(actual).filter(key => - allowedKeys.includes(key), - ) - - return { - message: () => 'Expected value to be a valid transaction', - pass: isEqual(sortBy(actualKeys), allowedKeys), - } -} - -expect.extend({ - toBeTransaction, -}) diff --git a/packages/core-test-utils/lib/matchers/models/wallet.js b/packages/core-test-utils/lib/matchers/models/wallet.js deleted file mode 100644 index 855249fdc2..0000000000 --- a/packages/core-test-utils/lib/matchers/models/wallet.js +++ /dev/null @@ -1,11 +0,0 @@ -const isEqual = require('lodash/isEqual') -const sortBy = require('lodash/sortBy') - -const toBeWallet = actual => ({ - message: () => 'Expected value to be a valid wallet', - pass: isEqual(sortBy(Object.keys(actual)), ['address', 'publicKey']), -}) - -expect.extend({ - toBeWallet, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/delegate-resignation.js b/packages/core-test-utils/lib/matchers/transactions/types/delegate-resignation.js deleted file mode 100644 index 04de841350..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/delegate-resignation.js +++ /dev/null @@ -1,10 +0,0 @@ -const { DELEGATE_RESIGNATION } = require('@arkecosystem/crypto').constants - -const toBeDelegateResignationType = received => ({ - message: () => 'Expected value to be a valid DELEGATE_RESIGNATION transaction.', - pass: received.type === DELEGATE_RESIGNATION, -}) - -expect.extend({ - toBeDelegateResignationType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/delegate.js b/packages/core-test-utils/lib/matchers/transactions/types/delegate.js deleted file mode 100644 index ba3032cb75..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/delegate.js +++ /dev/null @@ -1,10 +0,0 @@ -const { DELEGATE } = require('@arkecosystem/crypto').constants - -const toBeDelegateType = received => ({ - message: () => 'Expected value to be a valid DELEGATE transaction.', - pass: received.type === DELEGATE, -}) - -expect.extend({ - toBeDelegateType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/ipfs.js b/packages/core-test-utils/lib/matchers/transactions/types/ipfs.js deleted file mode 100644 index 613d83b4ef..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/ipfs.js +++ /dev/null @@ -1,10 +0,0 @@ -const { IPFS } = require('@arkecosystem/crypto').constants - -const toBeIpfsType = received => ({ - message: () => 'Expected value to be a valid IPFS transaction.', - pass: received.type === IPFS, -}) - -expect.extend({ - toBeIpfsType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/multi-payment.js b/packages/core-test-utils/lib/matchers/transactions/types/multi-payment.js deleted file mode 100644 index ce6bf3fd68..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/multi-payment.js +++ /dev/null @@ -1,10 +0,0 @@ -const { MULTI_PAYMENT } = require('@arkecosystem/crypto').constants - -const toBeMultiPaymentType = received => ({ - message: () => 'Expected value to be a valid MULTI_PAYMENT transaction.', - pass: received.type === MULTI_PAYMENT, -}) - -expect.extend({ - toBeMultiPaymentType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/multi-signature.js b/packages/core-test-utils/lib/matchers/transactions/types/multi-signature.js deleted file mode 100644 index e52b95799a..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/multi-signature.js +++ /dev/null @@ -1,10 +0,0 @@ -const { MULTI_SIGNATURE } = require('@arkecosystem/crypto').constants - -const toBeMultiSignatureType = received => ({ - message: () => 'Expected value to be a valid MULTI_SIGNATURE transaction.', - pass: received.type === MULTI_SIGNATURE, -}) - -expect.extend({ - toBeMultiSignatureType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/second-signature.js b/packages/core-test-utils/lib/matchers/transactions/types/second-signature.js deleted file mode 100644 index 7b0d591989..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/second-signature.js +++ /dev/null @@ -1,10 +0,0 @@ -const { SECOND_SIGNATURE } = require('@arkecosystem/crypto').constants - -const toBeSecondSignatureType = received => ({ - message: () => 'Expected value to be a valid SECOND_SIGNATURE transaction.', - pass: received.type === SECOND_SIGNATURE, -}) - -expect.extend({ - toBeSecondSignatureType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/timelock-transfer.js b/packages/core-test-utils/lib/matchers/transactions/types/timelock-transfer.js deleted file mode 100644 index 9140a4346b..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/timelock-transfer.js +++ /dev/null @@ -1,10 +0,0 @@ -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants - -const toBeTimelockTransferType = received => ({ - message: () => 'Expected value to be a valid TIMELOCK_TRANSFER transaction.', - pass: received.type === TRANSACTION_TYPES.TIMELOCK_TRANSFER, -}) - -expect.extend({ - toBeTimelockTransferType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/transfer.js b/packages/core-test-utils/lib/matchers/transactions/types/transfer.js deleted file mode 100644 index a2a814a9ca..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/transfer.js +++ /dev/null @@ -1,10 +0,0 @@ -const { TRANSFER } = require('@arkecosystem/crypto').constants - -const toBeTransferType = received => ({ - message: () => 'Expected value to be a valid TRANSFER transaction.', - pass: received.type === TRANSFER, -}) - -expect.extend({ - toBeTransferType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/types/vote.js b/packages/core-test-utils/lib/matchers/transactions/types/vote.js deleted file mode 100644 index 08e813d4ae..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/types/vote.js +++ /dev/null @@ -1,10 +0,0 @@ -const { VOTE } = require('@arkecosystem/crypto').constants - -const toBeVoteType = received => ({ - message: () => 'Expected value to be a valid VOTE transaction.', - pass: received.type === VOTE, -}) - -expect.extend({ - toBeVoteType, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/valid-second-signature.js b/packages/core-test-utils/lib/matchers/transactions/valid-second-signature.js deleted file mode 100644 index df3e4bcc2d..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/valid-second-signature.js +++ /dev/null @@ -1,16 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') - -const toHaveValidSecondSignature = (actual, expected) => { - let verified - try { - verified = crypto.verifySecondSignature(actual, expected.publicKey) - } catch (e) {} // eslint-disable-line no-empty - return { - message: () => 'Expected value to have a valid second signature', - pass: !!verified, - } -} - -expect.extend({ - toHaveValidSecondSignature, -}) diff --git a/packages/core-test-utils/lib/matchers/transactions/valid.js b/packages/core-test-utils/lib/matchers/transactions/valid.js deleted file mode 100644 index ed752caa82..0000000000 --- a/packages/core-test-utils/lib/matchers/transactions/valid.js +++ /dev/null @@ -1,10 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') - -const toBeValidTransaction = (transaction, network) => ({ - message: () => 'Expected value to be a valid transaction', - pass: crypto.verify(transaction, network), -}) - -expect.extend({ - toBeValidTransaction, -}) diff --git a/packages/core-test-utils/package.json b/packages/core-test-utils/package.json index 2d613a121b..cc159d27b0 100644 --- a/packages/core-test-utils/package.json +++ b/packages/core-test-utils/package.json @@ -1,36 +1,60 @@ { - "name": "@arkecosystem/core-test-utils", - "description": "Test Utilities for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/crypto": "~0.2", - "bip39": "^2.5.0", - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "lodash.sortby": "^4.7.0", - "lodash.take": "^4.1.1", - "lodash.uniqby": "^4.7.0", - "superheroes": "^2.0.0", - "xstate": "^4.2.1" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-test-utils", + "description": "Test Utilities for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust ", + "Erwann Gentric ", + "Joshua Noack " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-jest-matchers": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/bip39": "^2.4.1", + "@types/lodash.get": "^4.4.4", + "@types/lodash.isequal": "^4.5.3", + "@types/lodash.isstring": "^4.0.4", + "@types/lodash.sortby": "^4.7.4", + "awilix": "^4.0.1", + "bip39": "^2.5.0", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "lodash.isstring": "^4.0.1", + "lodash.sortby": "^4.7.0", + "superheroes": "^2.0.0", + "xstate": "^4.2.2" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-test-utils/src/config/index.js b/packages/core-test-utils/src/config/index.js new file mode 100644 index 0000000000..6334cf89b1 --- /dev/null +++ b/packages/core-test-utils/src/config/index.js @@ -0,0 +1,3 @@ +module.exports = { + passphrase: "prison tobacco acquire stone dignity palace note decade they current lesson robot", +}; diff --git a/packages/core-test-utils/src/config/testnet/delegates.json b/packages/core-test-utils/src/config/testnet/delegates.json new file mode 100644 index 0000000000..822250478e --- /dev/null +++ b/packages/core-test-utils/src/config/testnet/delegates.json @@ -0,0 +1,55 @@ +{ + "secrets": [ + "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", + "venue below waste gather spin cruise title still boost mother flash tuna", + "craft imitate step mixture patch forest volcano business charge around girl confirm", + "fatal hat sail asset chase barrel pluck bag approve coral slab bright", + "flash thank strike stove grain remove match reflect excess present beyond matrix", + "various present shine domain outdoor neck soup diesel limit express genuine tuna", + "hurdle pulse sheriff anchor two hope income pattern hazard bacon book night", + "glow boss party require silk interest pyramid marriage try wisdom snow grab", + "direct palace screen shuffle world fit produce rubber jelly gather river ordinary", + "wall ketchup shed word twist flip knock liar merge rural ill pond", + "measure blue volcano month orphan only cupboard found laugh peasant drama monitor", + "scissors sort pause medal target diesel reveal stock maze party gauge vacant", + "hand anchor hip pyramid taxi vote celery clap tribe damage shrimp brave", + "merge thunder detect stove else bottom favorite doll learn festival basic basic", + "educate attitude rely combine treat balcony west reopen coil west grab depth", + "advance silver advance squeeze load stone middle garden perfect invest field lounge", + "prison tobacco acquire stone dignity palace note decade they current lesson robot", + "team impact stadium year security steak harsh vacant fire pelican until olympic", + "walk intact ice prevent fit trial frog glory monkey once grunt gentle", + "same lens parrot suspect just sunset frown exercise lemon two mistake robust", + "skill insect issue crazy erase okay govern upgrade bounce dress motor athlete", + "peasant alert hard deposit naive follow page fiscal normal awful wedding history", + "resemble abandon same total oppose noise dune order fatal rhythm pink science", + "wide mesh ketchup acquire bright day mountain final below hamster scout drive", + "half weasel poet better rocket fan help left blade soda argue system", + "target sort neutral address language spike measure jaguar glance strong drop zone", + "race total stage trap wool believe twin pudding claim claim eternal miss", + "parade isolate wing vague magic husband acid skin skate path fence rib", + "neither fine dry priority example obtain bread reopen afford coyote milk minor", + "token atom lemon game charge area goose hotel excess endless spice oblige", + "pledge buffalo finish pipe mule popular bind clinic draft salon swamp purpose", + "west hat hold stand unique panther cable extend spell shaft injury reopen", + "van impulse pole install profit excuse give auction expire remain skate input", + "wrist maze potato april survey burden bamboo knee foot carry speak prison", + "three toddler copy owner pencil minimum doctor orange bottom ice detail design", + "ceiling warrior person thing whisper jeans black cricket drift ahead tornado typical", + "obvious mutual tone usual valve credit soccer mention also clown main box", + "valve slot soft green scale menu anxiety live drill legend upgrade chimney", + "twist comfort mule weather print oven cabin seek punch rival prepare sphere", + "say tumble glass argue aware service force caution until grocery hammer fetch", + "idea illegal empty frozen canvas arctic number poet rely track size obscure", + "chalk try large tower shed warfare blade clerk fame second charge tobacco", + "category nice verb fox start able brass climb boss luggage voice whale", + "favorite emotion trumpet visual welcome spend fine lock image review garage opera", + "waste axis humor auction next salmon much margin useful glimpse insect rotate", + "remember rose genuine police guard old flavor parent gain cross twelve first", + "coil tray elder mask circle crush anger electric harbor onion grab will", + "shove airport bus gather radio derive below horse canvas crime tribe adjust", + "retire lend burden cricket able sheriff output grocery empty scorpion flat inquiry", + "agree grain record shift fossil summer hunt mutual net vast behind pilot", + "decide rhythm oyster lady they merry betray jelly coyote solve episode then" + ] +} diff --git a/packages/core-test-utils/src/config/testnet/genesisBlock.json b/packages/core-test-utils/src/config/testnet/genesisBlock.json new file mode 100644 index 0000000000..a09a1fa3a0 --- /dev/null +++ b/packages/core-test-utils/src/config/testnet/genesisBlock.json @@ -0,0 +1,2210 @@ +{ + "version": 0, + "totalAmount": 12500000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + "timestamp": 0, + "numberOfTransactions": 153, + "payloadLength": 35960, + "previousBlock": null, + "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", + "transactions": [ + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", + "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", + "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", + "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", + "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", + "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", + "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", + "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", + "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", + "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", + "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", + "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", + "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", + "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", + "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", + "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", + "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", + "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", + "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", + "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", + "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", + "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", + "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", + "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", + "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", + "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", + "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", + "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", + "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", + "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", + "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", + "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", + "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", + "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", + "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", + "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", + "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", + "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", + "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", + "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", + "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", + "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", + "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", + "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", + "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", + "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", + "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", + "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", + "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", + "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", + "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245100000000000, + "fee": 0, + "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", + "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" + } + }, + "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", + "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", + "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" + } + }, + "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", + "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", + "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" + } + }, + "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", + "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", + "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" + } + }, + "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", + "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", + "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" + } + }, + "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", + "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", + "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" + } + }, + "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", + "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", + "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" + } + }, + "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", + "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", + "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" + } + }, + "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", + "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", + "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" + } + }, + "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", + "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", + "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" + } + }, + "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", + "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", + "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" + } + }, + "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", + "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", + "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" + } + }, + "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", + "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", + "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" + } + }, + "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", + "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", + "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" + } + }, + "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", + "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", + "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" + } + }, + "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", + "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", + "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" + } + }, + "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", + "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", + "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" + } + }, + "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", + "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", + "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" + } + }, + "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", + "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", + "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" + } + }, + "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", + "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", + "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" + } + }, + "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", + "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", + "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" + } + }, + "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", + "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", + "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" + } + }, + "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", + "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", + "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" + } + }, + "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", + "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", + "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" + } + }, + "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", + "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", + "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" + } + }, + "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", + "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", + "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" + } + }, + "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", + "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", + "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" + } + }, + "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", + "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", + "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" + } + }, + "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", + "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", + "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" + } + }, + "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", + "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", + "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" + } + }, + "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", + "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", + "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" + } + }, + "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", + "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", + "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" + } + }, + "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", + "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", + "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" + } + }, + "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", + "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", + "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" + } + }, + "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", + "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", + "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" + } + }, + "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", + "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", + "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" + } + }, + "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", + "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", + "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" + } + }, + "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", + "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", + "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" + } + }, + "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", + "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", + "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" + } + }, + "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", + "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", + "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" + } + }, + "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", + "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", + "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" + } + }, + "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", + "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", + "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" + } + }, + "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", + "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", + "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" + } + }, + "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", + "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", + "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" + } + }, + "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", + "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", + "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" + } + }, + "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", + "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", + "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" + } + }, + "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", + "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", + "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" + } + }, + "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", + "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", + "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" + } + }, + "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", + "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", + "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" + } + }, + "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", + "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", + "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" + } + }, + "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", + "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", + "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" + } + }, + "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", + "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", + "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "timestamp": 0, + "asset": { + "votes": ["+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357"] + }, + "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", + "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", + "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", + "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + "timestamp": 0, + "asset": { + "votes": ["+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80"] + }, + "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", + "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", + "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", + "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + "timestamp": 0, + "asset": { + "votes": ["+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e"] + }, + "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", + "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", + "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", + "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + "timestamp": 0, + "asset": { + "votes": ["+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055"] + }, + "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", + "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", + "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", + "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + "timestamp": 0, + "asset": { + "votes": ["+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2"] + }, + "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", + "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", + "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", + "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + "timestamp": 0, + "asset": { + "votes": ["+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e"] + }, + "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", + "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", + "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", + "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + "timestamp": 0, + "asset": { + "votes": ["+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883"] + }, + "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", + "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", + "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + "timestamp": 0, + "asset": { + "votes": ["+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252"] + }, + "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", + "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", + "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", + "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + "timestamp": 0, + "asset": { + "votes": ["+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4"] + }, + "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", + "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", + "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", + "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + "timestamp": 0, + "asset": { + "votes": ["+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28"] + }, + "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", + "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", + "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", + "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + "timestamp": 0, + "asset": { + "votes": ["+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d"] + }, + "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", + "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", + "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", + "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + "timestamp": 0, + "asset": { + "votes": ["+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294"] + }, + "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", + "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", + "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", + "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + "timestamp": 0, + "asset": { + "votes": ["+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983"] + }, + "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", + "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", + "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", + "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + "timestamp": 0, + "asset": { + "votes": ["+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964"] + }, + "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", + "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", + "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", + "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + "timestamp": 0, + "asset": { + "votes": ["+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5"] + }, + "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", + "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", + "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", + "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + "timestamp": 0, + "asset": { + "votes": ["+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95"] + }, + "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", + "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", + "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", + "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + "timestamp": 0, + "asset": { + "votes": ["+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a"] + }, + "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", + "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", + "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", + "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + "timestamp": 0, + "asset": { + "votes": ["+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689"] + }, + "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", + "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", + "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", + "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "timestamp": 0, + "asset": { + "votes": ["+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a"] + }, + "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", + "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", + "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", + "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + "timestamp": 0, + "asset": { + "votes": ["+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a"] + }, + "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", + "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", + "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", + "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + "timestamp": 0, + "asset": { + "votes": ["+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f"] + }, + "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", + "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", + "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", + "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + "timestamp": 0, + "asset": { + "votes": ["+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751"] + }, + "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", + "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", + "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", + "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + "timestamp": 0, + "asset": { + "votes": ["+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb"] + }, + "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", + "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", + "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", + "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + "timestamp": 0, + "asset": { + "votes": ["+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d"] + }, + "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", + "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", + "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + "timestamp": 0, + "asset": { + "votes": ["+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a"] + }, + "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", + "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", + "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", + "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + "timestamp": 0, + "asset": { + "votes": ["+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe"] + }, + "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", + "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", + "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", + "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + "timestamp": 0, + "asset": { + "votes": ["+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e"] + }, + "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", + "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", + "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", + "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + "timestamp": 0, + "asset": { + "votes": ["+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9"] + }, + "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", + "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", + "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", + "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + "timestamp": 0, + "asset": { + "votes": ["+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca"] + }, + "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", + "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", + "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", + "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + "timestamp": 0, + "asset": { + "votes": ["+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c"] + }, + "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", + "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", + "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", + "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + "timestamp": 0, + "asset": { + "votes": ["+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24"] + }, + "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", + "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", + "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", + "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + "timestamp": 0, + "asset": { + "votes": ["+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f"] + }, + "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", + "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", + "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", + "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + "timestamp": 0, + "asset": { + "votes": ["+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b"] + }, + "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", + "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", + "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", + "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + "timestamp": 0, + "asset": { + "votes": ["+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12"] + }, + "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", + "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", + "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", + "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + "timestamp": 0, + "asset": { + "votes": ["+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904"] + }, + "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", + "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", + "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", + "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + "timestamp": 0, + "asset": { + "votes": ["+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c"] + }, + "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", + "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", + "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + "timestamp": 0, + "asset": { + "votes": ["+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd"] + }, + "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", + "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", + "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + "timestamp": 0, + "asset": { + "votes": ["+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647"] + }, + "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", + "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", + "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", + "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + "timestamp": 0, + "asset": { + "votes": ["+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd"] + }, + "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", + "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", + "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", + "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + "timestamp": 0, + "asset": { + "votes": ["+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564"] + }, + "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", + "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", + "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", + "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + "timestamp": 0, + "asset": { + "votes": ["+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0"] + }, + "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", + "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", + "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", + "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + "timestamp": 0, + "asset": { + "votes": ["+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b"] + }, + "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", + "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", + "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", + "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + "timestamp": 0, + "asset": { + "votes": ["+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc"] + }, + "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", + "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", + "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", + "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + "timestamp": 0, + "asset": { + "votes": ["+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565"] + }, + "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", + "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", + "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", + "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + "timestamp": 0, + "asset": { + "votes": ["+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374"] + }, + "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", + "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", + "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", + "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + "timestamp": 0, + "asset": { + "votes": ["+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93"] + }, + "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", + "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", + "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", + "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + "timestamp": 0, + "asset": { + "votes": ["+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17"] + }, + "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", + "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", + "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", + "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + "timestamp": 0, + "asset": { + "votes": ["+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1"] + }, + "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", + "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", + "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", + "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + "timestamp": 0, + "asset": { + "votes": ["+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d"] + }, + "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", + "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", + "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", + "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + "timestamp": 0, + "asset": { + "votes": ["+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37"] + }, + "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", + "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", + "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", + "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + "timestamp": 0, + "asset": { + "votes": ["+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a"] + }, + "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", + "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", + "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" + } + ], + "height": 1, + "id": "17184958558311101492", + "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" +} diff --git a/packages/core-test-utils/src/config/testnet/peers.json b/packages/core-test-utils/src/config/testnet/peers.json new file mode 100644 index 0000000000..fa4e124d8d --- /dev/null +++ b/packages/core-test-utils/src/config/testnet/peers.json @@ -0,0 +1,8 @@ +{ + "list": [ + { + "ip": "0.0.0.99", + "port": 4000 + } + ] +} diff --git a/packages/core-test-utils/src/config/testnet/plugins.js b/packages/core-test-utils/src/config/testnet/plugins.js new file mode 100644 index 0000000000..662f1de692 --- /dev/null +++ b/packages/core-test-utils/src/config/testnet/plugins.js @@ -0,0 +1,78 @@ +module.exports = { + "@arkecosystem/core-event-emitter": {}, + "@arkecosystem/core-logger-winston": { + transports: { + console: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + dailyRotate: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + }, + }, + "@arkecosystem/core-database-postgres": { + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_development`, + user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + password: process.env.CORE_DB_PASSWORD || "password", + }, + }, + "@arkecosystem/core-transaction-pool": { + enabled: !process.env.CORE_TRANSACTION_POOL_DISABLED, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + // 100+ years in the future to avoid our hardcoded transactions used in the + // tests to expire immediately + maxTransactionAge: 4036608000, + dynamicFees: { + minFeePool: 1000, + minFeeBroadcast: 1000, + }, + }, + "@arkecosystem/core-p2p": { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4000, + minimumVersion: ">=2.0.0", + minimumNetworkReach: 5, + coldStart: 5, + }, + "@arkecosystem/core-blockchain": { + fastRebuild: false, + }, + "@arkecosystem/core-api": { + enabled: !process.env.CORE_API_DISABLED, + host: process.env.CORE_API_HOST || "0.0.0.0", + port: process.env.CORE_API_PORT || 4003, + whitelist: ["*"], + }, + "@arkecosystem/core-webhooks": { + enabled: process.env.CORE_WEBHOOKS_ENABLED, + server: { + enabled: process.env.CORE_WEBHOOKS_API_ENABLED, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + }, + "@arkecosystem/core-graphql": { + enabled: process.env.CORE_GRAPHQL_ENABLED, + host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", + port: process.env.CORE_GRAPHQL_PORT || 4005, + }, + "@arkecosystem/core-forger": { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4000}`], + }, + "@arkecosystem/core-json-rpc": { + enabled: process.env.CORE_JSON_RPC_ENABLED, + host: process.env.CORE_JSON_RPC_HOST || "0.0.0.0", + port: process.env.CORE_JSON_RPC_PORT || 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, +}; diff --git a/packages/core-test-utils/src/config/unitnet/delegates.json b/packages/core-test-utils/src/config/unitnet/delegates.json new file mode 100644 index 0000000000..5bac5a50bf --- /dev/null +++ b/packages/core-test-utils/src/config/unitnet/delegates.json @@ -0,0 +1,55 @@ +{ + "secrets": [ + "beach alert increase quit vessel address mandate click deny result play receive", + "girl smoke wrong keep fruit huge duck label buyer dizzy exhaust thrive", + "garage mix praise brush memory gallery drama credit fall afford powder still", + "genuine path nuclear spy arrange clip day this night journey gather pipe", + "below idea aim message advice hint shop chronic bright develop slow steel", + "lab change cancel inform throw today alter engine bamboo address core antenna", + "attract beauty license vacuum reunion various inmate multiply concert raven mimic hurry", + "early junior cargo sphere raise ethics collect spend funny beef anger awful", + "vital trade lyrics hedgehog peasant uncle coral veteran harbor stay know super", + "sign heavy tornado equip bag hobby reject seek voyage drill catch napkin", + "slam vintage pig design disagree tunnel crash sadness slogan tag use protect", + "trust theory sphere adapt under enhance valid leader sense equip arch inform", + "obvious bike worry flavor essay fault forward clutch lumber remind ring trap", + "diet castle oven kingdom much hunt cliff fine chunk tongue grant scrap", + "little shaft option steel depend knee file vendor cereal forum deal clever", + "quick fatigue split famous coral venture scale chronic urban hazard slow human", + "foot tag danger trash alone coyote report struggle motion such basic guilt", + "flock vendor hurry solid parent physical various hat wreck blouse pride script", + "humor dinosaur clever basic company march viable labor night sand mistake need", + "vacant session visual suggest supreme suspect surface birth scatter setup pipe catalog", + "napkin story fun asset cigar cage diesel spider cargo bless knee farm", + "section history result hold seat must dial awkward impose want pumpkin consider", + "worth atom wave inherit battle purpose rhythm crouch course agree solar shallow", + "tiger hammer space fox cereal harbor october divide solid secret retreat sad", + "device gap ladder time load shed clump rule taxi fury trophy observe", + "thumb answer owner always click parent glide exile peasant slim find risk", + "evidence museum beyond glimpse inner length story rent crater option liberty dignity", + "rural typical middle pluck service huge near fabric hurt base unaware trend", + "token rug empower easily hub resist clean early enlist purity avocado arch", + "skate pass help company speed chapter program fiber submit slam adult rescue", + "trophy plunge law minimum cause fitness derive cover bicycle truck help talk", + "spoil pink point speed mouse guide shield parent alcohol hurry toe glide", + "chat enrich draw release secret weather arctic language era total innocent finger", + "solution essence another ceiling diamond ship review swing emerge scrub version odor", + "addict enemy camp ship infant survey neutral kidney feature vacant hybrid double", + "tourist alter manual sausage scissors home illegal curious this hammer input ship", + "company cliff shiver grid false gallery zebra border record spring benefit time", + "moment found patient double have project service detail hard dilemma soup message", + "wool thought danger sniff profit concert fever fitness mosquito hour fox iron", + "equip cart desk public bonus boring useless what firm anxiety curtain twice", + "loop blouse media brief patch shell wage cat witness pioneer young solve", + "arrow autumn crew usage cluster dinosaur know buyer riot security fiscal door", + "eager erupt ensure fetch rebel submit avocado burger chicken tourist ensure transfer", + "slide violin dream hour old spare outside hospital use wear steak summer", + "annual endorse vessel impact company dinner trigger monitor way eight update picnic", + "alien giggle juice employ network miss interest kit candy apology hurt small", + "question private pill fan object duty wagon any jealous tail milk believe", + "impulse fly oyster wasp name view stadium army display bunker horn weird", + "step eager genius better differ divert hamster response soda key method shield", + "name gold lonely resource rude century fury time disease morning patch usual", + "cinnamon skin option float carpet great clinic gorilla leader erode arrange tourist" + ] +} diff --git a/packages/core-test-utils/src/config/unitnet/genesisBlock.json b/packages/core-test-utils/src/config/unitnet/genesisBlock.json new file mode 100644 index 0000000000..07ad32e36f --- /dev/null +++ b/packages/core-test-utils/src/config/unitnet/genesisBlock.json @@ -0,0 +1,3944 @@ +{ + "version": 0, + "totalAmount": 153000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "a63b5a3858afbca23edefac885be74d59f1a26985548a4082f4f479e74fcc348", + "timestamp": 0, + "numberOfTransactions": 255, + "payloadLength": 55608, + "previousBlock": null, + "generatorPublicKey": "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + "transactions": [ + { + "id": "0b127468138499138c9498d356975c2aac194f5a6963a59d025d1e46fc29241a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANZ4tvGqebEWKtXiHgzFVCsv6KEiEGNupr", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204b965ee6b4d07758f4e792c4a51ebf922b7b3a88068d8c47233111f5273bb8a602204e23b4dfa9dbfee74f490a33eaaef08187ca5f59e182f6d7271db9fe47761a99", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "dabb071285c1f29d0f1e04a60ae3ef13a4189f46f4330bf85cf5a0f7cfaa0f09", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbxSmnyBVzx3FGMjaFkE5tdXMZZvvuH8wd", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206c48cdf1a1b580e277c938c23106329e3638d1dfe0e6e8d68afa371bc892b74c02202d55a54e47a49c2afc167d926b3724ac22349cc79925152ea932361446af2506", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "21f63109fd69efa99583249e1d8fe0fd794997186a0f8db3904459c2fc49a03c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQoAJwxR9AzoaJfJWiuJ1S6mvLHfKtY5BJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022042387fd0c622e648451ec03f7ce461cbe3b5b29d8e151492be6c6cb25b71e39c02207dc9bb5a45ff4d65923e603e7e13020f4e0a738c4892c1a2d36c45432efcc100", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "13ba85ffe79a702c667bcdbb0e01da654ea5c572180e2ef5a22b9abdf27bef7f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWoysqF1xm1LXYLQvmRDpfVNKzzaLVwPVM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100fb4459a93cc115d9ad481c51d91c338496d583321fcbbcde86a4024ae0591f5f0220631339e3dbc001686db05dd7fb6bd7f2d043f6c83a0901d68699e0cd320a1643", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5aed9f79fe9addf50ee78b3602963177d251e8db0c815f167e31d3f970610045", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbsErLb34KfWNLV7ChC3QheZpqgxdp4Fxv", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022068925d76e16bd56c154bec93ff537bdfe50ba80dcbb3dd061873263d43080aef022069dfb2b36fa88fedc146c24f596f343c84dd7c0a10e3aa8a8722d6f32f95d2da", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "be26cbdc41a2dfdbec7224b3b1ab5756532582fb4091552d9d1a7f351adf5512", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALvY3DscFkU4H5EXSRe59wpE2CHuHRSaqo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100baa4d9be7e79b972fe59ea61336275503620ea59c619ee04022a8606280887ec02204a960f40fbb570b58c3777fc1794d19c8099a990284d3d842ce4bae5749a2e62", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8f693d75d3a9a4d637e260feb73a0f3642a9a61dc983f52972e9965693f2efb4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALCLA26axFSNRsWHnECjAggxbkSEnU2ncB", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202b19f2266ed6c2319cb839bdc7d6eadaee9b3edebcdc8ce20afa16f556123fff02202bec70495af64a3f5064407b0fced6b5b800008af4cdad88bf6d8608c8c875c7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1238a1a698374e5eb30a94fc2c4c5255b6a3376650590ca79d6e6fb218e5ae07", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdvTKR9AT1MSALnaiAmvRBukvomZDj86gx", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202ba92ed764c96f7cf64e921b040b7be00782eccd18eeb352bdbc251e57b1616202201ef137e8529403f5a1459c86895cfccedbbb0faba50458319f7151751539a607", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f1e5dc5b8939ba185b44ca246e1919983718b276822a8779728ebbdf76437d04", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZz7W8kf7FZPaHcTT4CHgUiiTUnKK8hxEg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210096198013bdfc0de6adba0ba6034690e20c6ff9bb11b60c2b00cfa6bfdda7d909022072cb6ac164af87160cf81bb756d19a3d162261a2e3063b47bd26a407247e3ccd", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c27f4ee03afeb34d2fdb6d66848ce36744339515c8251c03d43d0e89a9615b87", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AV5DuSRJ7o37refVkVcFNkpCbYbsQQFfV1", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220634d0d5baf7cd36b4934172a0e944c53481df8fc11caf31932c1f8003b3d5b2002207aaaf8a0e260de26b1b786e654458e9665f4085935767600853a81e12e51d920", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fd4b0ae6b226aa21d03e8af8ca238377a85668868fe4f6dd4ec89680323250a0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQVGV9A42N9rBhgvMd7FDzRW4A7nzwzqMr", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201227ac283b17d6a7fa27bd3eef91f403c14565a29975c7c9626cc6580db4003202205f09749d61c4c2ec7bb517fc17aee2950d81564795357a432ca31144ef2b1aa7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "56747a89a8ef40e51a956ee432f2b4a2ec75a5fcd1df6f235e9001ddc59faf78", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANTzfMjau4R5cDdYZrbdbgkeXgYaAny7DP", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220546000ca4960ee70a3914a4425ce6cfbd22f7234354fe418bd1e32dee034d9bb02205eeb945c1a9e159a4001bb3543a835e4749e0c937da852c4775d46bef93c0150", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5884828e08e904645dae532544c347fabcdc01465ce59a2a926fa6f7ba960092", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKA3HJAnRgJf6pRtuR8zyUXXkAygiY1gYE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a34ce1541baa69ffc48d4d60dad3080b9cf80711597453c31468f96795c72e2f022061cf7fcf64d86735d7ab9967be6e447f82678ed720e00157ec6f0b1bde95c2ec", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "6ed0ee7e2cc203ec731e0f53e8d6b4b806dfcf451041cf648274ecf51b3163e0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Af2hTgerw9M8GHuWLC7PjJQetsNKy96XG9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e0f5be9cd1a2a1df0755d12358668ca303d4485a51b27a445622aae8b1c135bc0220144a1c224c575e908a68cae09706083b7ce079627d6fad464d053c34630ad58e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "92884f8bcb1564bd547ce84e5ff278c4dce197b8640078f754151bd8bb84b97d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdHgri2tVkUto6CWb8pgsxyW9ouSteJULN", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022043134d6373c8c6d052aa76c9d85b587e40f66dfa7db2bd0b5d3aaf41410f45c6022025ec2034b8da1c70a208460213efa59bbcc138b58a124860d93f1128ac1d3e95", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "69a7444676192bef05ee1e54f47cbb240690454effa660c6d1572242998d7bd4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFnRoALAnBAd2mZaZYGFT9bBr2Y7re8gCX", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022015ab48a2f57569412cfca817a20a22c98ba5581e5f39c85d06b77ef01322785e02203d95cbd6a11d1ac7978d13ba0bc61c75ee4d6f455cd57d11ec80d9f96ad88862", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a352d1b1aea8ca0f85d68c200f5c89af56657f9c4efae45d1e36275ef8b28b6a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALx5FYHiVKrUEogvVAaBHBWtKdrpY5u6Ur", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a6fd4821f57c09770f2fac5ea28d9301de531ae3d7f544335c8e603fc710ab33022007749a31e896285ab4f601800b7fe0e8e67ee3ef91b3bab0504fdafe9c8f3af5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a56af285362aee68b103b6378f7a69fbf755a90ac3a82b0a0338e25b5fd97809", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGYtkWXBUD3ohy7a8Yow2sqEuXZpmUWMA2", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220096ebcd8c8f202a79e8cd5d290449bf99f4bf2e79288d7c5981fef4bde5900df02200caf2765c08e80f7b328a0938118e95addeef950f32ac6ae66d03f351ba8a972", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5f6763e747c5ad03fe7688ba5b993f4588d811893a0a8b231317c67b5efc1325", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANpBNG2abwurGKggb8MhbiLYaoSqujaUfB", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220704878223a2d7e5dd8bf06ee62ad39368c3f61b4eb39cf065ee97cdc92b7185f02207bb4a2bae5c95964586fa6bc3141c44826f28b4c553e62172ee586ca32563161", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e7e958a1767ea6d2ec0fdd1ab11d38085027020e44de2322f6e0a831d7e44b2f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbN7uVxpy8SPWQeCtF8dJWbHghUDU85NVb", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b340f8880716565eaace21015e259349b0a4207b62d5eb0f60853f5bdc7f2cf6022028d7e45d4b06c390c014a8a154a2b63c4c16a8b79a99f9aa10fc0747712b3e59", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ce21f02fe0870d429a23e1bfc44a45d288ee3c74e405af5df4709028848b7716", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGuv1r9zRCLhe2Tk2K5r8miaEq4gKvUhwp", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220787b6da60cb2ccdde0ac18f8fbdaea8cf41160f02a2597d71113ba2bfedbea9f02207b074a2147bd9e5032c32654360dc70927fbe9563349c3caeb86728885d0aa7e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "0a12fc0b242e630f3587ca32063d85885051b66c1afb6a2a9f4af8c084e7908a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASt5oBHKDW8AeJe2Ybc1RucMLS7mRCiuRe", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022022c6980430bdbbdcb264da73b1c5115b192c7506f2909d17c3b2b1faf545dc1d02200ac1a5d496279e0d2cb64a2ae6879446e8788bc0d752de6dfe5ecfea3e9cc252", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "46214f9f017a321e0abb75331245698a74ff9f237523c9ce215ac65933cc1cfe", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APazxbmv7XuY3HGPv78dKBrmVEcwM7WvQT", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022077b832c799ce580767d5cbecdb11931f275eea20dbebd52da9887b1d14f819fa02200d627285c77f4aef89a4af226a4ac6cea5e47555948693600aaa0fca6cce06bd", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "6ed37d18b737bb47ebe3573f111d4e065cee44b0b464651afd7c6fd1a3ac64a0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Abr5cUc7zCXKAyTjz5irn4JcxEmihPG84U", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203a5610a2f5004252ac2cecd7a96122e94df048da7e6827542710e52b0ef8e3d80220037cfb81f3ae8c30a590cf443cae92f7d8b8fb14e61cec9d9f9dcfda8e4e07f4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2395164855b021ab8793d8fa437799f44c9c1dee5e8f1fafb194256fd455aedf", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APCQx6efoAU2i9aALsmp7VtKcSgteuSwQR", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201f66a789fd8c0f3546e1e4c3138d0dfd08cc4f4b02ead17da6d271f691e3f8350220156c2eb3b114af58a4f479f21a7d5f3b9d42b27f68727bcd4e8d55e3eefcf556", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "dabbefbd9c7abcf87ac1d02a63a93bf705851e2c18729c3eccc733e5f8a13fe0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKcewanzTJGJ6akaUBV1zU8EVgFcZ6L6iJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200c59e7f29cedbc614272cc54db38900dfd2ed3e950c6b09b1e05351454ab18d7022058ba7ebfc4608b0c1dba7f738bfb9079ad312a2bcf582f7bbcfea2c9d9154512", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "64fe150acc22603fe1ba7e2b36cb9079fc4416e98459bce373a50e12a470a2df", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASQqwPSZhBpeaMRt6FinqfDRLwQNUwdLou", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ff5efda54a16a449738762ed1bfc3b827fc445b45d08d5f24bb7273b17679a0202205081d641f5439659141865d6326063dae072bad99653833dccd3140ea16361ba", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fd8c6ea2eda8883bde71e51afe1e6d41f38650cdc1086b4a2d22a8a754b9b0e9", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdyH7jMm8yK3QpucKYbrxWJBFfekmSkNtj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200aac2082a2ff8f691a6e5fb78df2f5653aabaff8bc820b54df055cf08d1718d80220434d25749c41b440195a3a7a740783d77ffe651ab7dcb0bff87cb31517cf7b90", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d32ea8d330f50c1a9e9d2cea2848470567bf95cee87c7e149285af5da1004a75", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASChm8xtb6tHD75zfszZKsscQnjXk5KVo9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220246bda16b8733b126c70ff18355dd415d67108e4e7f70ca655eed3acc5c5b7170220248fb5659db3536d85d5d81b8edf9e79bb3da9b8bdb27c30d1d7832d4b144114", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a1daec9a51eafcbfae35c716d9a2d923746e076d0ebec2807afbb2602fab34d4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdAd7MpFF8HYVL5PkZ173wsYutFiWs3svS", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022028936c369540f3798b93534ef30af8b3598bddbf679a76b6ec6dc4dc23485852022043e69874473c54d8312981e08dddaa233d5c2bd9b1367da43605f7dfbdf16382", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bf0fb0bb99384b54e489e5d014ee7716e49827ecd186dae3f27e724b91eec56f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AP5j7BWDUrncx8QRedbFKrxTmGxa3n4uSg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100c199667870fa4cc632608940f31bb7b2b0cd203223859cb540fb5f0fd58277aa0220767087cb7e0c2a90516804ff7227886333c4948310f63e2bff57f139ac2abcb5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d83bdd38633aeb434e88726c64efc21d974e1889bfa10bdf538987baa5247670", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKFbuM4jtwfXCTqCJMXin6Mp3tTubSxPya", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204ba60a594dda3c1a445908234096823af00ef0f2f7eb1da0aa94fd5a23989f170220342c45a45c0778242f6f1ef80f44446f8eaae6f2a3a4a085dc5976ee2bac9352", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e875a9deb0e058bae1685451373703479fc887abc0c26ddda6b132d12b10b33d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJXSVBkY4A65aDLudX8DohM6f7gyzYqLqF", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008e9e915e490e7aa1d969752f4932b595e9a8d5883a17d87883235d44753d6b0002203ec87c347b447c8c998e62535897df5f93bc2a5428616db0b165c097358af4a4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "30a5c19cf5e938ec498f5225ce47aef762cb0b8c9f23663e6646eed890914d33", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGPnbhUdoCoqdQWi6trWnCjJ3jxb1NuZYo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200f4c408046d46d8b04deedfc2f21b1dbd7988c8c5d5354b4aa371ea77c5babc90220571bceba36616dc7691e29e6b12eb567e74e20182d3b8f9093737a9d69c7a8c2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f84e2c788c3c1d5af4cb03d4743a1dd2fbd960f50ad3b35c4a20570388038c72", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMefC8bZdYZPgYjcnsEBgVqR4ZXGt3HEmJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100add36cbd0bb4f52721565c2d8df625965fce1b5f6a97943fa414a7918d51c429022072260aa30e9ceb9f59c5474db25885b8d888853b7a9af2a54d059907a3196583", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "b8a9bb6ba45428ce26df1870e2c7815efcffb161a72e658ef5ef93c931516fbf", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXysAKqGRaUaw3XrwXpj91b8CDPY8buvQA", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202f0d8cb3c4fac91a4c0da11f30a594433906638ecc37dd2877b14d5188d7e7de02205bd9aec0cbef58f02861c864be3b859acbc48863682d60e0714bc874bc5d65f7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "9e0b4329be5764791663357a363eaaaa453a51e0e6dadad385c891ebd9be4b0c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ATF3vBJKFQPKjNyUxieoArZ8TQgso9GGMK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100fe8b71f3cf816b0aec223486ad7a983449081d98c4a28cfb60524d1350eb28a2022030dd6b2b1b368e1709294cf05e1e6e2626e8eafa0e65cd4fdbb1a769c8b54d6e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f6ad0edbbc1a13418950e41c5bd88732a5ba8f84e3ce79c5a2abf286d4ed8e80", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH5S3Pc7it3gEzUUJN3bZePDzrNN6pEjcR", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b1da007c36b376f58f4a9b2908a4ec443c83954ac2d24d513d5a848312486207022043251f92b524b5fdaf62a3b8b796df0d61151743dddc3d42e03f5b2844892567", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "703926c801192eb4003dfb341269fc43cb71b7983cc789513d58e005cb37d528", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUP95N5fHeqCuvXVXQZWZpxm8bj77FmASL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022036cffb519c2d58623a5ce2793c49cfd2e064d6afe8b63f9bece927dd9be3337602206e445e277699f2c84e3a4b38ea9b3d63a542dcc88ffb313dae7d3b998b43296a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fb5019c3134408882602ac82be0e33cb44de2adaa6d228818c55002e4de79308", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQko9usk8N7wGV6VF8QsxXtsqbsm5YdCDL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100c5ccb793dab297ca6b9bf7aaf816492209cb6a4e76aa7ec669a8f4953436cb0f02205bb2764c5ce25e2fcd78bea82045a4d938ca97ad73b1cda2a0e217330677fe85", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "9cc49b610292df5549f617559c717d9729d322e221cacc3edc133b7a9a445c89", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdYJP7AmU5DJfsmg1Lycc9ytGAbMz2wuf6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e9d0b9103e3b7039bd2af8c683d18b9fc80b3ee7a37fda7732eab83f134b9e6002204345e884a6b57e7a60dae33ca84037786982ff156ebca0b7242e46f7564dc087", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d6d58bc704c4af5aeb8099857f6924eee24ffe0a006245679bd9b3f2245150f2", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARyFEoh7kXmPjDjZtbbGQEm3o8rz9bxmGa", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100f2eacfd6def9d0821cebe20d0a9ebc39300ef04123d6e400d43a1acc9fc395df02207eaecc1544bcdf2a7efe94e8a34658c8bf85bf5f5773cbe596d8420bdd08aaa3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3127ba17809631c57c9eb3222f6d7d4f660e4d182485cd43bbcabec11add28b4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJ9MXucUFg8wqPCNmd32iUZmYa5roXhYiW", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a3b5cd0f63a58b4f8ae324e2b36c2227d0473e4f9605850a8164670906432c3d022005ebcb4a9dca2084612261da00ff98cbabf26cdd34d0b2002ecb025958cb6439", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "277ee179972217a706d3acef74d06d07e906ef596e916034855e4e7540dbf2e4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdXg3Gchp8XBiPUdKCH9oqCgYSxbezXNdn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100df31bdae55e88f356e6aa8cb25cbb4e15ff7f4193666fcfb7385d8a22963c860022063a7675978379b15e215c0ce6a0411070b750cfcc3b7c706f98a826317d0de3f", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "b8b318753433e67f9a7947125cc32a14ae2cad94bd1896bd40677f4d4f4922ba", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdFL6sbWYiJG2AFsAevb3juTiJseEH2rrr", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022004dd2f18f68f424c2c69e01156e39f222e26274b642c6cc5ee6b66062304796302201fa14951b0950a7234510da425d88a1199e9501f033ea4fa32c4c1e7bb1d3d78", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5df90252e3727693ed41b9be836ba413dfc471b1412283cccedce8555479cfd0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFzhSbX7MMxZEeoz9mNFCvUeNX2iBxNK5o", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a0a9092f9b3514bc48eff7c6bfb43c0697eb48015fe7897ce48e8f386e051dbc02207108b1ef48e65ef7756ce002392a6f8cf59430c18ff7b3ce96ab8f23d755fe7a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8012e6f2926c4ea1962cb329f19a1939fdf2d3896b66358605cb667cbf7f43ee", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXYAfJVjupjT29R7g7V8ZQXGtfBAYnMFZn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022032ab7465a76eda3028b3798a4b6c16885171e413d6bf3b072d4954cc09fd620a0220530fa60b84febc2c2a185aa911258730b35108b924b71b7754240af795a6f975", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "75c5ac9d784bcb1ab2af4325de0844840ef0b08a56717ca85beb3aef7fa04c69", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJZeVY3wgx3VwbPs9MrVXWz1mK8quD6omE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022023035af672aaca03ba8ae2a15e18a30565df3e4746e02acaf298291db0678188022048a7832778504c5764670543db3b3f5e587857623db2be66a6181b1cd8b3ddb3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4c6995634cbfd7ec71c6017f4cd36b0f38d35d9dd2eb96069dc7cde68047e994", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AcnNYB6HhHzfwzfyquZTXmn9FCLPFsXugf", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220750b33fb08446db47bdc77cc7321ba9f108b077a7fe4f6c03db58e876ae07c050220054da7efa11bfbfc06fcbc04a0d704c6c8c3cf24fe113f4c5d25aacdd074dcd7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c95be8065bfa8eb0bf7d475d4bde55a1f1a0b4f740c773ff05545317859ff6d7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJ1bRNQ4onQPvs9AMWoKtheVStwcf528zh", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201487f7a73feb70885ce25cc5d5bc0f56da5dd6be347a752b64d77784d835312a022005733ce71d2fee65c95f80a62473a41dcfe4e5345dd5c141a6a72eff5da6baab", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "301e915143b8edbbeeb7bb7333a38ffb1888318e57f3feefde3c2927ada0f635", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APM5mRCaLXyifdsfmd3x2SKdnq324aW5xT", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202ed51c6076c602e665372e30e5c02591c3a901da816772d34be845e3928e467102201229bed0e277adbaa75722991c86ef2c5ca6c057ee8d22bf5de5ce2b9412a835", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "86172795f61762d31fe2886372e97025199002f176479e9720102a7684ade9ca", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUqfHo5psb2xs2vzfikJVkJgfUodjreDuk", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206f5eea1e2aab48a48269a3bfd45c976b8fb5ec2438a82607a23d058bd15277ce02203c234bc81ceb7e439418dc72fdea94802f4cde42ec0a74230910df6f9dbef130", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d1ce4a5803bfe9b5a193996a0baed2eb69fb25902e1af1972396634315afb8be", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b0d237f5e4d7fbc844fdf22b888732c7fdb34fef2b22eaea5f5891716ec69af202200a62c1183f994792796aead0299ea11374a1d06aa3fdc68f53793ab27f558fc8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a12c680b5156c3d93ca836555385632d6d34198d80ea8e417db2f27b2d2298c0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASjr37NksJJunDrwkCPKyZRANPLPxFNGSJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220746df34e38555133a36968218c7cefb46e58e471139e11bdc4909cb18c4fad4d02206941a01b1a02c5bbf201fd82ba42c68bdabd34169292c3c372e36808d2194b15", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a3fd14746d1b31b010cfe35c904f80f70b538e3c2842866861b20667d2bc72a3", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGvYueD6VRnE2D83cU8FLhUzKHmZXbS5Vo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202af2052045ae608334dddfefa8d94fc1c8b994ddb6dd9876dde9f076c0f7227502200a5aba26a3170a5ce8ddb530e87527fc7a9f81ebe78019049ab37accd75a30e4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "01cef74d15bdf0f0889999bb2fde52bb9465ac60680ffad1c898a31129fc90b6", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFv8tBd4biYfZrAYpgDmcvAqgdBi9y5k5c", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201c84d773fba9cbb0653ee64c07f0ee041761a3520c81988fd0afa8c58b1b57140220114fe2f3fae89e52838ee5556138706ba90c409ceac1ee091c3e3eebe9c138f9", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "513cee5310e9357620b371d1a17aba3b5c360c44521c08602ad90222c43ab2b7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbuQtgGCksfDowEg62W3R6fN8iUSKEuQ7p", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206780dc95400f62205fb1ff95b28fefdfa3e4f3bb1c2dd8b989ae0a9598710b110220788b984f9b6aa33772bdf10783e03d4ec8ac6a6330a7c29a61603f8d80f1a100", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "671be425d8b568ef7e73102c7ff94306647fe83871a46d06929abb18d58fa2df", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGkZtkcyh8i8wDoiGm2zHvfLzZrhWc5V3w", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100dd428faf70964c81d43252a59576fb09e9ae0597257c023563fd07a26b2fc17b022074e6f434aa1c58c4e025f1dbecc6c66d52dc423c2713a8ffd4631c5e538750f9", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d1976ad729fcd16e577c3ace4fc2f70dae590471b4668fcce6383a67d2ece5cc", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYXnBEVyMzQ5ujJjyxrGnfexubnAdc3Xi5", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100da0dc3f1d42076c3e6b61294c1dc5484c9107ebb39d77a2f7929690f498a253d02202280b49313bd61e55c92e9b623fc74657e353bc7a62269888ebef38fe43a9539", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "66ba43e050831bc5149b69908f494f4ac2945d42001d845d0d8c30f4ce9d58ec", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUNdcLwCjM1n3oaqxMKmkZPWtdYPNS3FgM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022024d56750e148d5ae545466dd42dd0eb18bfbbd0c07a2c6c4ae244b7b2286253302207ca7283b8f8b45269d99cd597b3dca72cddb897819330872f2567980da6dfe40", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8cd52e0c8077c04d8d8e8e8a50bb11aceb0b150b342cfdd32295746cceb2a56d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYUTobeLMQdRv9mgoK3JSfBriNi9iGS1Ff", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200fa3d8377e738cdab0c4ef2d00395347261dec3af3ea06dea480dd747d3b4d3d02202c27c151b09f9088789bcb7afa5d98913c39940c3c82bf63c7ee5d859ebf6b56", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "b5eff427afd510d0da18794e7e548091cccea4d1268135468ae7eead8349efd1", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbW8nwbHoDhtVsdVHVVQLy88vRHfySnEkU", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203bc385dc273e65a5eff2284ba02ae88be7adc90555cbac01053fbb91a0d4ba71022039e5d47361eb2b6fd8d9f99c5ad8231d78b1f5c84ee9e3a023b5607a45ded74d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2e8ef1ba0928cc8ed9a63f7ddc43df60ee9a556d4c151ac05446a3f50071d44c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Aa11BRTWqZbeb4jXAJkGqaQ78SHmXikVrj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bef96c0c308b9dcfd73b3e8022438a3623e15b5ae3864956cd75ec80ae68ad03022019034b10ffa4aca438be4f5fe56639ddbb59a50994df187e5ed0bbe0856fe00a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ee7dec36da049130d5d9310a391e24f75620ac5baa46fba1b1486fb3d242cbc5", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWGWuovUFcxKNzNy5An8AFA2JfxfSnEGDK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bd6e15fc5e58445b9c5b0c67981a31206e697cc5a846fdd00cabb9cce556db5f022009b69719470f1ffe82da63a9f4f0a6219006cc769513d4709951cbbed801f5db", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bf902b9dfc89e78332e12a81b8ec7a017b6040bbe2e2d979969ce449937c5143", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZaos8cfC2u9TLw7mE3qMPJuEtuDLyxtVC", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206578ec80b033f4ac9a2ebce658fc7e096e49e4c60ae91aa191369ad3570b287302201b359dc9f191963ebad6dbf819ca0c03ac341373767153861c44c4cf68933e7d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d7d4d20213df9e6410db4b22937a8d2926130ca4dad5c0a466edfc06a04f4519", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AaP3FYebrQXfLT64T2r52HsVEYXDU41QEk", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100aa0af940a123257c5d1e3f06d7dbd0f537e03374ec07a2e8cd00f1651618c1cc02203531bfc2578d9df5082f87f7ac86df4441157cecc6b451db69d2a09985e86f22", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "7946eebeaa7b312f726d0ff3e4184f3087694c5567b9f4a218562ff0251256a7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMnKgkf7DT6vcyjTBs3gPqPhTNJ2z4oY95", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009cdb480847a9ba3559f5a1659ecb2d421e533759d04f5e85f7118e5749ca52e902200685560c698960d088a57b2dbaae13ba32ed8ee0c441e0ea23b45ac64e523ec8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1d06a25bcf303c4717c1de9906e0266c27d1de70bc226e32e85dd6fd69cfd208", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Act3vdrPTWgJFoRaYdRtHCZAwfQ5cJpbF9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204f8ad4f936551545bbcbdc0e52de66dd7cc207f3740f4683b6b3d72a101865c30220546840e876e8332ad07272cddcad2bb1e1eabed705c7c3ffd6e4e7918770c536", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e876c22b4d6fe9b4df652e14b1446f5385e457ea8ac23993b0ad32aa8d352704", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH9MHh829oyDk4f4sJhtEWvQTENY1P215p", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100df982190a2122f1fa4d64e450e717636d9135223e57983bb0c41624f7408f40402202704731d2e9d480475dcfe935adc9ac88c1e3eba28532b53de9d99640b0d8814", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d59f887b226f6e72876bfe8d2965eaa8938bbe64e90890500247ac81d9f2562c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APJYmiKAtvmPQmuibHB7qab8EhDoTHYeuj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220728d51f31d5852429dee752acaccb34df20f47cbd65bf42820cb4d4e572b3230022071d16f6845ac42021e5f4a2e32ca6cf6d276525e177327cd6551f9b615f22f28", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "91fcfb3f5f449cb13007e1f12e3939d08d41b220f0af1e32d7045f3454e82c97", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXn57wj5CCcAts34twf1V9oTLVoNuRH1YN", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100afdcfc39831803e5e2d01d368e4d582657e1bfdf38ea2f87204fe30699dbe1d1022070c91f4b9072b56a3b8890a6fbc8dbbce5b291aa828da5c17fcc1f6c5d387c22", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8833bad42ff07fb0a8cb4faed234c9a3e1978ee22456afc3b30fbf9e688bd345", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH8LYTdW45WTpKL4vE9TycXfoAYQvBstkn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a247cbd500187e31317879baac09db3e9f5144b1267a20021357fd73ea7284bf022057e3df5c18fdc64fbc99d86650625a514f67955ac47ac482218dd42c4dc81305", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "642b2728aad41b7a86fe457ffee0a709be9eda7caba04c2dc497b2daf6289f93", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMZ8udbpMyJPAj3qwWKGQmtTyfoorV79Yq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100aaaf8083f7a3d13bd2f0222762b0a884fe25293d64f5ce7bf08967be13ff3c3402200acc5bd9620a8e033e0a4d11023b1ddfe6b76b0399575d80ebd7ad1d7df467db", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ef9abfaa8d8d816226eba4ac8bb90f89267b9f2bee5bf217171c478b1a7bdddd", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AeS8atkPDW2y7vXo5oPtK7ifTEYKytVi9i", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bbef8d139a18877df8c9116dfae553c578e98bda4c74cdd95b5df3c54513b6b0022039a8aa83f664016fab85d86d45339d552aef25dbe427fb71819016fea0ad9819", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "08de85ffbf62a6d50669373a68ac792008348ce88244c2c21b2afb1f720c47f1", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APyHyZ16VgCEgLERv86jUx4qYjCZH1NWjq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022058c38374dc01f83559503e3813cdaba2c75da25147b0e564a11df324b1a14cfb022037b1c9d030f8ffa0168b99a395129bece2cd7f21bbb86647a0d91a13cba1e523", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "65300ad39080bd1f96bdf896a2537bd658594a13eebc6f218359c344441b166b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AT447ubYEVkZjS6MesqrRkha1CKfUxVzmi", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220041760cfd3facf18334de6b79c24e6d1e7fdb0bfb4a2f827d0d255b63f68945f022004af0966e25d7acab8b9ed497a5a3420c03f8df10ccd23f09ff4dd5976d36d25", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "75b22e9f34731518b93611f942174a4d3f93b34c47cc6f82c7a6c154f0f46313", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANwc3YQe3EBjuE5sNRacP7fhkngAPaBW4Y", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100df1ee811cc250d9df6a0b634164e7508edb09e98573dfd98d5df9461b28badd602205ab9815a56979e7838e8e539ba4c575357caa26ad9ade783686cafd350ed961b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "0510371b21bdacb08ce194fc533fa82aaaa31fddccbc439f2da8f93d5cbf34ac", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWcwdfqduZx2SsdW6JDpvYqnDT7KbrQ7pZ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100c66b8540a76bd0c2d9cbd2ec0aed0ec31f7e648968c7180a26320072e9cc9c29022059f3a3f8aead4fcba621e77a59c709e89738e773a3e8650b243d0f5efb03e31c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "73ae2a136fe5085970a1ce9e1522cc07c0edffd0c65cf77984b264eb0b039d44", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AHsJaHCVtNbZHprRjZjutEFgQ4LVbxkGCJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220009d0a46ff621e3c73408a4babd9e165927401c150e11f3bcc88501f6842161c02201fd2cde89f1d7bcd524a6a0ba4c1be23e0dc45c8b3b2e7671d9dc11dc5235653", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "06f4158caa35b8e3e10440f544585ecf91d73f78ad3f597570b5294826de9e05", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Adg7FvqeGvnNM9UsvcntypKQnrPEymXhQz", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206c213188492aa5958a932d675c529e77283fb73ae93c9e0e7618184daeb621de02201d38bd6792b71084ee757866eb4715f046e5bac784f1c7419850cbb385d73c3a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f54bdd4e36662a5e8fb6d7453d84d4ea37ecb4741c9e8992d771abcd94c009df", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALQf9fK6iT4RSkwimHepPZ2p2jViAqeA5S", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203ab6ba58ab71faa8d1557340b244b90f7f61541172e7d1dffd0de5475dd8d660022079cd78bd8ea40ec271db7ead0c2bdf65cb7334f9b00cfc523fef8bbbecb92786", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8476b2dde8f481cf18a74104c80bf47c04a473a64b2462fb89511da17e2866f0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AN2cyuquL84qWSQLhEXifdSHod3Nd2a8E3", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100af39fbe4d556c35b5832c8499daced22c6c25148a4c6491734eabf31bff455e1022058148961f68d8a5b94faf17f39947440850b3980de1b0a5d16d590fa9723e245", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4ae0f0533b136bb7bdfcef5bce6b9e380cfdb67e4b10f0eab90727cdde30ff00", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZiXrft2ugGCqhZSTEBgnqpJyL8Tv91ZsW", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022017b878789f1dc5e12970c3ec202acb7b6fd276aeed4ecc634ba66097356c468a022015109996017ac0e9cd3e58ec48c0c9ee76d1fef363110c0f0b95a1cab1cb236c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5c918c703ddc1fc9c173dfeff61fe795f547f97f6a773b6523b8d7bf3a5496ea", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXRk7ZtUZpswU1bKyPmKAyH4Qu7itHBRSK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100f10f5e41f00518a078749daf819a5675f4fb365d165b4c9cc271aceea9143f3202201f3dd4c21117a34188b8684ea76b3f71fe618b9a6afe08e179fd331f0237a0ba", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c42ffd89413e3d7cd3d1497e28a4ce30898fa5ab48869c36640d78c18ff41470", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARYSXqnkS5uC876GTEtfQF4RbJ6Bm9Ytm6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008c878960fa82bfc3f8494f66561735fa53ee107a85b3dc5ea71e90ff9fe2120f0220459efc529df7ff01f23d33aa521f93f18ecc7666fed4b93606de26101587d4e5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2a1768cb587c810d09d97f08b73fe6e9e729d87833535b8e56e8ac3400cafd9e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQkf1rHnN3n5dtFsT6RVfEdrdFf1tzwjH6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e9d965630d1f329baaa6d1cbce306d2cc12dbf5bf825c3d68ad437f5473dce40022031e7f20890ca0028785df1b4eca780b7f70b6dc83042c10a3a2a79242cf65715", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "952732a35e6fafeffb9f4a5eb64724a75a9c0a302a56607fad2f2d92463421aa", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJnvt6gphaN3Kr5gU51jEPkwdk1GFPnxAe", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204acf6dd9d37fc95faf34c127e561a8046bc2c2f954f39837eb839e5275b333540220299f166d9ceac1190734744cfb2bf5ec4fd8c9a9f372a9edc77f58811a51c1ef", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "216daae674a4ea45ce17330a777e33af2e9d2ecfa1fcf623746c01a85af601e9", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZKmryqiPiowe79yvVQ18vgRTLsfcXuZ86", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202d0a4ae36c491669ec7efd7d02bdba56e111bdc5c650829896344905ddd93cc3022064773b2a4399c2ee797a10fe152e23cae43b88db5619aa347823013e49d0cf23", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "eb8278e2e2de773f041adcf4c875a011ea47d94d0809bfe691665723a12f8d33", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWoGgnki5EqT6oKB2gB5xa2FSxnhwqtm3T", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022036fe1ac60836edfd0ed4c48514c980ed7b04cfaeb1415a5a2d186a3956d0e3dc022062ae95e9247fc245dbee957044c11cf68619631a4e8fb39880140f5f340edc05", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1434a6640960d6f35fd14f36d0bccfc1000106c67265a9f7632385003ad22861", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZRMoSEx5YBC7P2s78XzLStbRigMe96HrE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009357edab598aa46f07aaddf5aca8322adda62df4456d84c9f6a291f662571cdc022061d4ee2a09e39fb15e08c9b5024e5413ee9c4b76849290a8ee76efd9fcc0812c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1f47a9994974e14e6b939b1081e8f61debcd71c6c351be1a8315216eea11d6cf", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK3CFg4RFwRydgFb2woho9TCE21tRkzsB7", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200f0d1cbfffe14a34cb82a2ac093be93b501d82eb22ad9cc21ec2590034d83310022040a8947034b408244a8c5931409558422b762d225dddce2bf35977b30ecaadfd", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "38d6938ce3c2dff20e5b041b792b0bedcf77d098f7b95f48a6417dcd367aea45", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZUANHK3Gkk9yGAXmebVkqqRokJuzDNZyE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022029ae998756536639e8cf0d879e2be659a843ceb181b44c624b091d40b1e155f702207bda16f92084dca94e3270b977e09a17dd4f077d655ce9cba8471ca5927869a7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fc6bd895f874e798cf41b20bc6def70dfbb7ef8cf1bcba29cd2e004bfe0c3085", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYzLFs9vPy2wC8niqP4eEamt2eGmTjsyDk", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b97b63cae8c1b39681724638990e30dbcc57542b2727bf65769e83f5872dbe82022038d55cf9bda1d3bcdf0c3c788eff3b3bda1f9400dfe47051142bdf6d0f81dc3c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d96c14bc00afbd396696ac66b5601dafaf60a8afcb4206433074854718ea89ff", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AVuRN3XiwUTwTrwTAds3sVN1MVhHUhLn4X", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bced1ebee9bb9bc7f654f95bd084582ea99c87a9ed0617ac98f1785d994c5e320220436d176b847fe6dfd139fc30dfef74346b7b1152f43791a61debb191afdeedbc", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4886ecf094a0b5134f84207e4832f114922efd93d920a612b0d87110e359d640", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXRp7afm6g4ZYscTJkCmB9VfzrpwMyRr5h", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ff42e460789d4ba65ee6a6211d1145235d5af1e7a38b58328fa9b7b8695bdac60220322370d83709a33f3f5a035a26ae07be26b340bc0832025ee9eaebda46870065", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "76b352e8320d007d120b225b107a36e3ae290acd99e483a304649c39e0c28bfb", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJn7wi5twb3D9UUPBjV1Y2XnCDhVot8RLp", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100d89c2028faaf114737a93afdd63f7baa6aee8335d904cce2f8314e50f3b7407502202dbaf317c685124c5402866f24b3091f2963b2c8f8955c005278bd2abaf4c020", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8967abfb69e7e25ec14ab8ddbdcfc983c20f6e40e297f8e2f05225cb1e806747", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AckcWKfPcT3xoYzFALHK7i7LYTTM4cHVq6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100aecbf727a2766ac2322a8475702a40f1d15030ab97f1021d1da982e1122f2db70220709d8121129761b7f15234e3301dd13918b6fa881a111fee061c80b6ab10fd92", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5ddd6879bd58253855aa17e49fd6ffc8107fca1310b3566cad7322ac244ce3a7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APttZLM1BYZAvkxh9LVJt8XRwuJZTXnocM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220605bca4598deeef380b50b6804e561b783cf53060172d5ff62833d817d94fb7f02202da5a575a7d1e9a2bd6d30771c6ea0abeff05159d47cd3c0de222e38330fbff6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ccfe66336c2b54297cbf38059295f076a6d6e50df6ebf36ed650ca283ce9d30d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbHBVzCfCkwiNky9zDmZNNTHx7ZCnpgHCH", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022005646a217c3873e24e28fce61da19103e5bbf8916754fe03af204f7778753185022036f19ea6bdc73f3f7f72a007dee2fda49f9a016f9f47a3006f64d35235083bec", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "07fdf03a4dc65793cca4dcc55c24e10443468097b8af85c48d781586d8fd031d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AS1dhJTJZMJHqea1zqy6h3VnLeDYEsW7Zq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210096287ca81293bf79c425d5265cb438cf06967544a4a4de12b7ec7b2a71d0af4b022058ba5fb1af559e67671877ba2467d16b47c14665920fd07a51a53179a27ef841", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "078d713affb765221a9e1c1277831b824d114baa2af9d6c0d128d85d7a83ff36", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMHwi4hvpkhSaet9VDoeCDYMMTh7hnV2Sa", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009b3ba150626e719639bd4ecd82450ea5e95410f1dbd0b70a4c2e183e9909e275022051cb68725df2a26e0a63d3a3d60939d60435c7310d918e32fe7944baeadd2bca", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d01fd5d1d0ae1b12362288e5a0fbdfb4449c348d621e975ef546e8ed01e2f614", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQKURU7zdHs8tjEHWLt5ipAXaGCgYxknvM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009f4e8cbb5fb43bd8760d41fd338e7d75f263df3a4bc395d5cfa4f5d29e4bc2ae022065a9b07cabf68ad767f0c1159400b5a8f1ffb65be9c2c22758fc26b911a02a60", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f8afcd4d151b1086c0fe88e5ea1181aee6455052b01e41fd902d057c7e207df8", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKR9GXZGUYqz441D359coxztJonviA2FRM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201b8587b3030213230c38bd19163a1905cf8f0c8ffb78cad152cba66bff387e7502207614b30d9e6b3afab2fcc1d85b10437e44d09b4af90c1fe8fa5038bc1967b900", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a01ad978540a34e9b9c823e8160db6125487df5a70186ca0d28b9854618c7d13", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASn4LHMaH819aCuUwynTBZsaoUwG3uxAhC", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008979c6c1670d77c84ee199a136c8766089ee29bd9fac227a646bfcc5d3f1a0fd022062aa994f7214530e86cc54d1e8d2c8bc33080576f94306451d1cc2bf5d5a8b6d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2d1eacb315e29c888f6fdfc141174dd3096d68aec8b4dbacdd6685d2454bf4f3", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ATMnnCBtz4mYEi5oEqkCnDWk9RkaeNLvw8", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bee18db9859f3eaef98cb5250751ebe583222f7d348a57295eaf66225f302f5a0220791cee851bd19bd1977d35c9fe5f0e6eb5e93093bc72257d0a42868fbc26fd7a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d77f1ebcde44ed574f15837ca11c4a96003ce4c6269b6191d28945b8119eb027", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Abu8suR9L2tD7F4gAGHMaQqVbdvXdCFfXG", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203fb66664a3865147d16440b6717c51f6bbe8ddb66823d2f747b40811613bf1ec0220032b2dc85a6ba32dae5f0f26b0c4bb1a5be946988b4db43e853642d7687a5aff", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4f8dca7f4891b8ef5f19a8143459162a60f169fb73e5b2eba3864b5d534916dd", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AL6sZNMWm2VGABvMX4F6eVFLUQJQyJup6i", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022014cfe7dae7e2b16c511d3d46ca0d5187b0d66eaef38fdcab4d3b4c0ed033e86c022031a57b2ce96e90ad18efd72977ef141d4f8c41d3ac65b3105acde652ec85fdb5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8d47f1b6bf907d0a4d1519cc3bf0751af72f478c256b387de484956b892f3a58", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFyrB1QJSh7enu2JpMUYEKUgv3xnJo5gUJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022062a1de80a33c3b11b033ffb5395201cc1a35966c5147bebf08b5e8cb25b3fce80220597ff97c23e9a0f476deb1ef544e560930d7f3b10d038598124b6dc997ea25b6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "94bb60eb4cd0c06c518deede1f34291641f690c9b9e8afb708e63229b2d11c66", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AVQ7sUZaTDi6MgKNuNxzzC1Sn3ee3KjdNg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100cfdcf2052c8d9b7fc8cebcc95c0f478b7ae73c8f3c341c83ec8d53338d32092a02202746050a64e9f885b5a437ea15149564edfd95f5d64df42cc2ae2cc9d6c06a7d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8d78bbdd92ec1f5a3155b35091e78efd2ea609390e89feb068ba8fb2daeba144", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALf5oWGQhq2xbTSZyCfDuvxbnDSjvYqiKE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402205777cf25f8fc2b1e4f49956a184fea703cf8d23dc13d463b9ad00d203ac72a0902204c787fc03df8cb5d1b3a123883e75795906f7ab86c648981057ea9cbf6ad5216", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "7f3ba8a2764ac7763b312502c288aa156fe03234b534505a14abc7a41bd97b1b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ATHYUiLRifNafWrFLAVgQYo9dVxntZkow9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ec3a72421fb511b8862d6c57aa99e5f92ecae3663e2ecbfd18460f74a29dbdab022059b0ec463c271e1891a31dc30fdf8aea925f19c6ac94b8a6d4665acaded60e7a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "85b5107a1574d236ed625729c6e6f30fce0ed716c4713af97795b72ecdc25647", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALsRw7zX6PeYFVqHpJYeVoeBy3a5mjXCoL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220572d28ae4915d0e4f3b7d0f067e804a3b26fca136f64e52e1c38104f63a9a30d022029e5e9b5ac89528b912aa10de9a82499a9c9804cae6490ad392aebe8202725d2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "9e8b2eafd2065f6d2b3cc9803539a91371308ec683d9ab555cd90d4f5336374d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMGsRHFRL8V6qbUz115q1Wc8pnXBScuS84", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200c85feb2cd8d220b3058310a8eb5211de3b447c7aef7362c9db6365782119b130220276a65cf28e5c13b10cba7356d5ecfa9d534d9519ede5c9bed6505f1f6b0daee", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bfa961e75f14d9c59a347785d2173375770edc0f1bf0be8e4b1f3489a4651c2e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANLm1RbdAqNcM5PMBS99ARjHrvBBfTykZM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a1a0bb2a4673249f97ab09936ccb02741c9c26f34eddd73fbe411dc6b7763801022006aba02b3064692f4cef1da3407c422ef37b6b955b8523bf57b0f33184cc20db", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "26451f460e007ce959cc3e3e523767cfacf7a38daae563154fa6d307e97057d6", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK8aUnLmspWtccNwDEciSBf2BoxmpRPAow", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220519c73adc7d10d5dc255bbd99591c2de921876f7ffff166d4a844c8fddd9177e02202eea63bbf9895cd42e9161e68f0705cb1a948e6c92938afd66584980093837b6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "55d4f3059db31ba55349992c06edcb19fcc90d0774abeeb827232bc592a818b7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGJUdoyjXsfQNsDa1G2x3smFfdJEPB8kyn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210081a95850fb03a338de66b910596f3a2393b71c9639b479f72da1f0bb9c8abd2e0220417847f1a9a9f580bbdb113e2c33a60c1e731f156ce1ccd68b998c1e78d35416", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "cbdc3d4f2c8e6101071807b0a6e772bc70dd2fadd21773d2f7261076964ec89c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Adm62zHj5cUuJQKsbrz467exSVVgk1bH5j", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b483316f70425b107545431eaa5cda46401b90c058d424ebe7a3e79e669a3d700220449be77933d84d804620cdb86b03274ef48f2c6ce19551c7624a3b667467f094", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fbd8e580d89cb7ba39eb22dc563f4fba03262961481b21e2deded869753fa0a1", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AR7wLy5nU2kML3Wv9Fdot2RdHAZXn7ftTL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a5fb7b61e2d383a183c3df3b5dcd35dc117817f773b0a559013265615b0f3ef102207bf3145303b739d03a178eed86d556f43f1e0d69ab5ebd775c0e204e7c0477f1", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4b06e580bf1fe51ba4e18edb8cc737bebd9037d3d5c47a3956dfb92dc6126c95", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKTZGxPzLsr3jeaFbBRFJShzrBUpEZHbBA", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210094c4a199937fe607add75465fb4d897619afe089e2bb0540b52b0437f77649bf02205d4015b7a98403f2e630950fb617e62b0a201cf246c9f0c85dc4c2caf84f80a2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "412acb8eff340260f965bd3204818e8dd735ed3148a79320ed0c1cb51fa005c8", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbJyaYJYZYS4uDwAy7sfNTTcjT1wUnVCew", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bdfdeaa54eccf67f4b817884d749c616e8e8a0f31ebac750265dd29e319c3198022008befaf9d11fecaac276994f7d055efe2bd3eb062920c40726aa48f4afc912b5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "eb782b052f5293d3770f8b135230c899c65e6db03eb07df05c93658554bc9a9f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AX9HUjeUfgZieGsbpkKmUBJLqvZiC7N2aL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202a96e2fae0745187fede717b93002818fbb06f8626169f7b8a9a3efac3b83d7402204efdb6ac12e7da4283849cdbefefe352480b63753e028d76474d63c7ac038270", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c38959c42f58037a788a83f071848f591b8b6f5ed3093ab7464b164e2cabf32b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK9Adi5y1xEftjbr1WVyRCqUuptKsg7xGj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206b832e0894327676b0ac1c045ccba3f6d382a0e117ad75e90a5612d9b81363b702204ef4cabe3c9930bc7d66d5c632d990a7e0cbd2212c48fc2eeedf33c061335b2b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "576f8af16460ade10e986db13c75d0527802d68befee6b621e44f9b78af234ae", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AVxjxrm3bnGSf4yo62f4bbLBWFwAeNhvTq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201a863874f1973eaec68ea12b911d4c96d46e593874b3cf69228aa1ebc641c20e02206698e10529a352d8d120a1d02842eb7c937d0dad3be09f32942a47f87c6e58e2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c2b3fb3874dd0ab647939456c4d6c4aee1b78391b03975065ad804559736972b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AV4dc3U6awxGiRwxSEY4cgNSwNReBqeKFj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a168e45bdd69a809def1211a68a1a75f15c8959ae7fd89d5bacc22c3f8ae2cca0220100e01bd865f361d6e0ccaac9c15841eca19556da5281b05d0a22e92aeea8367", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "66a7b7b8589537edf28f5c604b8632a697f82acf4ded5257fefc58c337afffdb", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH9qyvot8HnjcVCvz3s3vCZ3rgg7qumnj6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ddfbe135233d387ec88e199ed09f0e12f161a9e9081a9dfd7aedf2f67fb0d80202200ab29642f6298459e84b2e8c63efbe9e656d6888f6d38d9593397333612ed7a4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ffa745c5bea46f6bf43495e73c4c73c9443fba611caecdb57747c107a8d99722", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASTDtbrw1i53dbMyaTr6WT4XEbTv81e7pi", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206bcf836361511ecad0d952991d9e6c8a80079a9cca79f7f4500cc41da4c79c4302205377880c4ab4513c061a4423be280465d6dd519a4e08bd610268c758dfe6f1f3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3be1aeecacd7c09d22aaa89fefe6048e2e547111e8ffa5da6416a2bfd381d25a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AeQwXXZEfMkp2zB9pUJzM85wn52EKX7cE8", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e0579ee6a9cfff8fbfe9b3f379461f0501d24da51396647ffb4f999311437cfb022034064f6ed175a60d20004b811eda3823e24412414cd3eebaac2117335f05234e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "6a8ac0e6ff034f5b68a6337c3d3468779b5caf39b9794285e89e016cfd291dcb", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWRDMAnZgZp5QEKkQ8fH8E26wtTgwoNUe2", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100d7e8754c4893018358665d0d24093ef6d46ccfbeae729a261cc2164ffcb04ca70220706ee9a83174b8d1007bbb8cfe0b33f181ce8e795ff3ee4606d2b6fb1aefe709", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a269129b91761a9b44e90545d8a4600cefa0445bd61fb57a691dc06ea152c897", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AHo8ciM7i3ro99RUcZMPV4Ytb5Esq8Xs1E", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ded0a1779b8684fff05c59756ec6fb8628ea48c4554800a40fa346cb969ee6a3022051c763d13821ca260ec6dfb84d433e8036641cb847eb49eeb7b5989b655f3fa8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8e2c8606b23ed885b2f0f4bda2526303bd62862e220a9763723781829324330e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AV184d6W1B171mb4KkXbmM5nhRQYf9qSYJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e17ecf694f779b77c4ab33bc05160b0426b14275545603ba1b5c0a93cba5b51502200b1620f080d3c9eab72418f0df74bd6f3be547c480161cc52e6c1dbff3979372", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1aeb70719ab7c6e149b7984a884f072b8934ab22ffd85f2228734b72cecc24bc", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AHpqfxf3EA6L4383nw2rq9i5GqoNJadjGD", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100da1d70c7ef34014a8992fc278d394cda2ba27f1e0fb27cd3af1691b691b9418002207c155fe5258ad0c72d0fd964d2caef6644272d1def04085be85086f3378be486", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f133840ad4f0618d7538205e9768815da99286b108de7f9e281ff4f925e7c77e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUdygMQuvJQ1zBgiEF7EmbisnVN7AM5Aeg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204d1a6cd2ad8d056d84b81eba6fb62bed2e1fc7dce9ce52c95dfcc13be5dc7c23022065b2171d8b8ae7d16bac8c13140a73228f230e570503e49d5e2561c6bd12a018", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e745b9bc309d17e9322d46392598a64b9e7776ee2167121731936e18aa6c66d0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANyRSjSGgb5Lf2bxFkdJG1DRXX5C2GLRwW", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220612a9b44211722b61e4dd299e3f1dda40175f36474ee657ab63cdb83a795475c02205040b4f18184a88ff2850d9fbffbe1a08bc9996824d0e987d537e3296a250523", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e0507bb6801b2fc35322859788aed6c36d9885f609cb3e70e9eb19058123375e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Aefdn5H3hQHMxfFAYjTXUzbn43HCi13zbF", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022017b920dfc1cd4f2866ca74fc29f94187b10f72442de97991750855cd1552fac302207504ed879538deafce89dcc1338ce04273be493e4b5b4473f4034f3fee4e18c4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ab7f47667098ec957f98b68249ac32a22b3e40d38bffed42a7b5723308965a20", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKRMzUmHeuHA6nRjW54FLcDFwyg6f4erVK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220262fbb48893c3759c6c19bca588ac3df17819b88a14287c78b58077d8691a271022062c7c2c04b88c424a3fa5d9c3a7f34c896883017d74c64c2c2aad1c091e658b8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "34689da07514153041f8c1520e35abbde604b3e84101f54892728772a1ad5086", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASdb1P1eikFmmZuywwSKcJ4iFa7LNBZdjC", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402205acf0bcfe01143871ed110d21fd6b2d69e1572f053a612e646473cb91a18273c02203665fe5328d7256e5630bee18816048c263d2cd4c16749ccdd88ed283c0ab596", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f6e3515d45b1d8fb060ae5216e574428e165e61893022848f36954a68a21bd3e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARF2AUrWPyEMSY7w6SCqg6jeGgi6QxBUTT", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022025b82bdc0abd9ce35d67d8e45a08010fd63585bd9443ad3f83df8d8ad4abaa1f022002630834071f926d0e0f841f2c733dd6703513378d45b12bdf1dc603a38866e6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5eee7b1993181729bef38d17f4578c8c726833289ef1a15a6b6914007415c77a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK4MTqy8SoHsrATVDEkVDKTVDX12XcJzjo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ba3507c30d2732ceaad71f9e78bfd4f941c0cfbf2c76f1b7c763bcf88cfeb2c20220165b637442ba57c05cbd787fe99bf165e6ea2e365add2d67f423f464bdb4ed1b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3fd3b767215cc7ec93691da2308e98cc39bdcf825dfc55a7043a0ca849c687f8", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Ab3oWYGX4mMuXsNtE6MXJoPafHG1EbVkS2", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a984c45ea0f2529b8f70519d420c668d95674ebe137ae36eab222b6758acdd1902205e5c3380f6f6fa89dec7a54a17db33597f8209f99e98e74c2ebf198e345361bc", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "16d7a16b5b79ff20dfcb71d4f48410416efc574eaa7fc18b37de7ac2954ea89a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQiMw9hxzdss2js4HN2L1jeEio8Pdpd9yB", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022027b0c0b08bb9cf60137ae89ecca629a3cfa954d8993bc4db473b74a76cd79ea5022018e9b9260e137fce64d31b55d6c307417023552be6720890dc5b3624bd5ae4a2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5dcdeb5e8aebd4cd0e90782067bc9153f0bfc8f0756c07880d95dfbeb915ff67", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARzo499fWcgSDomQdquwxt9DzcdkMYDQvw", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022062d3b9fd8bb8825f3e4d854041a6a6498a62fd7d0b9afeb8ca30a3d901aa6a3c022073d44fe06624066f7b493e50df17675bbaf95ee7f58d918a264a148d4301ab76", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c492f2910b1bb03da512e7de4fe9118572ad2fbc6bc50cc527f16578ab7cd567", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZHEyQWc4p15fKRhaK9zmV7gc3mAVa6AF1", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100902528a4b27f364e03c8b64ee440cb12a675c944a61354e785767ceef907aadf022071d2903350cf631d59731714f4023e8f0be3ee2a06bed6fbe003462867382bd8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "924906d74ec751189887708df2f871205aab83639822608aba5ac3ad8d365e8e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFnYJTirDrhEmadpc6pqq1sE55RreeiB8P", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210092f3e7a7a51f60b3bb066913db06af947de935bd3d2dca15aa4a50186dc55e6e02200f25c64eac24a656c81ced4cf2f52117c7b4c7a488d354e53ee1bdf9bdfed362", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "44923020dd138a6aea4d524109e7209e33e9241e0ab46d55e8f8e6a2f7956467", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AST3nbdEcDc4uVWzDe9hpgZHJxCJxmmysY", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a6b930eb41963b041e2cd4d47c66c264c9a07a4e2c9322eddd5353221fa44c6202200870a6e4c05c03850958f5f9526040452398dde09fc93c750e8e7972f57cef7b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4efc7395530379cd73b60350282be549a24ee820c31233e6f53697eee30cc2e2", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQRSAa6sFA9oCd1XV6ZbNg5KJwEiFaJH21", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100fb5e70beda3cbf396466465a7503a5a2caae8f6a6905ac2590e859351f6f9f1b02206a5a869c2a41bcb275bc9b446fec87490d2f151fefb958ea0d04347175f231e4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "adbc0691f2d644faeafadbfdca2674e1bd26a246296ec03c4784c771e577d39a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANzBg5Kw43X2CSQgaBeHHubzw1swY25Frz", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203131c4f34b81744095aa9a651310ea6ff9cac0a88d38c4a05105c732816c2f79022020760518c29255e19cd30f677b9eef5a3c4d10c4085071b4ac69f6659e102b15", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "7bdf981c420c4632b9958b0d99e955b4e4c723d99e9f9bbc4a47c935f8d498ff", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMzFowdfLaxLKNThawWEq3QyvDP2SfUv8b", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008dcea7c514e22a165fbe1931fbf6859458417762c3ddfe02756b57c31779a803022030a651befb9afe798ab38f7b4dd9b8f473b292711ba3ebe38d5ff8b2abb08c2e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ed0471000a0cdb90359cf94688b6c42f00f47cf4223ee670eeb268cbadb4fd7b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFrWisLirag5Upjiik6rnFVLptCpaE9yZj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008c7c71fb49fe61b53fe678147be01d4c82b0d9f81189c4478512c30a58b67ef70220485c9bc14feec32aad01c79b65adc9ee20c356d5efce72a23ad7f658d20a9b4b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "22446441b1d6df010bbfe2efd34b6d3e73a3345784a6d4ecdacbf456ec02725d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALtiFFMTVY7uL7hXUpAa2NiCcqUfAKkYqZ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201dee9b2181fb6c631e908c80b5e8f133edfb158bdbc04479ca509084c6cede5202200fa9b3a4dacd37b3bbdf270883219b236a16800353880eb708f8ea3e0bc816c4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fcda2a2d21852bafa0dd63786dd664a03a707de946ed2caeea101b3ba4ae6099", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMWaGRkxUMc7EnZVRGKxuihYjQxkXJTGv5", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200955a4f98d719ba3f9cc6adeb62344631589a544548e62c6b0d5b444fc9d83cb022017323d3f7809b42b2bea159f6178b5db2bb46152ba0aa07eecba84a5dc810ffa", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "0df9638f07dc701a9c6dcb415467ea128fe093bfaf1e865c57fe3d61701c2934", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQuppbFyZJoY5D7H1vd2bdJdtG86jHhiHL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a472f360d3d2971f09017b8dfd0992eebc228d8332eda827c069e16481a749860220127469400183f8732bce62479335f4c71f49377ee4ee274afdd3c38c463783f3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5554e8811b7ffddb8b046d9f7f56c1f78a28100e26dfa70a582aac4e14157ce4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZm1iZfCLyqAqnWMFhekpTeh6XAspxCUYY", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100f5a54138359baa3cf068b7b158b0fc12d54a98b6767bd91def4b37282197f0ab02205891142737810a31fa38343f8dca3a64eed666263a08b8fa629be7c87df9308b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3ef7e5802a86f2d7888af2ee7ec9bc9870906adc419ffdd719049634edbc44f6", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYzQYGbwgcmrCgZM3xx5Mv7E9j2TEcAhAn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202291196aa89541305c68dd4ef7cefba16648e71abadedb51fff6d5dd6816ea70022058aa3f59f0eae41d92f568c0d0b05a420ed04bee0cb2f96fb60bd80d1c1dfbde", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bb32309a08d91dbb8caa7aae4b19b373fbabb901b97753066a804da0db81a038", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "026039e8cb61e6050ea0ef22706eecf6f880fcf7581bafda135f0d496ed88e2bbe", + "asset": { + "signature": { + "publicKey": "0309a74e99521a5f4c3fef42a68a4b5c65b5ea89cfad444cfa11fd896009561708" + } + }, + "signature": "3045022100b40c6bda165542258f7f54590bfbe792c652d3eb7a1367363d93cd4121020c9c0220190aa9126905e8199c7bde279a443901dda9b249bb95caf838a4512ad60c9bd6", + "senderId": "AZm1iZfCLyqAqnWMFhekpTeh6XAspxCUYY" + }, + { + "id": "bba6d5be1970a5b24b94010b1fc624721d66ada2e67d5a545f1ea866902d711f", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b717b22da94ce06e2b6f76b20df13cea230fa0c3f411dc45ea98b9a2e2674623", + "asset": { + "signature": { + "publicKey": "03b6d6299759f40d29760a9e2cbaff0fe8159b84a479d3865450e93adb9442239a" + } + }, + "signature": "3045022100b56b35f9ea060ad1f61124a28bcb5909f516bf58727d70af9d9f1446795a28c602205142ae67d4c94b536d29f0a05308ff0e924ef30bd0c1f3bbb02fe448d1aa23a6", + "senderId": "AQuppbFyZJoY5D7H1vd2bdJdtG86jHhiHL" + }, + { + "id": "47a96b97d3a1f6e2db5f2d77c61a419a2850bbde3ab51d97cb6faae6a1c3b4f3", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032cb55adb5adc85a666b10b101e8ec595bcaa4993f3ce9119375f35ddad6c5104", + "asset": { + "signature": { + "publicKey": "020d5db68f8c33612971e0c8a00576e1516b344191b88b7edd608bce7c953a60ae" + } + }, + "signature": "3045022100c0cec62e2a7a2d548df4b60401b747744711869fda9fdc1dd438bf639d74006302200c8523fd9bddf2597055480a965173cecb624ca8b42ebe1319b80cd937858841", + "senderId": "AMWaGRkxUMc7EnZVRGKxuihYjQxkXJTGv5" + }, + { + "id": "397db1ea74e602cc705c33855228cd3239bd18a63b85cdb156b43b613d2f16d8", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02e1714b7bf5c6101368bb47e68e8904b93ed5dd9e358eadee4bef89e1e25e5c76", + "asset": { + "signature": { + "publicKey": "02ae7019155478100138e458536f15dca4dfbb88e3f393185f52e9cd9edffa5bda" + } + }, + "signature": "3045022100f359a27ffad33510cfd770e0b19867ef9c327a7cce9f40d47a0835097d65ac9102202e26caa61ac8ac11dfd150d86081724df8313d0ea5befd86376c46425dd8ad96", + "senderId": "ALtiFFMTVY7uL7hXUpAa2NiCcqUfAKkYqZ" + }, + { + "id": "513b4b98806ff1916ba9774007659a9e4881a73d5d50e6722410f4000bd881d6", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "022134c050b767b87991bdb17582302fa1ab6ae0549b0ff36fe1259cc348084e68", + "asset": { + "signature": { + "publicKey": "03708880de2a462cbc0077dabecc85ecbc27d5085cbde2f23186972b96d04abe0a" + } + }, + "signature": "3045022100afc767fa625e0e055b239a6c040670457037195ed22b61ae95b72ed22800fdaa022071e70bf4b3d8b7b08a66caf00914e4dcf737ee9cb30aede12f69685503d78ec7", + "senderId": "AFrWisLirag5Upjiik6rnFVLptCpaE9yZj" + }, + { + "id": "14c63f36bc4086a5fc848f654c4def6fea8daeb4b6cdde91c07bd6e348299112", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0230dd82d88f144160176270f09ebe50f365d94837563ab7c47630759b45cb4121", + "asset": { + "signature": { + "publicKey": "032069ac5f3ea991c800da6a52efab48269d4bb1a287d449f75b4963e8b97aebe5" + } + }, + "signature": "3045022100d15b5bafd3ac96e51e16263e05667ecbae694e726299983211892a6443b6628c02203d6e3e67f6b399e99b0819ef12c0ea9c713985ca412a38109165d2ff396014a1", + "senderId": "AMzFowdfLaxLKNThawWEq3QyvDP2SfUv8b" + }, + { + "id": "6dbd7c01b04d18fc5b503a943aaa9412f7e6d8bc60daae94ed62a71fce683787", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02e9725fb335e3e4a0163a43292428d696a71aa3e02a5d3bf1c400263360b13aa5", + "asset": { + "signature": { + "publicKey": "021aaad76814e52441b7d6c1119c731ca9e677804aaacaedaebed49ee5f1263843" + } + }, + "signature": "3045022100a6f2eb6a9f96a8e80e0ad7e88a0cbbcaacdd31f6da8bebba1d94a615c4f3bb0802206ffd8effcdcd2f5dd67f79ff861173bd6d16baadd6a680141d40c3cffcbd1509", + "senderId": "ANzBg5Kw43X2CSQgaBeHHubzw1swY25Frz" + }, + { + "id": "5c43c5018379663b344fdad44a287b3d702b4ecb44eb1015b7431571044f4838", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "026f5418fc21d9300c51d4294c2482217db2f756b79899abfd68c57bb8cb7e6d33", + "asset": { + "signature": { + "publicKey": "036415c49027e08b725be09bee1df772a0f97967911944284553991f131cd53f52" + } + }, + "signature": "30450221009e32b798c7da49bf26f19f6af2286e2a8454a9d698ec4f335ebd2d8c4634b2590220696032103713d41f0ee76814cd18eae9715e5362b60d1f38e7f253b1d7d239e4", + "senderId": "AQRSAa6sFA9oCd1XV6ZbNg5KJwEiFaJH21" + }, + { + "id": "cedaddb346da1f549e538e5078f059706e06c260c07a523c19c29dca3963e9a4", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03bf66b51d43f9b373635a04dd785882d5b920463b845127dc50dc61323b6899fa", + "asset": { + "signature": { + "publicKey": "02da7401be77578c5816a686e490c2634958566ef4539a2ef8dbd99dfc3bbc1622" + } + }, + "signature": "3045022100fdf1ecbb5500298ea3c365785e20a40b05b5ccbadc86ce9aa04536b1f1293d8e022029f6d6b8f586207959dd3f6699f22d353c3984c93a0402677884aa1288925ca5", + "senderId": "AST3nbdEcDc4uVWzDe9hpgZHJxCJxmmysY" + }, + { + "id": "b149fc01e1fabcde67114ca832e7dea4a4afcc8fa8bf3ed8b08aabf2ed11b35c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f8a36f6c492c081bbe1943764d962eee5dbae0b1b06b299751380a979c620e54", + "asset": { + "signature": { + "publicKey": "030fb10bbba9c58b60fe077151e60f5afa44ffff896b23953032f9c8a342ca74b2" + } + }, + "signature": "304402201e7d7a8cbb170d58a6b5f6f7333d19305dd1de234aa8b7fbed087f3691cd947f02201ce02ddbacd7d7b0cb7d961157f1494227229198a3651505dfa4967fa4b96f13", + "senderId": "AFnYJTirDrhEmadpc6pqq1sE55RreeiB8P" + }, + { + "id": "81b39fbe0126dc4f663f988c99d823c7a97438df0665a8e18fea7c3db6761688", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03167b1b5a15097c60d4510425c8cb9ba440f2d48cbedf184a66e40ef5adde1400", + "asset": { + "signature": { + "publicKey": "028902efe250d06bc09d89a3789ee754652bd2d57d28178563cf889e7ef158b0f3" + } + }, + "signature": "3045022100be305b78d0257ae214c4c66a5862c64563e67326efb9291297a8671863f0b7f802205fa48e5abfb5eb07c259970344960c9b32c252e4817daced7667b4b810bcfca1", + "senderId": "AZHEyQWc4p15fKRhaK9zmV7gc3mAVa6AF1" + }, + { + "id": "85057fbb9dfb583406cce3a90d33ce438a51e854e176e96eed8969d551ee5077", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f9dd1b5a26d9c9919909ed33c3d832734c18e3bd876b24f0a74c96d35d2937d0", + "asset": { + "signature": { + "publicKey": "0201204f290bf5c8a826693dd85eadd514a752f788a3ae04027acf2fe91524fccf" + } + }, + "signature": "3045022100a924a821e968651e8ee2c8b69a18514d89b0bbb21aed2ae406b15f7e20c6f583022018f062385e6862b04d2d37beb011debb5d8ceeae40b25426b2ce13765d3ba48d", + "senderId": "ARzo499fWcgSDomQdquwxt9DzcdkMYDQvw" + }, + { + "id": "ff498fe16a3885ead3f447e6c1f12fa716591b053558a1f9aac50a117056a9e7", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02072106d6946a9de07cdfe30d70683ffcc7bae860fb29025fb8398b4de0e27f3e", + "asset": { + "signature": { + "publicKey": "0361416b34d1b9411119d19fe9fbabd92a3ffcb40f2f9809c5556d018a511a2090" + } + }, + "signature": "30440220153a84425518156f836d2df4200913156019b890d05185b1f1d092135cf16942022054f886da164e14057dddf61e145a073e3151a1ffb4b53e226a98eea1e7be5564", + "senderId": "AQiMw9hxzdss2js4HN2L1jeEio8Pdpd9yB" + }, + { + "id": "05ce849497a37c5d4a3d2f4a6e3f2ab0a03d668ebfca63e26c8670783c7f77fe", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032994c3c42dbe940d4e1aa91beaff6b6746e917cbe1709a36def3389095afbd4a", + "asset": { + "signature": { + "publicKey": "028efcad1f72b84857d8459b326fcc213a1098c0b3c04a35b60bed46c20feaef5f" + } + }, + "signature": "304502210099ada522fa643080d35ae0303f7bf9fd42f2bc0e82c345286f9ee09bc52a3b9502201a3cd043d51b79b5df6df4b334cd219e1981ada9675ba4117dbbc18e4d77f82e", + "senderId": "Ab3oWYGX4mMuXsNtE6MXJoPafHG1EbVkS2" + }, + { + "id": "eb227e6876955d0b610566c4d76c3a3c5c0f0d439dc26a019015ccb918ac027a", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "036c1748887645da79305f6b62da3df56725355538b56a3072ddeb32ee228c016e", + "asset": { + "signature": { + "publicKey": "03a1be2ffb0c08b7747757ac30f491d680ece8c7aebf93244deff599b49e9ac091" + } + }, + "signature": "3045022100f779704abe115d0a9450d98c740ca09c44f468f714ace07953d7efcd5ab5257902206206eac5e0ddb74a61724a8bd479480c92f5e6caa5be13d10d51389450fbc1fe", + "senderId": "AK4MTqy8SoHsrATVDEkVDKTVDX12XcJzjo" + }, + { + "id": "be606de2880d97099af92d50a761dd79830505513b9e4f1f1b64e6ceb5932b44", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f30cdb63b466e72751b34ebc8f98c68dfcbfd842486128f98b81e8e54c14cf47", + "asset": { + "signature": { + "publicKey": "0337be3b271c1ad0ad619ad68b5df7974e964e7e0e48f86ae2a4e02c7533885d26" + } + }, + "signature": "30440220513c70ebdee8dfeb6d5bdc6dd73b208b07040a09e820b5c8121a9b5ce2b387b3022055ef15a5214433bbfdcaf53eb5e4d74aed81fa04b1d92ce3b5c504ee0052f701", + "senderId": "ARF2AUrWPyEMSY7w6SCqg6jeGgi6QxBUTT" + }, + { + "id": "3e10c5b31d8a467d6ef56d4dcdca12595987ded73ab368e134a314a69b398ee3", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "037a5ab54742b6088fec6c487c37d961de076999eeaa462a9b25fadd131a93a75f", + "asset": { + "signature": { + "publicKey": "02a0653d79f2315557161cf5a2805a2bd0453fddf5ff71671a17d5921e1e309d3c" + } + }, + "signature": "304402204e76e4210b2439b57f6bbee6123abdd58bdc089069b6a76fa8f2793c8723f5bd02201eddfec1efccdc74fc0e3a6243dc53ce4c65896530a3003c9101bb51647b0c09", + "senderId": "ASdb1P1eikFmmZuywwSKcJ4iFa7LNBZdjC" + }, + { + "id": "8766e770f746dc80f9b037ed8a5ee5efefc91007c4f82e217607baa140f089c9", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f40978e58affc1bb5ae6746e1c481f6e4f2093b1fe4f627f33b2b677fc8b82a2", + "asset": { + "signature": { + "publicKey": "0389c87413dc7b58e8edcbdf31d30ac2085d1711272a29ceb84b352d1dd8cdc756" + } + }, + "signature": "3044022069db71173d696853191c8850f04d31e363aa6eac9031fa796a4df697b5efad3e02202a40b3704f4fdc72e85e89e027e84ea66b075e76a5518556ef85c7eead101094", + "senderId": "AKRMzUmHeuHA6nRjW54FLcDFwyg6f4erVK" + }, + { + "id": "226933126ae1fe568f8528ee2e703ebf31a341938255c80b4d4fae965d12bedb", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02d3e51bb756f2522139daccd06aacba36982536338210ac6f43b3dacaf22cde48", + "asset": { + "signature": { + "publicKey": "03ed895b981b6b7314f2bc6ccfa8aed69973be83bc0e8f0b37e861907110d744e3" + } + }, + "signature": "3045022100e2939bf631d1774359e8d517af94249da7ca01f8cc5193eb4448ed187198bbd20220507204da957078aa0f70cd4b8d51b71b715b1cd7db3628014942abe0a03de90f", + "senderId": "Aefdn5H3hQHMxfFAYjTXUzbn43HCi13zbF" + }, + { + "id": "3a6ef84e0713394c79cd3eb0f28e4f0bc9795fdeb120432ca231ed27c10c31a7", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "021b9a39a3281e7f4402021450f561b439fb2faa3b16a15c87b92fcfc5e4aac15c", + "asset": { + "signature": { + "publicKey": "03f0facc3075b52961d5eb6b5b8fc8a0653ee3e50749fab38cb30b8acb39423455" + } + }, + "signature": "3044022063da2f47889a8a5e655959677db7fd05897d109da21f050ad0bd57715379c0730220670968331b43dcd853bb969b94092690f5ffdc9729ba91f371570774125b954b", + "senderId": "ANyRSjSGgb5Lf2bxFkdJG1DRXX5C2GLRwW" + }, + { + "id": "7ec6ca17804529e63147d4a35de323d4e8e4c9ed8c5fac4aaaebcc172a88203d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "033425c8ebeda418aa1f0d2b7400923d53168e921a8be8ebd3c5584e793c17b038", + "asset": { + "signature": { + "publicKey": "02aba8f5cdabf7d5437590273c497ed4435703e4bc1af0e4018e5a670827fd6136" + } + }, + "signature": "304402205ac5f2f7dfed6d6baa8424d7eac78d7456fafe412837955eb5a95c17a5516ebc02207d6c03c145b754caba9eccb4814da30977080cbaeb9c6d1b4e2c9683cb2fb42e", + "senderId": "AUdygMQuvJQ1zBgiEF7EmbisnVN7AM5Aeg" + }, + { + "id": "20e49a49f39e0e581e1e5e2c9fb9435c45abaffd1c35340f27f084590e801caa", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03b2d0bc348b254f8bf98612e56af68b52ff84bf0daf35690b4d60455911092efb", + "asset": { + "signature": { + "publicKey": "0387fea8e631fd122838f8fcde005995f55865775b0ff9544f881245924873b5d9" + } + }, + "signature": "30440220772d2631aa0628899ca37376cd875a0ec1154c026c7e533687aa3cbefa0f7efa0220577d0cd09926e60e3d57d886050e282f6bafd8abab239bc5dd168bf48f4dae58", + "senderId": "AHpqfxf3EA6L4383nw2rq9i5GqoNJadjGD" + }, + { + "id": "6a932b3ff1e7afbe114138c3939ee91b62a16ece4c8ec68722c0b7ef21fbc524", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02fb92a875f324b45c5168b0f19c4cd9f23041861640cf13abf07c8919e4754c31", + "asset": { + "signature": { + "publicKey": "0264f4c774094302809ec7674ac4b113174c2d937338a4fe175300f96882dbf385" + } + }, + "signature": "3045022100fd3fd1eb2f934bb90c7c292de1abcfe5c73547654a815ae5fbb3f6de4fadd28502200e324eb03390b20be3668ddb53cf508393728003febb46e21f5266a48e04f2a8", + "senderId": "AV184d6W1B171mb4KkXbmM5nhRQYf9qSYJ" + }, + { + "id": "7e1c44418686926e9f19d17a0799173d8f5519cab9a12eb4b8e906e27a066f5c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02aaafe40f4c7b541084802f4fc3d6ffe8e26061478898c3589d449e83de80dc91", + "asset": { + "signature": { + "publicKey": "03ad89ced7870b796e5c2ddfb4538f5c65d2d97ce3c9fe9df40758622027d2231c" + } + }, + "signature": "3044022033a002c1cc78fb2d1c907de9f5ac8379f7aa10df9c49565aaf7321c35bfcfe66022028a7422a109b87e5e085a75195db9a8ad7ccdca9ba6e58f183f297fe12019d2c", + "senderId": "AHo8ciM7i3ro99RUcZMPV4Ytb5Esq8Xs1E" + }, + { + "id": "626d8158749179c7b3dfdf6baf34738e4ec2b6559a2fa3035a72db6b4f1ee52d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "025acf14ffa2ec86954c252784cd6e3931bbffa5ab8a490afb36ea250c01b72694", + "asset": { + "signature": { + "publicKey": "02f29581ed2bbb9e124c925ee5695a327fc52579bb0ebc39db32d625ca527e8b19" + } + }, + "signature": "3044022029ebd1d9db59978d59559b408770b738b80198ddc5020e523ec176003fb1e89a022074ff4088094885b1da1d44e4c4966b757c223908d994b879e27388e6924a96ee", + "senderId": "AWRDMAnZgZp5QEKkQ8fH8E26wtTgwoNUe2" + }, + { + "id": "ebaa86c90419ca638c27b69bc9da17675b03d2c9b123893226845649972776f4", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "028e16ce65270805eb06a1671cc5ddcff9f8d8ffce13387118fa5ab0fe39616052", + "asset": { + "signature": { + "publicKey": "02e5c23a1023c43f52f3b4d142072f851bebca3550b4dd13b6462af62724d8ee0b" + } + }, + "signature": "3044022034415ce70b229515b468865c5097aaa033316f3f6eeca2ca89e9483215f5c9c502205b711d05ed7636e01bb52735002838888bb2685a8a1e845b5d537de050257ce8", + "senderId": "AKR9GXZGUYqz441D359coxztJonviA2FRM" + }, + { + "id": "e48d3f6d60e72f705d923730ebfa58b6f8dcdb0502fd647dfee17d198787bb5c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "031639a30d2a92746da86629be2fa190cff2ba2871a4fbbee4badc9bfd466a0753", + "asset": { + "signature": { + "publicKey": "03a9baa9f916c6469d220266b6911c9feffb00734bd4a3bbc1cb09a98f0ef26ab3" + } + }, + "signature": "3045022100869a1abca4f666df149ea61f7c22a1f1335a008414751df91c677dfceebea48b02200e2c0b403f9402a12fe5f40a5747d3372aba29e6ed994b6617640f8ed189f90e", + "senderId": "AeQwXXZEfMkp2zB9pUJzM85wn52EKX7cE8" + }, + { + "id": "036eee5579480e1d4cb8fd33b4feb2d6b36ca87fc471b43ecc1e4878790b1a73", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f453fe29be65f3ae31b5c68554e777cf0ae6422fe48e598784adc293e0d1faad", + "asset": { + "signature": { + "publicKey": "039d12faf1218abc7ab623785367e4c4513678adc175940c5cc01283049d4ae0e4" + } + }, + "signature": "3045022100bba24cc848902ca32bb4a56e8608c23c3a044b1b2cf4d0474fda127a3b2e1b1e02202153cc6058645ecefe945e05677d29bb4dba0a44a72610f2e92a106eabaa4074", + "senderId": "ASTDtbrw1i53dbMyaTr6WT4XEbTv81e7pi" + }, + { + "id": "135d0cc36c89ea8155d27dad38112c51a84bfca6ba4e66d849588d1388e1973c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02a146b249c588574ff70a2f40ccfd27b6cc324209f8858a8f7b5063930d869756", + "asset": { + "signature": { + "publicKey": "0387dc56838f7a454ffcd73aa44184d8b8280a76319750758c59ef1d830296670a" + } + }, + "signature": "3044022000f885d9fedb98502b5b234da63c7960927677cef71b4a3ec9010de199064b4702203c0f1e103cb2a7b52a4a791d14ab1fcd9a278e841ab9fbe6c1d8a4307eee60c5", + "senderId": "AH9qyvot8HnjcVCvz3s3vCZ3rgg7qumnj6" + }, + { + "id": "aaf4767bd15bb77c4efbd09da9d3f63f4a0389488bcaa5debba23e2d29f365f8", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02dec8953912902e1ea7ca3c6d994b99dc0156165810656d1863e0a239a7ff2f72", + "asset": { + "signature": { + "publicKey": "02efb7cc98087861c1139edbf0cefd075750a110dc7e076de77812bbde9db70615" + } + }, + "signature": "3044022051bafbe54508fd5ee33d0603c510e753939597807980705a031f6cd7f2bdd8e702204b4d83622763db7837234aac353bf902d92a13b959225c9ba297698cf50d303f", + "senderId": "AV4dc3U6awxGiRwxSEY4cgNSwNReBqeKFj" + }, + { + "id": "96e8897859172603446d5643791b2b9dd82ae6520ca3faef1bac25a52717d4a2", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f4a9e1c98fd828d93e96ecc133187174c5d91566d09fa2701fbed36ac70fe445", + "asset": { + "signature": { + "publicKey": "02ea3a03e92f9c23e9c9cf5fd35c5a56233a1295f10b60c31d26bd36059e568933" + } + }, + "signature": "3045022100cd70b956d91c4f50b1d30434a238c44d334a91f503d3f52ef70b997245019fec0220503427277d6396b1ab4f41ffdcfd5551f1c010af0c572aaa0dd5a0c95d445532", + "senderId": "AVxjxrm3bnGSf4yo62f4bbLBWFwAeNhvTq" + }, + { + "id": "c59fb20f57fa13ba4524b4c6ff55f15ede3042cc99c03b7ed5f7eb51ad4f09e2", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0363a68f9cf32ad3d323f4f09cbe883bbb45c8e419d0e54e519a665b135d9be7d9", + "asset": { + "signature": { + "publicKey": "0210f73be877105655825d824c7b89d65e07c09ab4cd8ab5870a5b3050a5a46516" + } + }, + "signature": "30450221009a56f67b2210fec148fe11bd0ca24adb4e4e5bcc82c8d8ea77c6136921d0e46402204902536f1afba01e25cdbdf929873fece0abc1f749410b6bbce03c201e1429e9", + "senderId": "AK9Adi5y1xEftjbr1WVyRCqUuptKsg7xGj" + }, + { + "id": "b98137e28210c509e00827ec41e51487ef535a5af21bf71c664998de29ec16ef", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "020c8b7e3c16f5a1819e047f4be56f7a95b23b1eeac9078f3917016c6c1c22ecf0", + "asset": { + "signature": { + "publicKey": "03f0342418258c74ac75ab2329939f9b897447a79ffd483825d977ac4fba5acf6f" + } + }, + "signature": "3045022100dac7ef8efc848cafbe323c0af0234075d6d6946fa0748382a6e805965dbd2eac022026703ae9bd88e4af3c5c7ffebe6044582ee853b880312bbf5b8931ad9d7ac62c", + "senderId": "AX9HUjeUfgZieGsbpkKmUBJLqvZiC7N2aL" + }, + { + "id": "c1b9d9e12df90c6020e815267c2fa6c9e5c3b2a074287fc3cc327766fb122f38", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a7d7272168c98037e9c3c01f5b4b6a53eb88efb06057bf82785bddae0ade39c5", + "asset": { + "signature": { + "publicKey": "02cec2ff64ac4173530ab9d171ca8965c8275ac3bbbf00ac9284d716d407098fbe" + } + }, + "signature": "3044022031e4303e55ff6d0e4e69cdfabf8ac0c284d4a26c0faa1540b789c1fe83ebe9cb022032faa92f33bd2ade0e8ec8a28781e5ac0203a832fc9a707bae06bca61a75856f", + "senderId": "AbJyaYJYZYS4uDwAy7sfNTTcjT1wUnVCew" + }, + { + "id": "3ef0769da2b23e434c33364e4f43384caf3a3c08702f2288b0be46178632a346", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0294f19b7b5e338dc911810257a89277a487f35b0dc146fa78c2015d2489ba0db3", + "asset": { + "signature": { + "publicKey": "030f9f3e6fc33a40103e14d9981023b21b8f49ad95ad39adcb17449378cbe8ea24" + } + }, + "signature": "3045022100d107c6b7419eb3d16a4e3d6560132ba9376f2229850f3d8a9e1b77ee773506f6022045f37269fc1fb3a1cd6fbd467823ae4800bd972fb903f26b81622960990ca636", + "senderId": "AKTZGxPzLsr3jeaFbBRFJShzrBUpEZHbBA" + }, + { + "id": "e47ed307eb694798005c0f378002e83d201d6f184467de1127479f43478d4bb7", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02c48118c725c269653cd46b2836ab1c2d8a89ad7ba214b0b309eec5174b7f590b", + "asset": { + "signature": { + "publicKey": "035317f6d364bb4b61a89deaebef594f5285db398d0cf18060118b7b9625a34dad" + } + }, + "signature": "3045022100bc3414e2a2a411d40409b859e06b3b8a35682648c144eb1906ccd70ed975975102201fb43226e7e9b5fc1960ba4ae2157f0bf1e9a848abdded6e29ddcd5830a5b02e", + "senderId": "AR7wLy5nU2kML3Wv9Fdot2RdHAZXn7ftTL" + }, + { + "id": "425cb8ef0b1b41aa6bba2bf53621a4c874ed00ad08ff026e4e61ccedd249d0e1", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "033401817bf6e94157e8c6ee248e39037d28e0b1fdb1cfab726b06220323e0f29e", + "asset": { + "signature": { + "publicKey": "03037ed3a91937416c47e4bb8366437758c5343c0dfa672a8dcab592754792fe13" + } + }, + "signature": "30440220665c621aea56ba29768176b98f409871a1ac8a01c41e667a57552c814f96c297022028a05983e61c0f7d93bff39e226f7e52c3d663e185fe27d9c36276a131eed19c", + "senderId": "Adm62zHj5cUuJQKsbrz467exSVVgk1bH5j" + }, + { + "id": "f82d1388cd7db45d81d6b7573e5952462f8a41423d28fce2231dd25ed5d4eef5", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b4e3968198a714e9a2c81adfde1211a24fea24c578a7033312429a693a4e93c7", + "asset": { + "signature": { + "publicKey": "02394123b84cdb74f3e783523e372db0506245c9c73ced73ee93e53b06d5c2de80" + } + }, + "signature": "3045022100b54fe851008f68eafa42b5df55a10a9836ad065b927b2969a06578f4dc5c65dd0220588745a145c3bbf0b2dc001127c8862c435c7a1e900721f8da49d1897bef863b", + "senderId": "AGJUdoyjXsfQNsDa1G2x3smFfdJEPB8kyn" + }, + { + "id": "7869c6f8f682e33db9b32fa0e40370ca7d36914a1a282903878268ac9dec42f8", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f238b9dd9e43216c1e63390eb50adf9519d00c00541c29cce98f12ed3bab55fa", + "asset": { + "signature": { + "publicKey": "021093f23f1f74ba7c51f65f544c310be34014e348790accb81fc88f4f96a66056" + } + }, + "signature": "3045022100db22661430e150600a394a15366afab10d9ba47bef72e9a88af052aed0a0d952022067e66f731b35389f973525316729e37447bf8dbc2ed77bde30775c649c69ebb4", + "senderId": "AK8aUnLmspWtccNwDEciSBf2BoxmpRPAow" + }, + { + "id": "fe60ec2f0a4a8f33a168a8737962e17f421e98ad22425d4600d261b5867aa148", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03fcec41228c64a39dffbbe8466b23bc1db40efb1970e32de0360cc828ba953883", + "asset": { + "signature": { + "publicKey": "023f13954139fb1783ea9290606f2bc4973f697a0798e6b0976ed699662f11b045" + } + }, + "signature": "30440220020ec39a9b9ccc91e7e0ff98055010a2da3c08728529f2d85b96444cea16f474022057ec7c1cd5fb7947edfcb6175dc50b22748da8e2fa1af4ae90a524137fb4771f", + "senderId": "ANLm1RbdAqNcM5PMBS99ARjHrvBBfTykZM" + }, + { + "id": "3c7bbdb21be84ad8dea2c712bbbda704c0c33db905023ccc1f62d81972d606f5", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "037f4d9190cfa3c916820a3d410010226c399f6b4278eb07ecf22d41904c1d1217", + "asset": { + "signature": { + "publicKey": "02de982a6ad5c8bebdf3edce638747a4dfb065be8f631a3d20fbb63d172bf36cca" + } + }, + "signature": "30450221008bd6e023179aabac29caaac75581bd7daa8085ebb99a7ab0fc7dbc533728645b022032411ff25b2c0f33978e7cfa838f2e506e6adea90bb0d89530f5150b805187ff", + "senderId": "AMGsRHFRL8V6qbUz115q1Wc8pnXBScuS84" + }, + { + "id": "1daa6e3e318479739f39ed7f6d9a165ad459756d89df7de46ff7f101eb0287df", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "023244209672266ac90bf64d0fbd9903028c2483b33f3cf1a0de41822a3628c749", + "asset": { + "signature": { + "publicKey": "02687dff0ce2d481f69d27bf91b372ea1f215b339c6ceba900d7be430bfb2ca564" + } + }, + "signature": "304402201da228d0242749002721505a3db42435e4ade24621fe72c9d40d4926910bd19602201f871d5834995752d2419f05b2eadeca3913ea1eeaa5f29d183d571f2cebd047", + "senderId": "ALsRw7zX6PeYFVqHpJYeVoeBy3a5mjXCoL" + }, + { + "id": "a66c450fa242b6bf2ebed8912f0b69ae2ab3c0efa73e3882709484ec2dee0f2d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0384825d94953e85e3b6c3dc5ee27d363fec7c6dee2c1baa83dd6760458e05a402", + "asset": { + "signature": { + "publicKey": "02805584f70440601dbe3a89ecbfb3d93282c3acb8b2d3fe8dafe13c03bd4fff0a" + } + }, + "signature": "304402202b1219ffc989d3dca8ece9b0021da0dacde95ff2578c60b5c1270ce693ad3fc102201305aa4f510910f922ddd3e2652a19f84af8a67c65713afd755694bd93c25630", + "senderId": "ATHYUiLRifNafWrFLAVgQYo9dVxntZkow9" + }, + { + "id": "6a2be03d17d88936c9818f41aa474a2102dff7beff1760c4673cad20d3924276", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0264d678a13dddf54000ef95a38be00aa6e8698d82cbefa43c5c35808749d5053e", + "asset": { + "signature": { + "publicKey": "02ce8de343754d5b22fa7dfb72c383ba926b6b363e524cd35323f43090cf7bd2fa" + } + }, + "signature": "304402206e2dee8deec4633e10389937c2959c7ecd1ee457ab0ac9d09570e516c8f7139302205b1eb9e49c142d5a38294a8af570c14fe19659c9553a19fe8795e3037791e636", + "senderId": "ALf5oWGQhq2xbTSZyCfDuvxbnDSjvYqiKE" + }, + { + "id": "cca052b5e4a0010ee301ba166faeba8451bb8f4f5228c61cd39e6d1555227424", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03412d936ec21e8b69f43c4c4945e24defeb956c777d71b974740fe2f1b90dd4d1", + "asset": { + "signature": { + "publicKey": "02ecc545862c025a70bd0b1c1a2dd2798a46b64976d2a30670911d645fd1ae799d" + } + }, + "signature": "304402201aa22e71c5903b1c9c933e25af1290dea0cdf928ca9b5bdfd69228fcf0fccd370220524d005d9a92eb65e3c1b12ba1a856377902de0df8665c721bb36c14cc91299f", + "senderId": "AVQ7sUZaTDi6MgKNuNxzzC1Sn3ee3KjdNg" + }, + { + "id": "34aebfa8eddceaf9f55d82aa992753690ac46fb946f520f69f5281b89e763a8d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03882346ba5e7e2874d1cfd1ec8d30f984be9f57021b72f56ec5b4d4aee79caebf", + "asset": { + "signature": { + "publicKey": "03099b500c089faa04a89ef29f23da9f056f0a217d4a4966c6f88b49719313de1f" + } + }, + "signature": "3045022100c700dd147d1794d20023537c280334698c7122e05bd664741e8adcb1c2d0e77f0220529a8868e1044c524133e8a23461ce76983f1e918b1189bbee80140c4b2b5066", + "senderId": "AFyrB1QJSh7enu2JpMUYEKUgv3xnJo5gUJ" + }, + { + "id": "faa3968c129583d6e46d1e3ca4cf4c912a34e900c5c0fd76053e32c81b2f7a8a", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "039c0311d587b5bf354c2d42965436048dfadaf9d5f6fce7c5f842e5480f298c5c", + "asset": { + "signature": { + "publicKey": "029ee9628cc63f01c63b7537688bdad4d51f10211e1ceaf6810bd046c7bba5c8f2" + } + }, + "signature": "304502210084d61fa2e3afa0b17c4531196571a58e6c595ea94a12b47934ccbe24c7ad49c702204e1d2d02eb38ba653e23999d523b769bffca9466ce1992b066c8bd9b8a62565c", + "senderId": "AL6sZNMWm2VGABvMX4F6eVFLUQJQyJup6i" + }, + { + "id": "8e29afde6c611dee82ca0e3f089794fcde2f928af7087b5c9d5b3f1b78f7d8b6", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f4fc615d167b6e6e4ffd2c6d63ae7ff621341dacb8b0ab5809b82cd478304bc2", + "asset": { + "signature": { + "publicKey": "02ec72b44b00d8125a0b9a8baeada6d0d2642ff6c85885284b22b89326bef0fd78" + } + }, + "signature": "3044022062a8769e33ee2d25e559c88f6e0e23981094687fe9590cd699d19c5003bf51cf022028685a95e7886123e301677bffd63dd20d024e5fd9e90e54c5748abe41faf2e7", + "senderId": "Abu8suR9L2tD7F4gAGHMaQqVbdvXdCFfXG" + }, + { + "id": "1ccc824939323375f439978517cce8e551a9e1e9a30fad3828dd985db2c8de06", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02d59edc9009c55f4b271e0880ccc2e710eff3a69b5e41ee8f0573d93c639e7cf2", + "asset": { + "signature": { + "publicKey": "02c4da308fb28ec9f23ddc8122115594b7a3dc407e341359ae1d0f4457d86b3175" + } + }, + "signature": "3045022100b2f628b89a6c41edc6441546c93ace5aa289d7a5bd4731d3a58f48d8a592048702201282c7c3ecfb6be23001820c88fbd2e84c34dc2de8f65a73e5bd5bcf5bac3b70", + "senderId": "ATMnnCBtz4mYEi5oEqkCnDWk9RkaeNLvw8" + }, + { + "id": "671a995a541bfeae7c908fb8f3b6d4ba48bf8868d69cd07d69f1b9c4431d35db", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0223566d2ee34ed38a542e6bab5aa82feada724c86322a42ba544ac0a0d3cb90be", + "asset": { + "signature": { + "publicKey": "034ae1df2c1aaf5cc67cf6952297fec13711a2753a13e236a3ae617c1cb4800960" + } + }, + "signature": "3044022017df39dd35a0fb79d8b3e1e192c2ffe5c84546cceaf270811b48a2558928981b022046daf555ada51be01ff1e4528afd118484b28f9f6b88e31af19121df561c0f2b", + "senderId": "ASn4LHMaH819aCuUwynTBZsaoUwG3uxAhC" + }, + { + "id": "5f3a87e8640dc16175ed464fbe8c3a92e5769bf5d868713e0bd9391c92057888", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032813004bc61bb9125232369724c23f5d0c417f1690f0beefdae359694ed49eaf", + "asset": { + "signature": { + "publicKey": "02600925a8c12aa769aa81222246fe1b392f68c08e3cfd6cb014ee472797c4d9e9" + } + }, + "signature": "3045022100fe872625e0df7ce1a61bdd5e8b0a771a6ae71d8b4d74389574f2b2c093b1222b022008242b52199db1af7f2484cd47730ee7afc4cf496d762139219ff4bf0353dc95", + "senderId": "AYzQYGbwgcmrCgZM3xx5Mv7E9j2TEcAhAn" + }, + { + "id": "06fb5269ff12cb471119788c455d63c3decc9d04d58f0a15a6839d5ea97a1a78", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a6a69602037a74be19103d7666270ec9898fb25ae9b1a1d7be4e2e3c7c21a4cd", + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "03a6a69602037a74be19103d7666270ec9898fb25ae9b1a1d7be4e2e3c7c21a4cd" + } + }, + "signature": "304402207bd968f7154cfe278823f6d4316f42126da1961fafd9125591212638d18589bb02204b04af0bb3980d4ff28a177d683933bc3515e5e754cfd22305264ac67ce61c85", + "senderId": "AdyH7jMm8yK3QpucKYbrxWJBFfekmSkNtj" + }, + { + "id": "7e383f2c52a79957bef0a177151769a2f73bfffb02500bf80d37c4caa9a730f5", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02aed6613d71afac9c58f6d7dc50a8b83ef970a965134289b5fdb1316cb5ce4043", + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "02aed6613d71afac9c58f6d7dc50a8b83ef970a965134289b5fdb1316cb5ce4043" + } + }, + "signature": "304502210098430311b5d3284b92dc2f5a8aae2ec641973a88cdf4e70a294b799d043a26d202205900c0a9a701e77bdc15131b78b5f9607355177d1716e636c07e00b980179c94", + "senderId": "AQoAJwxR9AzoaJfJWiuJ1S6mvLHfKtY5BJ" + }, + { + "id": "6c7226c00be70e01178baeecc39b91b9949ef2a48f452e7c2c0e8e057a5ff952", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0224696d2d359eb27ba7f303e2cdad6b205b16958887f297fbb1006862733b93f4", + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "0224696d2d359eb27ba7f303e2cdad6b205b16958887f297fbb1006862733b93f4" + } + }, + "signature": "3044022052a4a69430fe6bbfe5248a04d228445ff304341d7c3c2a71ecc89b6681521177022037f3c06b993ca2a85b5fdc5886f43845c15170b42b4779a4072ff69f890eed1b", + "senderId": "APM5mRCaLXyifdsfmd3x2SKdnq324aW5xT" + }, + { + "id": "75154a668d529af7b8873acc7cca19ade919e75c94caf3747d6d980a64f8b18d", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0386b0158ffb50648b980feb1cdf8bcf7091c55b46f46a76e54ea53a31b0c10e25", + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "0386b0158ffb50648b980feb1cdf8bcf7091c55b46f46a76e54ea53a31b0c10e25" + } + }, + "signature": "3044022078bb479258bb419cfd683751913de3c34de6b073a7bba4c3d625e34aa879774102205cce0a6e1a781b77c3380d2bcc16cdb527f79c8782400dbd6f6a0922cbc163b7", + "senderId": "AJ1bRNQ4onQPvs9AMWoKtheVStwcf528zh" + }, + { + "id": "636addbe1c5dcab66f4cf232d433202fc1eb12280119a9e023bfd0af557af441", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03eef7da2ddb36f2becd143e94b96c9bdbcc855dc3a635f300473b692a27de1d0d", + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "03eef7da2ddb36f2becd143e94b96c9bdbcc855dc3a635f300473b692a27de1d0d" + } + }, + "signature": "3044022020472507dd7cae4477371c31d44bc158a784d644ddec9778f0c3eab7c1eafe8f0220478573d95c64ca73595826ecac04bbfeb94d228a38588facf6ae6031ac69f304", + "senderId": "AcnNYB6HhHzfwzfyquZTXmn9FCLPFsXugf" + }, + { + "id": "57878a82635004330c65c383840322a03d1b5ee3f23140bc767b8af1e5f502ec", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0286f55873cbda98f6b56d6b63dcd837c86ee2e8effd329a99ad9f724ab913e71f", + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "0286f55873cbda98f6b56d6b63dcd837c86ee2e8effd329a99ad9f724ab913e71f" + } + }, + "signature": "3044022064c97e36960a2de0dadcd777747bd826259c469e993a68dd1e32db5ac8ee8d7b02207b2e0b8356e88c12de6f8bb695c44dc3c8ba5011dcdb884234d0bb5615e123fa", + "senderId": "AJZeVY3wgx3VwbPs9MrVXWz1mK8quD6omE" + }, + { + "id": "73e44bc7a2f996a6b545ec3941626eb076e51beea67cac41e86e71cd01143171", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "036627ad7e990668b92abd09cd34cff49737ee5024f949b3cf060104bc26796dfa", + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "036627ad7e990668b92abd09cd34cff49737ee5024f949b3cf060104bc26796dfa" + } + }, + "signature": "304502210082371253e03d6b78ab733453f8b3271c6c8f016638824555d09c3494c1050e85022064249d21ce7253c62e60de0a1f2b98ecb4c9eec1cd08b4e68a82521923a6658f", + "senderId": "AXYAfJVjupjT29R7g7V8ZQXGtfBAYnMFZn" + }, + { + "id": "60eb7247c6d15dc51197109dbed260ff53164a01f12000d6ed49b32a115929e8", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0261861fce10c2d7329a7c5e9436ddc9f6e4cc7638a0313c365315d71c370aabcc", + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "0261861fce10c2d7329a7c5e9436ddc9f6e4cc7638a0313c365315d71c370aabcc" + } + }, + "signature": "3045022100a8b13c392f5b0ef3756e050af4502d565f93f0ac48ae55e192cc1ebd666ee15a0220049354377ba9b159c213b8a68c675f1e09f16fc93744717730577b4a8051dc00", + "senderId": "AFzhSbX7MMxZEeoz9mNFCvUeNX2iBxNK5o" + }, + { + "id": "39ca94093ca3d0e9074b88268f7db06613aef065021079f8eb31e53e2c4956e8", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "026bf3b6a49b53bf2fb4c3f3bf129080ab16027748ca3eae99382785deb9f20e64", + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "026bf3b6a49b53bf2fb4c3f3bf129080ab16027748ca3eae99382785deb9f20e64" + } + }, + "signature": "304402204d0fc85a8c3a11b72ac1bcc87d3ceb31ff5a396c03ab4611cfa1fd482d71abed02200843a09a02dd48635d89778d20fa5c7b51478f1ceef93f58eb800ec87bd90ccf", + "senderId": "AdFL6sbWYiJG2AFsAevb3juTiJseEH2rrr" + }, + { + "id": "f0fc54085f308bb8b0eee9f765d6d19c1e05c3a1eb79454bbc1b92adeb56f969", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0340c96462c621b849ed19ccbb43ebc99702b36e39eb31df6427d1040148b3e7df", + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "0340c96462c621b849ed19ccbb43ebc99702b36e39eb31df6427d1040148b3e7df" + } + }, + "signature": "3044022026416a12b3486bce56842a71f5ace25fadbf1ae124c3d9f77b1c1b64e41831d502204621759bc9d06e54f4f5594e3b72f4d6dfe7b09075af26b986ffe373836d8fd3", + "senderId": "AdXg3Gchp8XBiPUdKCH9oqCgYSxbezXNdn" + }, + { + "id": "4bb8d39521a1b8182b3917c7f0167094d6878f79da8ee4913c97a6b562a43f47", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0355d22bdc0f63688b11924ae3819b8ef1036f7e74f5ad5f9c7fb508bc219d1793", + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "0355d22bdc0f63688b11924ae3819b8ef1036f7e74f5ad5f9c7fb508bc219d1793" + } + }, + "signature": "3045022100a28852e9cdd300a702122ebd211e6f2a94935dabd6c50b8723ccbdda361781cd02207faa2db4c25cf427b77cb7ffad058014e02f0a802198b627f3717ce6ddd0a642", + "senderId": "AJ9MXucUFg8wqPCNmd32iUZmYa5roXhYiW" + }, + { + "id": "d8f77f20641273d42c747722749bc886b702dea8382ae8387a51cecf91919147", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "022f5905a84c78ab156196e220c4d816302d8ff121dca90d19c43b026c17a648f3", + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "022f5905a84c78ab156196e220c4d816302d8ff121dca90d19c43b026c17a648f3" + } + }, + "signature": "304402205c4a9c488fd269a10629466ed70ab6690ca8b5f62a0d748af17260919d75aafb022006ae186e2843af542e6cd7e4f70d2b7d082a954a5a27ea51447ffcfa70f401cc", + "senderId": "ARyFEoh7kXmPjDjZtbbGQEm3o8rz9bxmGa" + }, + { + "id": "357fd501799d9402daa580449c1a977ff4ecc6186f36d4188eb427d1da56ccfb", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03743e523107e718ef752267f983fb5d97d033225dede131c1b910c842c6845371", + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "03743e523107e718ef752267f983fb5d97d033225dede131c1b910c842c6845371" + } + }, + "signature": "304402201fc0040151885d641fd7ef76d930c7860400c6723564b927cb081872f9d36cb50220174bbf8fab2dedecb6417f5c2c3cc8fd9ef5b31a92805811adea2e5d703aa6b6", + "senderId": "AdYJP7AmU5DJfsmg1Lycc9ytGAbMz2wuf6" + }, + { + "id": "a0564512fed50a4a28d4456cdaab3528a08de330aa0e8318d05de8fe4a91fe7e", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a8e79a66f9abd185f6d273895357bf7f70b9024ea9c6ba6992c70769d2bbcd36", + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "03a8e79a66f9abd185f6d273895357bf7f70b9024ea9c6ba6992c70769d2bbcd36" + } + }, + "signature": "3045022100a1140072e432e0b9192d6bba5ed46bc20593af38a7ad538a5321de19815b364f022060018e75a58d0a39eff9e8981beb767b4582f94478f9bd1172903009cc1d05e0", + "senderId": "AQko9usk8N7wGV6VF8QsxXtsqbsm5YdCDL" + }, + { + "id": "c6267e48bd4e167c42c4bf95f459df6c3dcde1a3a9994523807a98ac5f05c30a", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "034e97723d50ff5a208279b95de3fa6c8d4cb719a8097bd42d7c35ca5df24d77aa", + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "034e97723d50ff5a208279b95de3fa6c8d4cb719a8097bd42d7c35ca5df24d77aa" + } + }, + "signature": "3044022065c542a6a9817f8835a6f938628d5ecc83b750549564547baa9cbb5791b7ae7902203646d3fc72e097b8296ab7198579bdfe7d412ecb09aa8cfd2b0dbe9318b0b81b", + "senderId": "AUP95N5fHeqCuvXVXQZWZpxm8bj77FmASL" + }, + { + "id": "cc0faa4b591b7b71dfee9f7512f31ebd48aea6888497cb726c3aedbd07ded68c", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032998b64be9fec881c3026a5aca7c6a6a726ff9410cdb96d30296724b2822c49d", + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "032998b64be9fec881c3026a5aca7c6a6a726ff9410cdb96d30296724b2822c49d" + } + }, + "signature": "304402202ddd18aaa1f7114be4e4ef8c00e05d24e247c8ae7c8d8d653bfbfdacdac9a9bd02206721cf2f013fe3ccba01a97988cec6b196ddff64028f93893ef95b7f5ae19ea6", + "senderId": "AH5S3Pc7it3gEzUUJN3bZePDzrNN6pEjcR" + }, + { + "id": "e31313b483d887d858db651292ae92ee18b980cba27dc883d678517af47275f1", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032bb244b81b8fe941f4f093c44185a4c4e8dd8aa06aea2c89cd31c5cb5095bbcf", + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "032bb244b81b8fe941f4f093c44185a4c4e8dd8aa06aea2c89cd31c5cb5095bbcf" + } + }, + "signature": "30450221009186d262e10ceadfa04535278ac4d3498365d26e7ca8c3acb68e2bce9a34179f02203daa360c21f3bc7dc6ac211428f44568019e3b43dafdeaac96fa05891d2c6185", + "senderId": "ATF3vBJKFQPKjNyUxieoArZ8TQgso9GGMK" + }, + { + "id": "bf652a118da818f07efa545ec558e8dd051b45c96748516f78125cb6f459da53", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02e89dbafbcb5b131c7855063d403cc9342b4ec56453c7106d2a5fe09e36fee3b2", + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "02e89dbafbcb5b131c7855063d403cc9342b4ec56453c7106d2a5fe09e36fee3b2" + } + }, + "signature": "304402202bdff7b629061c9af9e5ca8c87eff0114e89ddc6085100f90cf161ff8248c94302204ecf909fe1ab09717e29d46afbe360b468d43797a97aec30645caa7887b5c54a", + "senderId": "AXysAKqGRaUaw3XrwXpj91b8CDPY8buvQA" + }, + { + "id": "b2baf710d2146778248d7c05a87bb993ea1dce47a0b91351aafe60debe5f33dd", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03492a56295d3074c25f2fd1aadbb5363bd8b771eee4d3fd1fd900b7dba0ee00e5", + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "03492a56295d3074c25f2fd1aadbb5363bd8b771eee4d3fd1fd900b7dba0ee00e5" + } + }, + "signature": "3045022100a0971ea6d4f3ea0488acf6921e01668ad7ddb82b0bb79fe6eeb4cf31ede039f302205c9330a7b539afced21037fd1a05fc507621bf5264295f200f72deb90aa3b565", + "senderId": "AMefC8bZdYZPgYjcnsEBgVqR4ZXGt3HEmJ" + }, + { + "id": "adf551c6dee08ea329d1d0afece90e5c22fbfc4ed05c5355a767b1a4773e43d7", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0254315ae8629f844cf258cfd85ee5143a1b63fbdff674c59f7e24650ab9912a14", + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "0254315ae8629f844cf258cfd85ee5143a1b63fbdff674c59f7e24650ab9912a14" + } + }, + "signature": "3045022100ef495e0da088d59b74dcbd7846d72617a03fdd5bc865a3b9914211047de30e860220386f587e904838af31d4c7cf1dde35714944fbe82cffea3b27a088c2f6fa1b3a", + "senderId": "AGPnbhUdoCoqdQWi6trWnCjJ3jxb1NuZYo" + }, + { + "id": "1577b0e2c726bee7f8d32a580e40037470d9e387efdc69a8d125d0a24e8093b2", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03c636148f8fe31233ae945d442cb82bc98a9183bc6cad582a23135b7b5136ca11", + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "03c636148f8fe31233ae945d442cb82bc98a9183bc6cad582a23135b7b5136ca11" + } + }, + "signature": "304402202a8db9b5bd862268f1005d2ca517946a338e5c6fb7b478b94891b44e79a1e1bb022014fa1cc48efa8927c47a42708f6e6fe9ab2e61d34b057f9144e07cb4d77b4074", + "senderId": "AJXSVBkY4A65aDLudX8DohM6f7gyzYqLqF" + }, + { + "id": "3eb7ca7b8d2e52dd68da27b54b91573b54cf69c8d6472939a30e67a394592ab0", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0214bd8ac8fcbcb3d04085942a20e8b6294251c5a02e77315d42f48c6ba7c1cd9e", + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "0214bd8ac8fcbcb3d04085942a20e8b6294251c5a02e77315d42f48c6ba7c1cd9e" + } + }, + "signature": "3045022100c7d1fe468fea8c045c8c5e8cb874aa65b63e3a4d50d48a2c809921ec674128f50220151cc061c7f80a3740e9a58ddcd342c94f049fed44049e1dc841475b06797e6e", + "senderId": "AKFbuM4jtwfXCTqCJMXin6Mp3tTubSxPya" + }, + { + "id": "20be520c39e7a5fddef6d596153ebbe9acc96af9956dfada460b301c42be9ac3", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03e01363edc5bd7ab7ff7d35485b376ce666270beb620900d928ee3b6a0cb10fb8", + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "03e01363edc5bd7ab7ff7d35485b376ce666270beb620900d928ee3b6a0cb10fb8" + } + }, + "signature": "304402201d2bcbffc0d798084495e8ae0380a4fa68d0eae725d608a0c749949e00eb373e02202dc70ddd3a8cadcef099141eeb52b9982103524ff28f08d3e867a2de5505f049", + "senderId": "AP5j7BWDUrncx8QRedbFKrxTmGxa3n4uSg" + }, + { + "id": "923ccd9b5490b0d14afa70797cbfb56f529d12cb420934ed8c6fcd894eb5b2ed", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f5dec188574f0b819cde2c0247b4f072f8f91ddf042d550921bc319050aed400", + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "02f5dec188574f0b819cde2c0247b4f072f8f91ddf042d550921bc319050aed400" + } + }, + "signature": "3045022100e8d332132ed581de7471caed3969f28407e8d9b7163bc37364bb62737400f10502207768680444b6d6764f0357b5dab89d9a53ed0c740b3a4eb983909c72b9540efe", + "senderId": "AdAd7MpFF8HYVL5PkZ173wsYutFiWs3svS" + }, + { + "id": "a0398a62b9600d93781a46ef14f04777399c441fb696206c9abee8931f213fca", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f8b1935b22d42ed3b2127e60775299808437a0b641b841bc9376a2898c4f44a1", + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "03f8b1935b22d42ed3b2127e60775299808437a0b641b841bc9376a2898c4f44a1" + } + }, + "signature": "30450221009aa14486ae557e22ebadcab2f7226e0e03b85a235947d772949127478c0ac60a022049452d58b3f1a2f2cb883b6a30d96ee5bdfb103bccbf782f3457a131d12e6f4b", + "senderId": "ASChm8xtb6tHD75zfszZKsscQnjXk5KVo9" + }, + { + "id": "7df3a30f9c5d27618c0d516b4bb5c862aaa31e95cbac726f56e895f2004e018c", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b63f38feb83a17fad2aacf6f953c557f50c94de636b874da14290bbf63f1517e", + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "02b63f38feb83a17fad2aacf6f953c557f50c94de636b874da14290bbf63f1517e" + } + }, + "signature": "304502210085d8047d4535228b429ed85855839732dbf91f019fd75d3888b81e786d5e322f02207d3d10085abadece5bba681981cd66969ab6d448100e1445b90961f0dee979d7", + "senderId": "AUqfHo5psb2xs2vzfikJVkJgfUodjreDuk" + }, + { + "id": "db6e50f672232615205267f3d77a260d0a1441654f598f86c2533dfc004d6c45", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b5957878800178932dab3313d6723bbf506f211fcb83cfb1d5ee22f1274a4c68", + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "02b5957878800178932dab3313d6723bbf506f211fcb83cfb1d5ee22f1274a4c68" + } + }, + "signature": "304402206b12d4c69f9248c77f71ae5dd7365cebdad16c03cf4780786dc6bbf886eb64d702206abd91d3dceeee7b84444b9c04a747b122de677ec38c0c2495640e7e6fa268fe", + "senderId": "ASQqwPSZhBpeaMRt6FinqfDRLwQNUwdLou" + }, + { + "id": "be19a8216ce30743a838d5343ab43427a735eb748604a55834d1d0bf27b623ea", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0281a4d86b3393af3011992b3f6be550aac7eee71993e1425562d260e83c6ead66", + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "0281a4d86b3393af3011992b3f6be550aac7eee71993e1425562d260e83c6ead66" + } + }, + "signature": "3045022100e48825377f2d2876ee85b36ec04ab55565a821e59e39cb4f42c4ad6333509372022079ec71df7ba0ce104b4194155e954f48053023104a075b8a0d9ee5c273b018c5", + "senderId": "AKcewanzTJGJ6akaUBV1zU8EVgFcZ6L6iJ" + }, + { + "id": "ea72c392f6256e9ccba1687efc5df9a3116d58fe05e0e3f199b39660de0bf58a", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02de366d3711a1a5932022f27d6388e5e75d3c1b8d9440db599b2931b03780368d", + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "02de366d3711a1a5932022f27d6388e5e75d3c1b8d9440db599b2931b03780368d" + } + }, + "signature": "3045022100acf8e6c4bfd634100df7b5605110a2187363ca6569bbba16384ce12c79d83b4a022029f58f54ca9313f268b078d48cafb4d6a2f66ecd3dc2f02b768cd36e96421b3e", + "senderId": "APCQx6efoAU2i9aALsmp7VtKcSgteuSwQR" + }, + { + "id": "95338770498b812527409cccf1f864c11b780cd20ab169bcabb995922da8c8ab", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032292a0c76cbf0d24faf582afa6323dfe5384e3bfd2bdf0310d3107f02b0e47be", + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "032292a0c76cbf0d24faf582afa6323dfe5384e3bfd2bdf0310d3107f02b0e47be" + } + }, + "signature": "304402205ab26c60f8eb59a1dcb4f6e89d779df575ab91ce35fe2d04d90ddb0d959532de02205c43da266517a550b23c3929c8564be62ea6818aa9472c7a27110ab89ccb9067", + "senderId": "Abr5cUc7zCXKAyTjz5irn4JcxEmihPG84U" + }, + { + "id": "6d6d335d9f1b399b9ccf888c8bed12a8e77f74eb4c4b15cc9819589a318981c3", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "020d8cc9a44b3cb46316879146eef90584d37ea5af3c2aa0c169a3fd7232f0b282", + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "020d8cc9a44b3cb46316879146eef90584d37ea5af3c2aa0c169a3fd7232f0b282" + } + }, + "signature": "304402200dd0dcae3715024746a993282c9ca686953b5ef2c84551e05d5d4e4147595595022017708d03073a8993ba1c97e500ce262562ce8f4086dedf9f47a8e1c9fd4a9657", + "senderId": "APazxbmv7XuY3HGPv78dKBrmVEcwM7WvQT" + }, + { + "id": "fac36ab057bf4bdaa2d2a0820dd4704a94c0d1202563dbb946a64fe141ff3c6b", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0316510c1409d3307d9f205cac58f1a871499c3ffea3878ddbbb48c821cfbc079a", + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "0316510c1409d3307d9f205cac58f1a871499c3ffea3878ddbbb48c821cfbc079a" + } + }, + "signature": "304402207ba71a7ce535aac054211ffe95aeb6aedc8b555dc9365a28fb8e657c495c077a02204cc218e73621a06ee167b9093911d7aaca6a15ccb1415655e916f938b1cb0a9f", + "senderId": "ASt5oBHKDW8AeJe2Ybc1RucMLS7mRCiuRe" + }, + { + "id": "598fa34389ac8b2ec8582f988a6c5e6d5a1cbd597669fd5ec379506393ae584a", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "036e5e9b5956c8f56d5ecaef8a24141aa0de681ad89a2318b6c2d4125676576866", + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "036e5e9b5956c8f56d5ecaef8a24141aa0de681ad89a2318b6c2d4125676576866" + } + }, + "signature": "3045022100d6d69dae087229727cdb034799d2dff9d83524b87a7c812a3cdcd0585686f1be0220056fa97bc4b37e5515d0c21fefa6b9af3b37243881796c1d1254b9ae74e1982e", + "senderId": "AGuv1r9zRCLhe2Tk2K5r8miaEq4gKvUhwp" + }, + { + "id": "189ccd5e56fdfd5a4c3dc46cec1dc843bacdb83cf26ba287c0a1e4e90de935ea", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f9ec59435f27d92667e7d6e8e929cb4f71cb441a2b403beff607a0894c3377d5", + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "02f9ec59435f27d92667e7d6e8e929cb4f71cb441a2b403beff607a0894c3377d5" + } + }, + "signature": "304502210095ddfb261e2201e4e47ffdc7015b2542cb5e29e93205e588c2b4edb40317698702204d84f60ba494660b1247408f23fa69cc9ecdd039484e5950074a1c50b8e1a73b", + "senderId": "AbN7uVxpy8SPWQeCtF8dJWbHghUDU85NVb" + }, + { + "id": "d8027d83d251f8bdb2750843ecd14401184e7d66c63a8d2e7ed76806670c2e66", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "022cc19322e8a4edd93e5988b775b237692fe14e7475e520c757e1b32669faf3da", + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "022cc19322e8a4edd93e5988b775b237692fe14e7475e520c757e1b32669faf3da" + } + }, + "signature": "304402204a90de4583cab218eac30c05e73b63f978072ec102f080946de9abad3bf80fc1022005d19f67a73ac1550ef64e9a17c2384752231700f204caf97eab315d524c7dcd", + "senderId": "ANpBNG2abwurGKggb8MhbiLYaoSqujaUfB" + }, + { + "id": "104e1ab044b094af536e08b372a6b645c42f9428f8faad0e7fcef73051cfe1b2", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0334cc485a8ff9fbf1ce5221153bec8bfa7f2ece3ce25ff31dacfa1eae91dcf143", + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "0334cc485a8ff9fbf1ce5221153bec8bfa7f2ece3ce25ff31dacfa1eae91dcf143" + } + }, + "signature": "3045022100a026b68b342e3128caefabeb2686a2144d8de1dfd4f9060835538f0b3e4e078902205ee6b7986d3c9f8e56dd1b3999f7f9fde5c438ef10725d91535e0d345fb49106", + "senderId": "AGYtkWXBUD3ohy7a8Yow2sqEuXZpmUWMA2" + }, + { + "id": "2b874377024000078f489c26802852e71c3bb87c16f6d8334d593cd77e55fe68", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a21fb105682f83a4149575fadabc16d836fc255d450bf4e15a6f9733a9c9e46a", + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "03a21fb105682f83a4149575fadabc16d836fc255d450bf4e15a6f9733a9c9e46a" + } + }, + "signature": "304402201bfba24659a4326d599cb34804496993b2777b4c3380108b3dc180815bca65200220010a5f16c9eef85c2586e4da6b5b92e3b5de12f9bdb993f843741ca8e4872b5d", + "senderId": "ALx5FYHiVKrUEogvVAaBHBWtKdrpY5u6Ur" + }, + { + "id": "340ca8dcb274bb729b039d535509e219639556cbf62afec0ba6161c8147b243e", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0374b638d5909814023679e91511744d8be43e7ea92a96ac813fc54170956b9a72", + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "0374b638d5909814023679e91511744d8be43e7ea92a96ac813fc54170956b9a72" + } + }, + "signature": "3044022059d15e6aa1997ee07c16a7992a10d256a60b836be2f66fc37fd6a32a25b06c2d02207cfa37bf469eeba9aad77239f39deb2ba67de12c97224cfbf1c0d9154bb57507", + "senderId": "AFnRoALAnBAd2mZaZYGFT9bBr2Y7re8gCX" + }, + { + "id": "366061dc1f5cf15c1d4881834b18a5eb0a34da527dbd1240099490fa4a6a255d", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0325a3a8faff0d859d0e01d970b6da791c6d40980e6d999ebf50902add2db649ae", + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "0325a3a8faff0d859d0e01d970b6da791c6d40980e6d999ebf50902add2db649ae" + } + }, + "signature": "3045022100daa7ff5f6fd68c4be4106eaeebf6d863116ed1b937b6409d936a7d377b76220e0220179adc41168ec44f3d5b857f27d34dd916c6f477ae5ba221a4cd353fb4994463", + "senderId": "AdHgri2tVkUto6CWb8pgsxyW9ouSteJULN" + }, + { + "id": "ec0b8642bea3659d5f3944692a8c18f379cd35ef56e38594592625e8b2ac2176", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0234b0c5728ad84f77c363893d8724c1030449dab292bf50985c1b4826116488d5", + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "0234b0c5728ad84f77c363893d8724c1030449dab292bf50985c1b4826116488d5" + } + }, + "signature": "304502210092d9331d84eb19a13bd508cd8850f5653cb4411a7b091a11d08ddaa18a8946ad02202bc063031e9749a42e41ea51846df5b5bac72c3e5ecd280c2d231ba0752007d0", + "senderId": "Af2hTgerw9M8GHuWLC7PjJQetsNKy96XG9" + }, + { + "id": "175db25ddfeb3c1d1bc062f4ffbd7ef34f9158c92c945ef5bffb2ba064427599", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "024fa1bc40902ce33ad42cce978299fdb9cfcd9e550e233713a005cc967fe43339", + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "024fa1bc40902ce33ad42cce978299fdb9cfcd9e550e233713a005cc967fe43339" + } + }, + "signature": "3045022100fd6766846b0615314f6d6be650cfcd1249c05b113343cbce868c37dc0c5043a0022070614d7bc62eb391596d5aa783fb3b8f86c44af19eafa7aff950aa7e216771b6", + "senderId": "AKA3HJAnRgJf6pRtuR8zyUXXkAygiY1gYE" + }, + { + "id": "12c83679491456b08126f56c22d2f020cd75a48c4601a5556bdfd0b8ded26a0b", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03bf04879563f0dfee9b9733af248847a03c5b9e89a9a81ca028fc0c44349c7170", + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "03bf04879563f0dfee9b9733af248847a03c5b9e89a9a81ca028fc0c44349c7170" + } + }, + "signature": "304502210094283aa8f0142d2cd3e41191f54b13496d74944953bc9d8faeb047aa55e01e3402200874890848d1319d58e9b2c39d88938567cf6b617c841378fd59a032ffaeeed9", + "senderId": "ANTzfMjau4R5cDdYZrbdbgkeXgYaAny7DP" + }, + { + "id": "93d85a8e0f2eab120f5cd708189c891277ba22d21e96e1520b7bb68acb09bd42", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03e03e8ec4c1de10ec8b4fd9269cbf9584927b99fe2585c5cdd8791b06bd806d5b", + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "03e03e8ec4c1de10ec8b4fd9269cbf9584927b99fe2585c5cdd8791b06bd806d5b" + } + }, + "signature": "304502210097c4aa5a741b76f425c82c2c9a980bebb2619e538ee7570341ced6121a23de0d0220161403379c8348a4359c3ec555a85d9f29af41ef07620273cfed8fe3c4b82218", + "senderId": "AQVGV9A42N9rBhgvMd7FDzRW4A7nzwzqMr" + }, + { + "id": "7dda93252f1ad57c5f658b9f1bd15724e56ffd1609e2522888617295039ebeec", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03fe050cd6a95d41f5bf6f6286c7e0c8029a12ebf38101821842993a545fee0154", + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "03fe050cd6a95d41f5bf6f6286c7e0c8029a12ebf38101821842993a545fee0154" + } + }, + "signature": "3044022044c6a4fe59a94a040be667a6f41c98820abb364b39077bc28ad77c853d7c29f2022053f16744fdc2784ec21dd4a32c9fde339d314fd8efaa7c14e9f9f4764d76e78a", + "senderId": "AV5DuSRJ7o37refVkVcFNkpCbYbsQQFfV1" + }, + { + "id": "8bc5a4be57e9d77b42aef6f0f779059e6ff8736ba7e5fcbf389aff00a6de2d22", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03069b9b610fc540de79e3e3ec75a237a055a0021cedaadde39c260b890eca6453", + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "03069b9b610fc540de79e3e3ec75a237a055a0021cedaadde39c260b890eca6453" + } + }, + "signature": "3044022000951abf3d20273b51ae33a4142ca64c4939bcb45963102980ac05352d97d7ac02201f7f6b1c1a61001568a41a20ea8f0c3ceace13ac2b6c6c492b5f5a2320a09dd7", + "senderId": "AZz7W8kf7FZPaHcTT4CHgUiiTUnKK8hxEg" + }, + { + "id": "6ce884f4cd379476e988483ed41c5a5941765972617df44973970d8d3809b60b", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03852d45a7d7657c972fce42344d5be31166be83d227d757980c8edbd4944505ac", + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "03852d45a7d7657c972fce42344d5be31166be83d227d757980c8edbd4944505ac" + } + }, + "signature": "304402201eb3248466785fe74fa83a5fb317e8fcfcf1c332e24454f30cb951166622b63b0220073c686386cecd62f81cce1ddb5eeda9ad54e439eeb93dd8e43462b2a6781c6f", + "senderId": "AdvTKR9AT1MSALnaiAmvRBukvomZDj86gx" + }, + { + "id": "dab21d6e4c577eb183ca33a78654b4ddc0d0447184087de10c8db1eda6bf61de", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03aab25b27a4eedbcbcc8aa4f8e5a722f1ad03db9a107ef68465c6ea1d18337116", + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "03aab25b27a4eedbcbcc8aa4f8e5a722f1ad03db9a107ef68465c6ea1d18337116" + } + }, + "signature": "3045022100a7a9139985357e8dfa690be74c76eeffd7e7781cd53e09ec866c9f0bac96c9ea02202c48381a310d671a12d8a5210906d3ba75ba8bec783f5d0661da2defb1436676", + "senderId": "ALCLA26axFSNRsWHnECjAggxbkSEnU2ncB" + }, + { + "id": "ad97ad8e2a19da3a48b784e75ea24f5f8e55f046cbe24657f55bc8a15eec7d61", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02d1a5ad2e0a094a430d5158447b9be383d1415c2678c890e39d2c811e27789359", + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "02d1a5ad2e0a094a430d5158447b9be383d1415c2678c890e39d2c811e27789359" + } + }, + "signature": "3045022100f8bf332d13c699f4dbc12446dc7a360096bf0698632bf8374d243fbf5b184f3a022007b806b1081f4555555e69bf99813e14ea900012deba396005d8a9aca33741b8", + "senderId": "ALvY3DscFkU4H5EXSRe59wpE2CHuHRSaqo" + }, + { + "id": "a9d20c478a6eff3e1411c62356258eb625ae3986273b481c8cfc220352d69454", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "025d124dd255096ab185771bd9284c69bfea670a497b04a1bba054f8feda60fd3a", + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "025d124dd255096ab185771bd9284c69bfea670a497b04a1bba054f8feda60fd3a" + } + }, + "signature": "3045022100dba697809453a29db7aa55fa2ac912fb94249cde40eed4bc6364ab0c2eb69dc302207fbb59d91e874ff7d89f1f0e43d1c2b631f8a6725518e39bb3946d787bd52837", + "senderId": "AbsErLb34KfWNLV7ChC3QheZpqgxdp4Fxv" + }, + { + "id": "d7b061c3912f7230a48d0e783da2ebf1903b848433c3bddc1417997760f0a397", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0316b3dc139c1a35927ecbdcb8d8b628ad06bd4f1869fe3ad0e23c8106678a460f", + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "0316b3dc139c1a35927ecbdcb8d8b628ad06bd4f1869fe3ad0e23c8106678a460f" + } + }, + "signature": "304502210086b4b602e0ff8cc73c1a74fdc3aa25e81f3786a8d67f90481647e76b93c2c64702206bd52e147c1e247205c8fa0fbc83d6035727d52035cf08df62b29f3a7378c968", + "senderId": "AWoysqF1xm1LXYLQvmRDpfVNKzzaLVwPVM" + }, + { + "id": "bd507d6ffea6cc86c518b0111bbb0ce573ef558e95c5e88d3d70ecbe8183febc", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02778aa3d5b332965ea2a5ef6ac479ce2478535bc681a098dff1d683ff6eccc417", + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "02778aa3d5b332965ea2a5ef6ac479ce2478535bc681a098dff1d683ff6eccc417" + } + }, + "signature": "3045022100ff6988ff42fef54d0afc6c0e83f120fa128948969ca5074ed95625dbcba526dc0220567856547a4984692a6d56fece27862eb747232d3969435c00c2798efba09c3a", + "senderId": "ANwc3YQe3EBjuE5sNRacP7fhkngAPaBW4Y" + } + ], + "height": 1, + "id": "4881670189836572019", + "blockSignature": "3045022100b0cbfdfabb77b7d431cb7fdc3acd148032898eb6b0026d4e8f6f08f8e5ca23b5022044cfad1c8e0df615b0969c5d1fe4965b2c18e6656becc2d5410c68ed19452770" +} diff --git a/packages/core-test-utils/src/config/unitnet/peers.json b/packages/core-test-utils/src/config/unitnet/peers.json new file mode 100644 index 0000000000..fa4e124d8d --- /dev/null +++ b/packages/core-test-utils/src/config/unitnet/peers.json @@ -0,0 +1,8 @@ +{ + "list": [ + { + "ip": "0.0.0.99", + "port": 4000 + } + ] +} diff --git a/packages/core-test-utils/src/config/unitnet/plugins.js b/packages/core-test-utils/src/config/unitnet/plugins.js new file mode 100644 index 0000000000..662f1de692 --- /dev/null +++ b/packages/core-test-utils/src/config/unitnet/plugins.js @@ -0,0 +1,78 @@ +module.exports = { + "@arkecosystem/core-event-emitter": {}, + "@arkecosystem/core-logger-winston": { + transports: { + console: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + dailyRotate: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + }, + }, + "@arkecosystem/core-database-postgres": { + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_development`, + user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + password: process.env.CORE_DB_PASSWORD || "password", + }, + }, + "@arkecosystem/core-transaction-pool": { + enabled: !process.env.CORE_TRANSACTION_POOL_DISABLED, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + // 100+ years in the future to avoid our hardcoded transactions used in the + // tests to expire immediately + maxTransactionAge: 4036608000, + dynamicFees: { + minFeePool: 1000, + minFeeBroadcast: 1000, + }, + }, + "@arkecosystem/core-p2p": { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4000, + minimumVersion: ">=2.0.0", + minimumNetworkReach: 5, + coldStart: 5, + }, + "@arkecosystem/core-blockchain": { + fastRebuild: false, + }, + "@arkecosystem/core-api": { + enabled: !process.env.CORE_API_DISABLED, + host: process.env.CORE_API_HOST || "0.0.0.0", + port: process.env.CORE_API_PORT || 4003, + whitelist: ["*"], + }, + "@arkecosystem/core-webhooks": { + enabled: process.env.CORE_WEBHOOKS_ENABLED, + server: { + enabled: process.env.CORE_WEBHOOKS_API_ENABLED, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + }, + "@arkecosystem/core-graphql": { + enabled: process.env.CORE_GRAPHQL_ENABLED, + host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", + port: process.env.CORE_GRAPHQL_PORT || 4005, + }, + "@arkecosystem/core-forger": { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4000}`], + }, + "@arkecosystem/core-json-rpc": { + enabled: process.env.CORE_JSON_RPC_ENABLED, + host: process.env.CORE_JSON_RPC_HOST || "0.0.0.0", + port: process.env.CORE_JSON_RPC_PORT || 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, +}; diff --git a/packages/core-test-utils/src/config/unitnet/wallets.json b/packages/core-test-utils/src/config/unitnet/wallets.json new file mode 100644 index 0000000000..6ccbe4deb1 --- /dev/null +++ b/packages/core-test-utils/src/config/unitnet/wallets.json @@ -0,0 +1,463 @@ +{ + "wallets": [ + { + "address": "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + "passphrase": "medal armed idle abstract winner convince brush treat width pet crane language", + "keys": { + "publicKey": "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + "privateKey": "c13bcd9a3dd64cabb27fcf2f4a471d35ffc3c114bb1278de745e6ff82a72eda8", + "compressed": true + } + }, + { + "address": "ASjr37NksJJunDrwkCPKyZRANPLPxFNGSJ", + "passphrase": "radio gossip stage caught hand slim cram scan trim truly price steak", + "keys": { + "publicKey": "03fef68dd47bc81ec206b64d973df1ae9b51d54632af1ce703958e62ddcec67ca6", + "privateKey": "d33db94b505fa904b268421a21a7b22d624cbfa3dce34e75961d7b87fb032a6f", + "compressed": true + } + }, + { + "address": "AGvYueD6VRnE2D83cU8FLhUzKHmZXbS5Vo", + "passphrase": "whisper risk easily town elbow oil pear brief enough forum bike where", + "keys": { + "publicKey": "029afb1971cf06955b8e96cfc639add8a7c9d96f0c3a2889ccb1670a8cd231fc0b", + "privateKey": "fbbec7ddf3902d443e251894e49d38c118473cbae5fb31c79258d105ac81319c", + "compressed": true + } + }, + { + "address": "AFv8tBd4biYfZrAYpgDmcvAqgdBi9y5k5c", + "passphrase": "again stairs dilemma high say over seat awake dad menu reward harvest", + "keys": { + "publicKey": "03b581d35b9cfc4e87d70cdc2b9bd114ac7410e0deeb495fbfd3747d9c2eddeb97", + "privateKey": "df808d8fa80cc26abafa544bdb533032ed7616d4ef151b6dc0272e9458b89a5d", + "compressed": true + } + }, + { + "address": "AbuQtgGCksfDowEg62W3R6fN8iUSKEuQ7p", + "passphrase": "bring ostrich stand tunnel remove habit deny canal program enough belt pretty", + "keys": { + "publicKey": "0356d0b1423208aac6e6a9fcddc29199d61f6438bbac285ed2a7d2e4d3bb6026c0", + "privateKey": "541ea42bfdc4f036d149ee1838edd0f9582ea87c29674ddde0631812a3fd6455", + "compressed": true + } + }, + { + "address": "AGkZtkcyh8i8wDoiGm2zHvfLzZrhWc5V3w", + "passphrase": "quiz before aim ostrich gorilla you burger okay caught green return feel", + "keys": { + "publicKey": "0237275b6e35bd0be5f875f0d289229146118762c84af4993ccde07af6bae51472", + "privateKey": "22e5f7225908f27a999df83a8d108f3dcf365697b828f26d0236cf650a0f75e8", + "compressed": true + } + }, + { + "address": "AYXnBEVyMzQ5ujJjyxrGnfexubnAdc3Xi5", + "passphrase": "pizza essence wing ranch shift chest slice wrap thunder document flat attract", + "keys": { + "publicKey": "02f5808dfff4506206c7ed5a2fe99bd0127d5a06e518364964714f9a599f7cf165", + "privateKey": "94e890c183599d3f99883f40d3a755fad28194bb2cc2687653fdde1a9cf9691b", + "compressed": true + } + }, + { + "address": "AUNdcLwCjM1n3oaqxMKmkZPWtdYPNS3FgM", + "passphrase": "curtain dove civil glance rude tilt debris client invest type tag slice", + "keys": { + "publicKey": "0380fe036f067f6133921b3fba1fc17d1d38a94bd857dc3eeda81f3065a4105028", + "privateKey": "df50524b89414fcb5e6f8372530aff2fd262933afb16466939c535f56ec27b24", + "compressed": true + } + }, + { + "address": "AYUTobeLMQdRv9mgoK3JSfBriNi9iGS1Ff", + "passphrase": "catalog inquiry search van exercise material afford run festival good theme away", + "keys": { + "publicKey": "02b234294fbce19b657dc523a8ce176aa0a093d2a1a7214f06d0f849e6bae845bd", + "privateKey": "2c246da12663812ebcd031e666f7b16f8571a75d3b702de48bbb7283a4a418ae", + "compressed": true + } + }, + { + "address": "AbW8nwbHoDhtVsdVHVVQLy88vRHfySnEkU", + "passphrase": "puppy upper know cluster process silly mass million critic swamp age credit", + "keys": { + "publicKey": "0249a34bfd4aa47bb196cf2f3a9d6e2e5cc0d3be030710dcc7114ca03d023da1b0", + "privateKey": "bbf8ffdec95dda3794ace40850c062daf2c5f1d2abc67d7221224b2adb946f8e", + "compressed": true + } + }, + { + "address": "Aa11BRTWqZbeb4jXAJkGqaQ78SHmXikVrj", + "passphrase": "earth indoor gloom abandon return theme wedding picnic inflict chalk disorder figure", + "keys": { + "publicKey": "02358969cd9a13d4458f51487ec9bd9fb1b3f2d716930f2f4ebd1a864222e12cd7", + "privateKey": "bf36fbbb32ffaabb9550808cb8da7939bf4ff8d38aab2a7bbbb897244fed1831", + "compressed": true + } + }, + { + "address": "AWGWuovUFcxKNzNy5An8AFA2JfxfSnEGDK", + "passphrase": "target device bounce cream clean capable harsh lady engine helmet silver job", + "keys": { + "publicKey": "023abda4beecbd4094578a8407dc7685036a555bccff4573df83d4fc08fa89e04d", + "privateKey": "fa3f7fb2589bd46fab1735d1f991dc565a369ee13f9386159d94d55bdb609b25", + "compressed": true + } + }, + { + "address": "AZaos8cfC2u9TLw7mE3qMPJuEtuDLyxtVC", + "passphrase": "thumb demise axis rug laugh pave lesson viable width episode ghost elephant", + "keys": { + "publicKey": "03655a9333d03dab669b1695a299e1738338e985a239814db2021f71a49bd3633b", + "privateKey": "def35700ab6612d339c388728060b5ad1d00a15e7a188013939adf70b6404389", + "compressed": true + } + }, + { + "address": "AaP3FYebrQXfLT64T2r52HsVEYXDU41QEk", + "passphrase": "property pulse salt scare throw ritual hen try cancel box differ mail", + "keys": { + "publicKey": "025297fff5e8ab804b89fa745a11c96a4199bb7567f531a9368fa1ecf65bbf28b9", + "privateKey": "44cea825b7fb94a73d2cc49cf4da386e4e9588e9f10051bddac8860ad290f605", + "compressed": true + } + }, + { + "address": "AMnKgkf7DT6vcyjTBs3gPqPhTNJ2z4oY95", + "passphrase": "legend engage buddy slot illness cash across design problem first pass any", + "keys": { + "publicKey": "0248283a8398592cc545c5e1ebba640fa218463b6231023cc86fce41a1cc106288", + "privateKey": "9fac03cbd32fdb2ce98b8e6aa8a49a132075875ac765750c2e5d81c80e3bb58e", + "compressed": true + } + }, + { + "address": "Act3vdrPTWgJFoRaYdRtHCZAwfQ5cJpbF9", + "passphrase": "stomach rare tool orange calm ring risk slot soap floor catalog join", + "keys": { + "publicKey": "033ca48ab042d88a78d32bf0195ed68f0d12090e6ea130acf42bf69298567593c1", + "privateKey": "c6d08fa1e113a735df64e73708691b1a686289e5f41388fe2eb6c1b7d2906c5e", + "compressed": true + } + }, + { + "address": "AH9MHh829oyDk4f4sJhtEWvQTENY1P215p", + "passphrase": "mimic eye legend drama leopard join carbon marine cat pelican maze scrap", + "keys": { + "publicKey": "03a370339c6c77cb39d56032fcb4c7eb0cb126528413b10667b65401a3d24078d4", + "privateKey": "91b248967d354bf4919e5f34809c871eccd0c4ca33863de07d958a2d7445fb7c", + "compressed": true + } + }, + { + "address": "APJYmiKAtvmPQmuibHB7qab8EhDoTHYeuj", + "passphrase": "betray hello cancel blind artwork client canyon gospel comfort echo duck junior", + "keys": { + "publicKey": "02d84ba4c7505e0cc4ec26b5ad724cb5f5abdf8ff23803bb9edb2a4caf9a800db8", + "privateKey": "2a4f1e56347e1cabf59d917b3babf8d34dfc11b9a7105855d3849b3bd04bc4e4", + "compressed": true + } + }, + { + "address": "AXn57wj5CCcAts34twf1V9oTLVoNuRH1YN", + "passphrase": "spoon acquire valve pitch solution gentle cream cable reunion fog pyramid off", + "keys": { + "publicKey": "038a782e4bf5dd01884daa65d13783148bb5b205f77a14f68c9eb6b3d9f8d5b0fd", + "privateKey": "ec42857c7473330105054006ac7b38d1a974d82a7d911f3cb88da223231987da", + "compressed": true + } + }, + { + "address": "AH8LYTdW45WTpKL4vE9TycXfoAYQvBstkn", + "passphrase": "whisper license mom dinosaur deputy token leader indicate hurt resemble bracket fish", + "keys": { + "publicKey": "0315f8aba185d1ba800971badab9cc06830b4d1820ba9a13021fa0dc7470753ac6", + "privateKey": "515ff64cea0c064cadfa89a3320584c696858c28d50886003e6b61cbf032ca77", + "compressed": true + } + }, + { + "address": "AMZ8udbpMyJPAj3qwWKGQmtTyfoorV79Yq", + "passphrase": "jaguar suggest balance flavor metal broccoli another essay note spring glimpse harbor", + "keys": { + "publicKey": "0333dca9dfd15dab10f1f4040bf6689c79e893d6e2598d0a6668233fd00e8c1026", + "privateKey": "116903f931b1d9148fdb4e8aaa85eb0728d7a094c2d951324757cb4d51ab4d8a", + "compressed": true + } + }, + { + "address": "AeS8atkPDW2y7vXo5oPtK7ifTEYKytVi9i", + "passphrase": "lobster dwarf inner amused resource disorder roast dance clay noise tonight scrap", + "keys": { + "publicKey": "02ae481f89b3899dd5a4245c300a566824ef04ea47ec4d47f1a5358122248a2855", + "privateKey": "f06bad2609163bb9b5e9ac133825c56ec59f21feed6162be7e925e6eb709e08a", + "compressed": true + } + }, + { + "address": "APyHyZ16VgCEgLERv86jUx4qYjCZH1NWjq", + "passphrase": "scissors false custom fish approve shoot category spin bounce burger rabbit album", + "keys": { + "publicKey": "03cb3b6f0aa9f47ba6def9d16a6f4d9bd246e6e160515fc8375ab475317d58fcd7", + "privateKey": "5bba9a2f281f9097b6c4a6ed09e23ac12956b5117e6acb7cf5001736c53c7e15", + "compressed": true + } + }, + { + "address": "AT447ubYEVkZjS6MesqrRkha1CKfUxVzmi", + "passphrase": "poem drift produce brief churn sound hello inherit apology regular foster list", + "keys": { + "publicKey": "0313ab74f11d66a0801b50ad8c8335bebbfe917534fa624e4e802cb2afd1b7262a", + "privateKey": "f0b23c00e93be4012379b5c3b77448a4743aecfe7ddb8e0a9ee27cba07901564", + "compressed": true + } + }, + { + "address": "ANZ4tvGqebEWKtXiHgzFVCsv6KEiEGNupr", + "passphrase": "loop know blur elite extra female saddle slide walk cruel segment cigar", + "keys": { + "publicKey": "038b6b2b34a240f5a487698a51916c08c9e95b6bad3cc2bb157a6e1335b7c7a6f9", + "privateKey": "58d6248d8210d65916bbfa34126ec90bed367cdd25ef13a2ffcc3a01ed5bec50", + "compressed": true + } + }, + { + "address": "AbxSmnyBVzx3FGMjaFkE5tdXMZZvvuH8wd", + "passphrase": "like attack excite notable deliver joke brief fold special repeat mammal stadium", + "keys": { + "publicKey": "0399801dd70f620689c6e66257c3eb7c3df1e589509fb3efb8ffaf988f70688789", + "privateKey": "bcad6e9225f387ef730206b6970e54ee2accbb287af1e555ec4231a46d6325ff", + "compressed": true + } + }, + { + "address": "AWcwdfqduZx2SsdW6JDpvYqnDT7KbrQ7pZ", + "passphrase": "spoon view buddy tooth wear mixed crane liar vicious tide brother atom", + "keys": { + "publicKey": "0243a4720e2f2aba7ad3fe6f0766377705f9800515472165afe69e866a904f8c3c", + "privateKey": "36d1ed5f44c0ef26a7ba58e594cf4c5de365c6a589c6ce663ab4befb02eded92", + "compressed": true + } + }, + { + "address": "AHsJaHCVtNbZHprRjZjutEFgQ4LVbxkGCJ", + "passphrase": "cinnamon jungle air clip canoe skull journey mouse broccoli banner cliff mother", + "keys": { + "publicKey": "037c7e47a284ae86b91a26ee067fd13562ad5d5a7c46eead1b0ba0b50ae386165a", + "privateKey": "6da0ff59270b0404ce44e477811855757d7eb6daed7a7aa3373f1b8f335ce449", + "compressed": true + } + }, + { + "address": "Adg7FvqeGvnNM9UsvcntypKQnrPEymXhQz", + "passphrase": "squeeze calm squirrel supreme critic orbit wonder renew key settle chuckle fee", + "keys": { + "publicKey": "03b3c0f6d1f10b46abf2e43a408c68ab202d1f26de82ec45a1d975872cff7c476c", + "privateKey": "1eca7e7bb911b8449fdc11033c25b91958a35f156d3e050e85b6d814f680586f", + "compressed": true + } + }, + { + "address": "ALQf9fK6iT4RSkwimHepPZ2p2jViAqeA5S", + "passphrase": "mask margin grid soup gown arena ridge aunt lawn reveal endless endorse", + "keys": { + "publicKey": "03a437cc06a86fd55a41c94a9a3db91e1b472b814470b914e100bd3b315391d8c2", + "privateKey": "17a5bee786efa0be7ded4e394713ab1e6850c8eae81ed1a602827da233e3aea0", + "compressed": true + } + }, + { + "address": "AN2cyuquL84qWSQLhEXifdSHod3Nd2a8E3", + "passphrase": "whale resist price bulb industry battle team smile problem option mushroom earth", + "keys": { + "publicKey": "02fb9620c7064219014f555943d9bf79e6903dac2ea78236ae1124f1f83f41a450", + "privateKey": "643cebc5b8f3d9aadbbfb461c11594a4cd4c859f709e519740327fa8b6f8260a", + "compressed": true + } + }, + { + "address": "AZiXrft2ugGCqhZSTEBgnqpJyL8Tv91ZsW", + "passphrase": "network join prevent fiber crack ill ankle sun body ball outdoor sudden", + "keys": { + "publicKey": "0386a02a400516b6bc118fbfee4582614b41c52c9354cbfba059bb7815e7e96b8c", + "privateKey": "fb8c465d4ae9453d9d4cb1776e34008def8ee18014e58314e2d9865bbbc76e83", + "compressed": true + } + }, + { + "address": "AXRk7ZtUZpswU1bKyPmKAyH4Qu7itHBRSK", + "passphrase": "buzz ahead disorder version master wash soda puppy ladder group clay blood", + "keys": { + "publicKey": "025c132a2e250af5021810db1410a09f2bc691b3843789986dd4757c077aef0964", + "privateKey": "4da5dd8c9984a1810918f07ff9b52202278ef05994763a03d1ae9795094d1231", + "compressed": true + } + }, + { + "address": "ARYSXqnkS5uC876GTEtfQF4RbJ6Bm9Ytm6", + "passphrase": "chef salt century heart general miracle solid garment ceiling bargain pulp turtle", + "keys": { + "publicKey": "02edc023368683c99f682413fd4781976e698ece982be8cae0338b6e55ab03fa86", + "privateKey": "8ca71ffd568ffe1b7b9b3bc10c2413c24d0cdb0f6b5c40811c870d9bd086d5f1", + "compressed": true + } + }, + { + "address": "AQkf1rHnN3n5dtFsT6RVfEdrdFf1tzwjH6", + "passphrase": "devote slush relief match helmet canvas hover void grain alien report october", + "keys": { + "publicKey": "0329292790095736ae29aa5fb8dc07ec5ac61bf6d0a4bce734b21b5244fa2d1c6f", + "privateKey": "b30938eaf819c0522c7a5928f6547e1b12012b390d7b4932a3e104d2b2b4bc4c", + "compressed": true + } + }, + { + "address": "AJnvt6gphaN3Kr5gU51jEPkwdk1GFPnxAe", + "passphrase": "transfer twice permit slam horse mixed math park divide cargo piece repair", + "keys": { + "publicKey": "02d6d8f61130f478e7b2b853c057816e7c74a980273939644b7482191782a74146", + "privateKey": "0f3a02915c8a564fe2b7968b3b464728e293791a4fde10c3643be945cf5002ad", + "compressed": true + } + }, + { + "address": "AZKmryqiPiowe79yvVQ18vgRTLsfcXuZ86", + "passphrase": "token mistake slow lion border lady behave broken develop company spare clarify", + "keys": { + "publicKey": "0317c23a818b62a263c9679af3bf767150228998d4a46b641b208ce58f33f9bcdf", + "privateKey": "9ed5f0fe507fcf87a5e726bb0d5396ea849767cfcce45935405d5a01d580a208", + "compressed": true + } + }, + { + "address": "AWoGgnki5EqT6oKB2gB5xa2FSxnhwqtm3T", + "passphrase": "assault achieve sail unhappy put whip sweet visual behind neck steel father", + "keys": { + "publicKey": "02844de35832d0cc3ed686d6231e8d4bc63744fc16d67ce09f59f8d4cbbaef4997", + "privateKey": "db385dfd089155b8b5b6785c181dc6be0517e91ba1a4038259a5a86288cc94ad", + "compressed": true + } + }, + { + "address": "AZRMoSEx5YBC7P2s78XzLStbRigMe96HrE", + "passphrase": "play borrow parade put peasant switch camera churn power clutch car snow", + "keys": { + "publicKey": "02995e543ceec2a1e574cab584354e9e36d0edcb41a7c4cd27e4640b8de6550793", + "privateKey": "be51f8d14f6e08f0810c4949ee803b1dc2a06457aafe57a0bc715c40cfc3c829", + "compressed": true + } + }, + { + "address": "AK3CFg4RFwRydgFb2woho9TCE21tRkzsB7", + "passphrase": "valid wash super oblige suspect misery rent chat word stereo tenant document", + "keys": { + "publicKey": "02e34b9f2c738c63a7df01df5c51a4ee303a24ab1c970847fc15121d9c4381f490", + "privateKey": "04b44d5d34cd2dbbab632dc2124038f2680e36aee7476fd3da0d1db0084fc87d", + "compressed": true + } + }, + { + "address": "AZUANHK3Gkk9yGAXmebVkqqRokJuzDNZyE", + "passphrase": "topple distance grid truly fantasy witness figure search never flock uncover ask", + "keys": { + "publicKey": "03ccac4091674f843981857af5f01be2fb7de7d76cb1739adc526d85e6f982a560", + "privateKey": "eb890c6428f880aff9d279ffc818f989e7f68a016a51f51cc20afdd09d8f0bb4", + "compressed": true + } + }, + { + "address": "AYzLFs9vPy2wC8niqP4eEamt2eGmTjsyDk", + "passphrase": "present hill female almost humble during view tired pear iron just fault", + "keys": { + "publicKey": "034eea6ee3e88f8d59b4c80677d536080bf9eb201962bc3c2a1000842f4aa7bac9", + "privateKey": "d1d4193d1dc2c82ad0b9b580b67b59fa8df5e84ff28435538144340b27997644", + "compressed": true + } + }, + { + "address": "AVuRN3XiwUTwTrwTAds3sVN1MVhHUhLn4X", + "passphrase": "situate amount grain fee pulp burden situate lottery open crystal obscure quick", + "keys": { + "publicKey": "0298df5bb9595224d738d8d8981c63e14ebe5c813275933b30274199ab2ff0b16f", + "privateKey": "742015d94b5d33752eb807969a89e5ae61799e188215d58d85a691dc8f4d50f2", + "compressed": true + } + }, + { + "address": "AXRp7afm6g4ZYscTJkCmB9VfzrpwMyRr5h", + "passphrase": "hat power fiction inquiry sick awake quick proof begin hockey lottery spell", + "keys": { + "publicKey": "0375670ab42fa9b1e3c7b49805d121c9fe853fa88f465824235549a96d95090868", + "privateKey": "b09f31e19f3bef142250b38e1e9f1a2cdae673fe429296ea3605ffea3f7344ef", + "compressed": true + } + }, + { + "address": "AJn7wi5twb3D9UUPBjV1Y2XnCDhVot8RLp", + "passphrase": "duty flight extend bird prosper bottom network giraffe kid bacon axis impact", + "keys": { + "publicKey": "0371be5e24d8fa69c27d1f46807ae9ddb20e9a6fe3d040f100a4169d233cd876d6", + "privateKey": "dd55ce5054697f64a4ae05fddbeb0269b0416ec35d08c2fb1cd04c694fb7884b", + "compressed": true + } + }, + { + "address": "AckcWKfPcT3xoYzFALHK7i7LYTTM4cHVq6", + "passphrase": "erase someone stool siren asthma circle wall divorce expect enemy rule flag", + "keys": { + "publicKey": "037a47b1dff4a9858ecd4918765349fa5b997383dd5345c0fa65527a35e0358acf", + "privateKey": "d43ede134debcad34317ddc20adc23bc28d87ac3306ff37e58ea1523f98d9460", + "compressed": true + } + }, + { + "address": "APttZLM1BYZAvkxh9LVJt8XRwuJZTXnocM", + "passphrase": "settle fitness remind wasp all glass boat drift zone cigar derive suit", + "keys": { + "publicKey": "02d9f8aace468d99e9b75187e6622807efc106fc51c13896a4fbb45e95cd005ff2", + "privateKey": "9212ca850402f803a121b74ecda499ac627d629b383ac80bfb0175a8dab0292c", + "compressed": true + } + }, + { + "address": "AbHBVzCfCkwiNky9zDmZNNTHx7ZCnpgHCH", + "passphrase": "engine tenant night rough surge million beef danger spend pretty crucial gadget", + "keys": { + "publicKey": "02c3ec422099fc1048eac0d5887568083c077d0e601345ec6be5ad54dd35f4ffc5", + "privateKey": "edad8c187ee0a03417254044e24022ac0186af675745e533560324be1c447b20", + "compressed": true + } + }, + { + "address": "AS1dhJTJZMJHqea1zqy6h3VnLeDYEsW7Zq", + "passphrase": "fold submit left dove melody angry tonight hover enter lonely brief orphan", + "keys": { + "publicKey": "038e78f99c168f5324f6618a0fbe3b1410c77c84f23ca2e05c06e1893c1cee704f", + "privateKey": "52a9b796acd2906be4b191340597caac92dcc0374169b5d06d2c16cf76e6e530", + "compressed": true + } + }, + { + "address": "AMHwi4hvpkhSaet9VDoeCDYMMTh7hnV2Sa", + "passphrase": "middle friend leave excuse swift city evidence approve green various tent join", + "keys": { + "publicKey": "02bb7ac7069c7ee584f7b8a453f5fd316f1a7352f098a4be0205fbc7700c492ff4", + "privateKey": "22e8cb9b51f72452756bfb054e1251307024fbcc36c2bda1f0d9d4aeb6fc0e67", + "compressed": true + } + }, + { + "address": "AQKURU7zdHs8tjEHWLt5ipAXaGCgYxknvM", + "passphrase": "yard scare room quantum car before fiction save camp exit shield tragic", + "keys": { + "publicKey": "02117998d03589ced8ac888ff39ec39f8a5639caa86ac356184adef9aeceaffbda", + "privateKey": "431ebf15dd83c9ac8772dcedb893cdf50438fb00f65ba2a5b523042dd471ef0b", + "compressed": true + } + } + ] +} diff --git a/packages/core-test-utils/src/config/unitnet/wallets2ndSig.json b/packages/core-test-utils/src/config/unitnet/wallets2ndSig.json new file mode 100644 index 0000000000..9ebb5a8365 --- /dev/null +++ b/packages/core-test-utils/src/config/unitnet/wallets2ndSig.json @@ -0,0 +1,514 @@ +{ + "wallets2ndSig": [ + { + "address": "AKR9GXZGUYqz441D359coxztJonviA2FRM", + "passphrase": "audit inject slight neutral tool leaf century hold tent rain patrol dry", + "keys": { + "publicKey": "028e16ce65270805eb06a1671cc5ddcff9f8d8ffce13387118fa5ab0fe39616052", + "privateKey": "3d865606200188f736d66163f670c1090ea83f79e3ae26dd003290ff03332e32", + "compressed": true + }, + "secondPassphrase": "tumble fan dose inch other off gravity absent tiny snow kid series" + }, + { + "address": "ASn4LHMaH819aCuUwynTBZsaoUwG3uxAhC", + "passphrase": "ensure minimum grow couch imitate genius response ancient future ghost trust distance", + "keys": { + "publicKey": "0223566d2ee34ed38a542e6bab5aa82feada724c86322a42ba544ac0a0d3cb90be", + "privateKey": "7bcce4f7b787ec141bffec919b1649d2b9da84ec81e2c656386abd629110c290", + "compressed": true + }, + "secondPassphrase": "harvest almost play ripple panther wait play allow toe cry dolphin average" + }, + { + "address": "ATMnnCBtz4mYEi5oEqkCnDWk9RkaeNLvw8", + "passphrase": "grant panda general width program hero town need stairs stereo copy rose", + "keys": { + "publicKey": "02d59edc9009c55f4b271e0880ccc2e710eff3a69b5e41ee8f0573d93c639e7cf2", + "privateKey": "a2064f75a0cba52eec9881077658230ed882250560c7ca60eecbb722b01dedce", + "compressed": true + }, + "secondPassphrase": "trust clog mobile august fat warfare farm rare slogan invite gentle sauce" + }, + { + "address": "Abu8suR9L2tD7F4gAGHMaQqVbdvXdCFfXG", + "passphrase": "cannon hotel duck notable brave loud matrix resource horse talk miss dress", + "keys": { + "publicKey": "02f4fc615d167b6e6e4ffd2c6d63ae7ff621341dacb8b0ab5809b82cd478304bc2", + "privateKey": "bfea0d90a1a09e8d443b9134dd55003ddfeae68c0ed6edc50163cb1cbf1ef12f", + "compressed": true + }, + "secondPassphrase": "tribe polar cinnamon tell often future tree any carpet tattoo close peanut" + }, + { + "address": "AL6sZNMWm2VGABvMX4F6eVFLUQJQyJup6i", + "passphrase": "shock broccoli fade around valid clinic better rely now oppose gossip change", + "keys": { + "publicKey": "039c0311d587b5bf354c2d42965436048dfadaf9d5f6fce7c5f842e5480f298c5c", + "privateKey": "8a7510f4107ac219d1ae2e381431dd970d07c9bcc3357b4581b0f839e87ced3c", + "compressed": true + }, + "secondPassphrase": "tunnel radio family hard metal cat rival orbit course urban toddler cage" + }, + { + "address": "AFyrB1QJSh7enu2JpMUYEKUgv3xnJo5gUJ", + "passphrase": "siren solve cable push range quote reflect zebra burden skate ability orbit", + "keys": { + "publicKey": "03882346ba5e7e2874d1cfd1ec8d30f984be9f57021b72f56ec5b4d4aee79caebf", + "privateKey": "d8c324084139c960140c05386c2dcf8dbdd48e542ed7ed676330f0a1a038164c", + "compressed": true + }, + "secondPassphrase": "live begin demise bitter juice shallow jelly saddle valley faculty advice senior" + }, + { + "address": "AVQ7sUZaTDi6MgKNuNxzzC1Sn3ee3KjdNg", + "passphrase": "roast segment vibrant police capable indicate boy drastic make sound kiwi negative", + "keys": { + "publicKey": "03412d936ec21e8b69f43c4c4945e24defeb956c777d71b974740fe2f1b90dd4d1", + "privateKey": "27e4072ecd1b3ea488bb3c2cac7626c1a550555b57f00989aaab10a62ef180c0", + "compressed": true + }, + "secondPassphrase": "bless accident wing verify fat arm quantum regret social erode detail angle" + }, + { + "address": "ALf5oWGQhq2xbTSZyCfDuvxbnDSjvYqiKE", + "passphrase": "fluid nose monster age struggle actress long embody wise relief asthma there", + "keys": { + "publicKey": "0264d678a13dddf54000ef95a38be00aa6e8698d82cbefa43c5c35808749d5053e", + "privateKey": "309a775302612ff309609031ea0bd81ac59eb59ffb71518fe5f93497a0284fb4", + "compressed": true + }, + "secondPassphrase": "load spike hunt scrap remember claim venue gauge bid rely margin lobster" + }, + { + "address": "ATHYUiLRifNafWrFLAVgQYo9dVxntZkow9", + "passphrase": "decline cruel narrow define east tragic border party alter daughter awkward fever", + "keys": { + "publicKey": "0384825d94953e85e3b6c3dc5ee27d363fec7c6dee2c1baa83dd6760458e05a402", + "privateKey": "6cf64590e90735f9b64b5a2835543a8c9f83d7cd6980916f9a9bb91b88d3eb37", + "compressed": true + }, + "secondPassphrase": "cruel timber into found inquiry sweet later panel pear pulp valid estate" + }, + { + "address": "ALsRw7zX6PeYFVqHpJYeVoeBy3a5mjXCoL", + "passphrase": "possible venue tube number reject embark document profit subway response today asthma", + "keys": { + "publicKey": "023244209672266ac90bf64d0fbd9903028c2483b33f3cf1a0de41822a3628c749", + "privateKey": "5ba085aaeaad58a4fd104eca2e145a7ac929871b6dd91127703392c826555ea8", + "compressed": true + }, + "secondPassphrase": "impose wheat equip security crash fan lonely gap flavor diagram steel cactus" + }, + { + "address": "AMGsRHFRL8V6qbUz115q1Wc8pnXBScuS84", + "passphrase": "heavy town train until brother happy country magnet fuel useless fade wave", + "keys": { + "publicKey": "037f4d9190cfa3c916820a3d410010226c399f6b4278eb07ecf22d41904c1d1217", + "privateKey": "593a8b9d777a3f40c46f328f003fa6d780c1b892c849f0356014b5286bc1c319", + "compressed": true + }, + "secondPassphrase": "either fancy virtual juice student churn taxi pink magnet busy slim bundle" + }, + { + "address": "ANLm1RbdAqNcM5PMBS99ARjHrvBBfTykZM", + "passphrase": "observe solar remind tattoo coffee parent law holiday language risk field arctic", + "keys": { + "publicKey": "03fcec41228c64a39dffbbe8466b23bc1db40efb1970e32de0360cc828ba953883", + "privateKey": "0700859b78efa8d621326f0b36d6371885f7b828405081db2513081ad26174a7", + "compressed": true + }, + "secondPassphrase": "cancel beauty raccoon cost ankle action wagon sing green volcano bleak armed" + }, + { + "address": "AK8aUnLmspWtccNwDEciSBf2BoxmpRPAow", + "passphrase": "solar appear resist basket laptop area try black use retire vehicle slush", + "keys": { + "publicKey": "02f238b9dd9e43216c1e63390eb50adf9519d00c00541c29cce98f12ed3bab55fa", + "privateKey": "07f850e9e3ded8273f0cbcbce1c4e2aae26bc3e46f614b8c5c7e45fec1eca266", + "compressed": true + }, + "secondPassphrase": "liar road live torch apology adapt diagram sure dust pilot blast panda" + }, + { + "address": "AGJUdoyjXsfQNsDa1G2x3smFfdJEPB8kyn", + "passphrase": "casino ketchup evil outside maple length coffee usage mule october climb genre", + "keys": { + "publicKey": "02b4e3968198a714e9a2c81adfde1211a24fea24c578a7033312429a693a4e93c7", + "privateKey": "af88a0e3572e356d58f1a55b8e8c65071e84ce44407b53493041e483e04c4778", + "compressed": true + }, + "secondPassphrase": "throw special true fee decline idea secret okay kidney bid safe any" + }, + { + "address": "Adm62zHj5cUuJQKsbrz467exSVVgk1bH5j", + "passphrase": "possible reveal mention flip view announce laptop mother mouse panda pizza primary", + "keys": { + "publicKey": "033401817bf6e94157e8c6ee248e39037d28e0b1fdb1cfab726b06220323e0f29e", + "privateKey": "6c72041d0a61cbd8ad3371a6b1461e088f67f958100e8546e288b82370ca0d97", + "compressed": true + }, + "secondPassphrase": "foster connect lens lobster uphold comic before daughter prepare correct describe render" + }, + { + "address": "AR7wLy5nU2kML3Wv9Fdot2RdHAZXn7ftTL", + "passphrase": "churn item tongue scheme palm unhappy pupil cloud insect party until sand", + "keys": { + "publicKey": "02c48118c725c269653cd46b2836ab1c2d8a89ad7ba214b0b309eec5174b7f590b", + "privateKey": "b9fbee46b88f9f0e491229fde3fa1058edc70ed91e1bd25192e6f4d2571d76ee", + "compressed": true + }, + "secondPassphrase": "pen cliff rug yellow grant discover crunch glimpse vacuum shuffle rebuild dawn" + }, + { + "address": "AKTZGxPzLsr3jeaFbBRFJShzrBUpEZHbBA", + "passphrase": "arrow found empty airport adult knock breeze humble pencil index cover gate", + "keys": { + "publicKey": "0294f19b7b5e338dc911810257a89277a487f35b0dc146fa78c2015d2489ba0db3", + "privateKey": "514cdcf5ac64650bdcc2f6d1357fa719bc52695d3d3ce36f729b53d4aed1785d", + "compressed": true + }, + "secondPassphrase": "brain staff bench lecture deposit erode popular fluid sing core promote ask" + }, + { + "address": "AbJyaYJYZYS4uDwAy7sfNTTcjT1wUnVCew", + "passphrase": "apology legal payment easily sail twice tilt dawn elite false tide chimney", + "keys": { + "publicKey": "03a7d7272168c98037e9c3c01f5b4b6a53eb88efb06057bf82785bddae0ade39c5", + "privateKey": "9e6ccda2a459e56e9449fa5384260a15aba1d2f82be9193d775a05d310715bbf", + "compressed": true + }, + "secondPassphrase": "broken chat quarter amateur among lesson country tent educate rose prepare jacket" + }, + { + "address": "AX9HUjeUfgZieGsbpkKmUBJLqvZiC7N2aL", + "passphrase": "beach sample feel ripple category pause surge extra science predict gospel next", + "keys": { + "publicKey": "020c8b7e3c16f5a1819e047f4be56f7a95b23b1eeac9078f3917016c6c1c22ecf0", + "privateKey": "0f730832a83d11be42c4df6289daadd03ca637b68f365a3d5c18eb8a00f9f94c", + "compressed": true + }, + "secondPassphrase": "keep detect metal security churn young else bird private school grain various" + }, + { + "address": "AK9Adi5y1xEftjbr1WVyRCqUuptKsg7xGj", + "passphrase": "only staff bronze vital connect fold vapor october convince negative clinic alter", + "keys": { + "publicKey": "0363a68f9cf32ad3d323f4f09cbe883bbb45c8e419d0e54e519a665b135d9be7d9", + "privateKey": "ef10c5c6f8ba44e831775f6e3f9c80052495b9198d9230dd98ffde69e3d503e5", + "compressed": true + }, + "secondPassphrase": "negative swap humble boy term grocery sad venue hen despair rhythm poet" + }, + { + "address": "AVxjxrm3bnGSf4yo62f4bbLBWFwAeNhvTq", + "passphrase": "picnic scene donor endorse toss box fortune liberty honey tourist tree pilot", + "keys": { + "publicKey": "03f4a9e1c98fd828d93e96ecc133187174c5d91566d09fa2701fbed36ac70fe445", + "privateKey": "237734222bdcde88d2e9b0f4f9cb6cf168383e1bd71a328c65506338f403e4e2", + "compressed": true + }, + "secondPassphrase": "have sister rich hundred moon turkey sister easily asset apology math undo" + }, + { + "address": "AV4dc3U6awxGiRwxSEY4cgNSwNReBqeKFj", + "passphrase": "strategy glance soon measure street ride budget goddess decline onion uphold plug", + "keys": { + "publicKey": "02dec8953912902e1ea7ca3c6d994b99dc0156165810656d1863e0a239a7ff2f72", + "privateKey": "6f95161f74ca431d740faedfc96a374a7979274d935d65daa8a1a27d42e6a01e", + "compressed": true + }, + "secondPassphrase": "city coyote expect hedgehog tape typical snow endless design deal three library" + }, + { + "address": "AH9qyvot8HnjcVCvz3s3vCZ3rgg7qumnj6", + "passphrase": "matrix actress snow spoil cancel ritual lab moment abuse pulse glow twenty", + "keys": { + "publicKey": "02a146b249c588574ff70a2f40ccfd27b6cc324209f8858a8f7b5063930d869756", + "privateKey": "8ace79960f58572841bd126487e73a8129ebd9a925c3f18d109ef405b379b921", + "compressed": true + }, + "secondPassphrase": "suggest renew awesome arctic antenna poet doll drama surprise horse six female" + }, + { + "address": "ASTDtbrw1i53dbMyaTr6WT4XEbTv81e7pi", + "passphrase": "slight width green joy leg blame welcome loan label buddy matter aware", + "keys": { + "publicKey": "03f453fe29be65f3ae31b5c68554e777cf0ae6422fe48e598784adc293e0d1faad", + "privateKey": "acc1f8e8f6e9fb7237161c2a85d6577f9d4267268ddfc97c6975a111e3a2447c", + "compressed": true + }, + "secondPassphrase": "cart glory raise usage cave want since distance orchard client shield boss" + }, + { + "address": "AeQwXXZEfMkp2zB9pUJzM85wn52EKX7cE8", + "passphrase": "average screen inflict loyal tower cluster slot divide grocery accident jungle member", + "keys": { + "publicKey": "031639a30d2a92746da86629be2fa190cff2ba2871a4fbbee4badc9bfd466a0753", + "privateKey": "27c387feeabe8d67bc6823190fc28f519cb30c4261b480e9822d671378e7c83e", + "compressed": true + }, + "secondPassphrase": "smooth black measure sing debate horror response behind bread flag memory outdoor" + }, + { + "address": "AWRDMAnZgZp5QEKkQ8fH8E26wtTgwoNUe2", + "passphrase": "mirror proud trust net suffer pig goat any gap rule bike fetch", + "keys": { + "publicKey": "025acf14ffa2ec86954c252784cd6e3931bbffa5ab8a490afb36ea250c01b72694", + "privateKey": "a5b70f189f5f2a757ffac889daa63be93a9891ca25a724118908c7c58c085bfa", + "compressed": true + }, + "secondPassphrase": "notice essay price give hair narrow carry industry fine space punch wink" + }, + { + "address": "AHo8ciM7i3ro99RUcZMPV4Ytb5Esq8Xs1E", + "passphrase": "copy hospital aunt wrap nature allow menu puppy balcony raven tray quiz", + "keys": { + "publicKey": "02aaafe40f4c7b541084802f4fc3d6ffe8e26061478898c3589d449e83de80dc91", + "privateKey": "a491bbabc16024ed1bf55a63b156a0cdda6c793294fd458f74f00e865146e26a", + "compressed": true + }, + "secondPassphrase": "today behind regular future invite middle potato result liberty bright sense hen" + }, + { + "address": "AV184d6W1B171mb4KkXbmM5nhRQYf9qSYJ", + "passphrase": "antenna guitar vendor manual chat bleak candy found gun square shield cup", + "keys": { + "publicKey": "02fb92a875f324b45c5168b0f19c4cd9f23041861640cf13abf07c8919e4754c31", + "privateKey": "57191878ebf763da12ea3b2d130836b083da53e1370751ecb348b09e769999a2", + "compressed": true + }, + "secondPassphrase": "recipe vessel fury system agent census night copy raccoon brave universe monster" + }, + { + "address": "AHpqfxf3EA6L4383nw2rq9i5GqoNJadjGD", + "passphrase": "stay such dutch reopen scale hover notable myth spend evidence sister cactus", + "keys": { + "publicKey": "03b2d0bc348b254f8bf98612e56af68b52ff84bf0daf35690b4d60455911092efb", + "privateKey": "46f8fd4cb95ceeaeb5136b801ed0235d9d416eb05669a38bf2a7d3966030d9b9", + "compressed": true + }, + "secondPassphrase": "kid depth blouse abuse toilet steak arena hire unaware immense clip surge" + }, + { + "address": "AUdygMQuvJQ1zBgiEF7EmbisnVN7AM5Aeg", + "passphrase": "abuse cube arrive season insane worth price remind spend thunder risk that", + "keys": { + "publicKey": "033425c8ebeda418aa1f0d2b7400923d53168e921a8be8ebd3c5584e793c17b038", + "privateKey": "2b46ce42cc6b41c903971691dcbea7879eaf085242a743375ea50b5636d19a81", + "compressed": true + }, + "secondPassphrase": "critic glimpse feed vendor dentist shuffle fluid ticket world scene tragic boy" + }, + { + "address": "ANyRSjSGgb5Lf2bxFkdJG1DRXX5C2GLRwW", + "passphrase": "forest outdoor into weather december royal virus trash loan weather venue exchange", + "keys": { + "publicKey": "021b9a39a3281e7f4402021450f561b439fb2faa3b16a15c87b92fcfc5e4aac15c", + "privateKey": "c2b4af8fc93bcfc861d14672214db6802a4e8d7e4e443ea35d45d0d8c4b0b286", + "compressed": true + }, + "secondPassphrase": "just cup exile harbor pipe wave mutual clown vacant humor involve calm" + }, + { + "address": "Aefdn5H3hQHMxfFAYjTXUzbn43HCi13zbF", + "passphrase": "load foot liar season cabbage photo bridge furnace cup purpose limit recycle", + "keys": { + "publicKey": "02d3e51bb756f2522139daccd06aacba36982536338210ac6f43b3dacaf22cde48", + "privateKey": "0dead692706572b58672041f9a80cfd5ff32989f01d3f2477318fae4db1bc2c0", + "compressed": true + }, + "secondPassphrase": "estate column panther enact script impulse pretty crawl clump bitter rough hen" + }, + { + "address": "AKRMzUmHeuHA6nRjW54FLcDFwyg6f4erVK", + "passphrase": "whale culture valve switch either grit enter learn jealous ostrich bench loan", + "keys": { + "publicKey": "03f40978e58affc1bb5ae6746e1c481f6e4f2093b1fe4f627f33b2b677fc8b82a2", + "privateKey": "64dfe2593fbc1d05e919f06d406a5c252844112f62dc12a3caa4d17249a3cb73", + "compressed": true + }, + "secondPassphrase": "winner error habit gather journey voyage spring force spider float cool release" + }, + { + "address": "ASdb1P1eikFmmZuywwSKcJ4iFa7LNBZdjC", + "passphrase": "fish detect earn oblige around fabric camp measure mistake input car woman", + "keys": { + "publicKey": "037a5ab54742b6088fec6c487c37d961de076999eeaa462a9b25fadd131a93a75f", + "privateKey": "96777fef5c41a2b0d9791bc9307e1131b29dac1fa9fa459ef6920c346b14c627", + "compressed": true + }, + "secondPassphrase": "tiny spare program pair drift isolate area same marriage canvas follow arrest" + }, + { + "address": "ARF2AUrWPyEMSY7w6SCqg6jeGgi6QxBUTT", + "passphrase": "tomato hair female punch sadness primary load helmet enforce steel fire tonight", + "keys": { + "publicKey": "03f30cdb63b466e72751b34ebc8f98c68dfcbfd842486128f98b81e8e54c14cf47", + "privateKey": "40c08f8bfcdaedb6ea34ca3ab65e0dd227c5c5552191fad1eea1a8b7952a0e36", + "compressed": true + }, + "secondPassphrase": "either above sphere spoon lonely magnet romance indicate paddle label awake economy" + }, + { + "address": "AK4MTqy8SoHsrATVDEkVDKTVDX12XcJzjo", + "passphrase": "two envelope also list father salad pluck polar wing square danger audit", + "keys": { + "publicKey": "036c1748887645da79305f6b62da3df56725355538b56a3072ddeb32ee228c016e", + "privateKey": "bf0247e699cff93e3e475017fc8398c5305278b286a7dbd0b248e56b25702492", + "compressed": true + }, + "secondPassphrase": "inside just glue obtain tonight exhibit mirror ball market vibrant addict salad" + }, + { + "address": "Ab3oWYGX4mMuXsNtE6MXJoPafHG1EbVkS2", + "passphrase": "web burst pepper length vital noise bird theory income involve length fever", + "keys": { + "publicKey": "032994c3c42dbe940d4e1aa91beaff6b6746e917cbe1709a36def3389095afbd4a", + "privateKey": "c7cff076246899be419f1e24e0f734e7aae97581027be40fe07a31341dbad2e9", + "compressed": true + }, + "secondPassphrase": "math pig mass tobacco detect oak ten act illness amount ignore divide" + }, + { + "address": "AQiMw9hxzdss2js4HN2L1jeEio8Pdpd9yB", + "passphrase": "obscure leopard manual poverty attitude dad exhaust board fault arctic merry fault", + "keys": { + "publicKey": "02072106d6946a9de07cdfe30d70683ffcc7bae860fb29025fb8398b4de0e27f3e", + "privateKey": "f4c397bac5d2775672f87d68f99b47fa94c1c5f387ef7173d2433206fa3af651", + "compressed": true + }, + "secondPassphrase": "joke dynamic used chronic mutual depth bachelor more patch combine fancy ceiling" + }, + { + "address": "ARzo499fWcgSDomQdquwxt9DzcdkMYDQvw", + "passphrase": "old clown link control disorder cycle cave melt picnic easy strong rescue", + "keys": { + "publicKey": "03f9dd1b5a26d9c9919909ed33c3d832734c18e3bd876b24f0a74c96d35d2937d0", + "privateKey": "9d5a777634e6b8755a7f2b7396103d051f07135ccb0221ddc44dc03fb4d4d30d", + "compressed": true + }, + "secondPassphrase": "forget happy viable useless scan vital oven collect fine fame pride thunder" + }, + { + "address": "AZHEyQWc4p15fKRhaK9zmV7gc3mAVa6AF1", + "passphrase": "aerobic tilt wheel boss gas deer claw truly urge phrase casino lock", + "keys": { + "publicKey": "03167b1b5a15097c60d4510425c8cb9ba440f2d48cbedf184a66e40ef5adde1400", + "privateKey": "9a678b1865e877a139df2e1d1395c301269437391db77b85c3cc7e2f43ac6ac8", + "compressed": true + }, + "secondPassphrase": "reopen wet bubble display animal game general town artefact firm parrot ethics" + }, + { + "address": "AFnYJTirDrhEmadpc6pqq1sE55RreeiB8P", + "passphrase": "void interest innocent token harsh report involve outside devote mother strong market", + "keys": { + "publicKey": "03f8a36f6c492c081bbe1943764d962eee5dbae0b1b06b299751380a979c620e54", + "privateKey": "cf5859b8328074fad8c12e6d50c55e1a141623f4bfce49fd051b8ea5fb522671", + "compressed": true + }, + "secondPassphrase": "day course pretty cabin differ man kangaroo age true forget way pluck" + }, + { + "address": "AST3nbdEcDc4uVWzDe9hpgZHJxCJxmmysY", + "passphrase": "one mammal retire elder wool slide define glance muscle wall tag choice", + "keys": { + "publicKey": "03bf66b51d43f9b373635a04dd785882d5b920463b845127dc50dc61323b6899fa", + "privateKey": "f3caad3a3c9f82e314e58ce740c3c4a5f7fb6e43142810c2043c9d2b213d06df", + "compressed": true + }, + "secondPassphrase": "travel again zone hidden gown when quit frog agree range kitten nothing" + }, + { + "address": "AQRSAa6sFA9oCd1XV6ZbNg5KJwEiFaJH21", + "passphrase": "modify click syrup tackle patch latin caution powder hour tomato devote surprise", + "keys": { + "publicKey": "026f5418fc21d9300c51d4294c2482217db2f756b79899abfd68c57bb8cb7e6d33", + "privateKey": "65ea381bb986c31a5368cbb2ba095b63edbdd800d25d5c47c78e36af2932a446", + "compressed": true + }, + "secondPassphrase": "physical police flavor spice social hurdle wreck furnace gravity sugar input obvious" + }, + { + "address": "ANzBg5Kw43X2CSQgaBeHHubzw1swY25Frz", + "passphrase": "hollow clown avoid rookie wish hobby leaf stove endorse play pudding web", + "keys": { + "publicKey": "02e9725fb335e3e4a0163a43292428d696a71aa3e02a5d3bf1c400263360b13aa5", + "privateKey": "0877a6a91a647884acf4b80cd809eee72656165496d6b5193f3a2fd82d57d5ee", + "compressed": true + }, + "secondPassphrase": "length embrace brief goat this other worth woman load vote click skull" + }, + { + "address": "AMzFowdfLaxLKNThawWEq3QyvDP2SfUv8b", + "passphrase": "hazard shaft drum half chief video buzz pyramid vibrant weasel wolf barrel", + "keys": { + "publicKey": "0230dd82d88f144160176270f09ebe50f365d94837563ab7c47630759b45cb4121", + "privateKey": "319cf6116ddbd6657a8f2fbfbaab4d2aafd38ebb01fac8b85c3961ad2b265809", + "compressed": true + }, + "secondPassphrase": "already noise inherit gold slice father camera age era fox flash soft" + }, + { + "address": "AFrWisLirag5Upjiik6rnFVLptCpaE9yZj", + "passphrase": "consider tongue liquid thought early humble mechanic omit gesture remove exit decade", + "keys": { + "publicKey": "022134c050b767b87991bdb17582302fa1ab6ae0549b0ff36fe1259cc348084e68", + "privateKey": "337892843aabab24cd38d37c99cf9df27c904f11d82f05191c37075078ec8f21", + "compressed": true + }, + "secondPassphrase": "summer eight hold fiscal rug grass type stove price find physical feel" + }, + { + "address": "ALtiFFMTVY7uL7hXUpAa2NiCcqUfAKkYqZ", + "passphrase": "adjust develop riot behave great kind bench old retire room street canyon", + "keys": { + "publicKey": "02e1714b7bf5c6101368bb47e68e8904b93ed5dd9e358eadee4bef89e1e25e5c76", + "privateKey": "701749b8bf87bafbb1a2b38a9ba9a2e897de3dfb58dc7f0a4ac58c4f8e5205aa", + "compressed": true + }, + "secondPassphrase": "dish script leave fatigue hand nurse ghost warm lens hurt service magic" + }, + { + "address": "AMWaGRkxUMc7EnZVRGKxuihYjQxkXJTGv5", + "passphrase": "inside cage bless install final define curtain gym anger outer connect shrimp", + "keys": { + "publicKey": "032cb55adb5adc85a666b10b101e8ec595bcaa4993f3ce9119375f35ddad6c5104", + "privateKey": "0740a2067493593e1bdd6f87d5480eeb4646d45f712ae4a41ee5ed78d79d7886", + "compressed": true + }, + "secondPassphrase": "when arch pig copper siren country coach another flip solar bargain novel" + }, + { + "address": "AQuppbFyZJoY5D7H1vd2bdJdtG86jHhiHL", + "passphrase": "juice roast current lend entry anchor recycle matter genius pretty ball bottom", + "keys": { + "publicKey": "02b717b22da94ce06e2b6f76b20df13cea230fa0c3f411dc45ea98b9a2e2674623", + "privateKey": "f45117a85a432058ddfd1edc8401d760ed7fb21f23839f5f5167a5eb100c48d0", + "compressed": true + }, + "secondPassphrase": "ladder cook mercy insane sudden flower day derive catalog tuna zone rifle" + }, + { + "address": "AZm1iZfCLyqAqnWMFhekpTeh6XAspxCUYY", + "passphrase": "interest roast biology kick chaos learn manage size subject amazing tube chest", + "keys": { + "publicKey": "026039e8cb61e6050ea0ef22706eecf6f880fcf7581bafda135f0d496ed88e2bbe", + "privateKey": "6261a9e9a7d43fa8429c3fb9db1237b2dad8ddd5d7194efa557697457e28147a", + "compressed": true + }, + "secondPassphrase": "equip bottom educate night task vivid dentist poet today uncover vessel cry" + }, + { + "address": "AYzQYGbwgcmrCgZM3xx5Mv7E9j2TEcAhAn", + "passphrase": "lunar lazy key blind execute property year unlock favorite group dog gain", + "keys": { + "publicKey": "032813004bc61bb9125232369724c23f5d0c417f1690f0beefdae359694ed49eaf", + "privateKey": "51a32489d8453759f3aec215ac212ace0793f175bd5974dbdb385463ab374d52", + "compressed": true + }, + "secondPassphrase": "chimney divorce almost forward draw jaguar vendor clap boy obtain wasp job" + } + ] +} diff --git a/packages/core-test-utils/src/fixtures/index.ts b/packages/core-test-utils/src/fixtures/index.ts new file mode 100644 index 0000000000..aec9912184 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/index.ts @@ -0,0 +1 @@ +export * from "./testnet"; // export testnet by default, if we want a different one we import from its path diff --git a/packages/core-test-utils/src/fixtures/testnet/blocks101to155.ts b/packages/core-test-utils/src/fixtures/testnet/blocks101to155.ts new file mode 100644 index 0000000000..708d0ec7d9 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/testnet/blocks101to155.ts @@ -0,0 +1,938 @@ +/* tslint:disable */ +export const blocks101to155 = [ + { + id: "16380709717848284005", + version: 0, + timestamp: 46584522, + height: 101, + reward: "0", + previousBlock: "6161515163793239359", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + blockSignature: + "304402204d1e50daa970881aa600a19e4f785fe3f74ffedede6134889c86bb7df5f3103a02206e623a242ffb5297ae09185f1f46cbcc9e49b0324bc0aac0bb30e4b0dff7b20f", + createdAt: "2018-09-11T17:08:42.241Z", + }, + { + id: "15490212522027991751", + version: 0, + timestamp: 46584530, + height: 102, + reward: "0", + previousBlock: "16380709717848284005", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + blockSignature: + "304402204ca739a7c99d035d01cb27a18e2989110196490f4d2e17293a6bc5f9d5240afd02200e49f9901aa1dc129866b98290958baf0fdbca41f3c9114f864228089859dffd", + createdAt: "2018-09-11T17:08:50.564Z", + }, + { + id: "7619316577889665171", + version: 0, + timestamp: 46584538, + height: 103, + reward: "0", + previousBlock: "15490212522027991751", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + blockSignature: + "3045022100cab8276fe2cebafefb6fcd0f895b9cadf5e38829d14ea8e0f1b1365e9481dc7202205395c196242699efd143548d9cf783607e7e4772bf572c3564ab8bb7c0a3e7cd", + createdAt: "2018-09-11T17:08:58.400Z", + }, + { + id: "14306710738176000705", + version: 0, + timestamp: 46584546, + height: 104, + reward: "0", + previousBlock: "7619316577889665171", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + blockSignature: + "304402202702687d7607151039ebddeb69715ec72effe5458a7f02004ee84124c8482b480220625a2d2fd5214d820464f960240e3f98b45174cab5c2d043025c1d61509f8e38", + createdAt: "2018-09-11T17:09:06.414Z", + }, + { + id: "16285816300440069381", + version: 0, + timestamp: 46584554, + height: 105, + reward: "0", + previousBlock: "14306710738176000705", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + blockSignature: + "304402202b45aaa43e3b0801389f4ed4bca834ef83d7f1160a25d7063bb5434fa07a1384022054e14371cfcb448dd46cecd84981b33a35a9aca892f4aab4c8495c6ffbbaeaf6", + createdAt: "2018-09-11T17:09:14.346Z", + }, + { + id: "16505099800747927529", + version: 0, + timestamp: 46584562, + height: 106, + reward: "0", + previousBlock: "16285816300440069381", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + blockSignature: + "3045022100b1d9bd1a77eb5d9ddd94a45b5f83168438d6f09ab7da068eeaee457aed79e6c502207a5423a3c7e3cae99d4b656e7c62ac2672030fde9ccd618c3ccc2388c203bdcd", + createdAt: "2018-09-11T17:09:22.407Z", + }, + { + id: "16506832204032304009", + version: 0, + timestamp: 46584570, + height: 107, + reward: "0", + previousBlock: "16505099800747927529", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + blockSignature: + "3045022100f25903940fae22d28d95c7232d5220ef5ce599abdd773bea1b6942e1c331f0b50220726a691c87a99645fc3eb756a3914aa2b0cd36cf48c48f76f2f266132e3d3dff", + createdAt: "2018-09-11T17:09:30.531Z", + }, + { + id: "9467089070361350584", + version: 0, + timestamp: 46584578, + height: 108, + reward: "0", + previousBlock: "16506832204032304009", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + blockSignature: + "304402205d1b9fe34514316e70682743dd35e328d3b8424747e3951b93f6c7373442b67c022064fce5e5569a7a7a8a4cb7f914c5eb217bd71e9254a5934f05802705d3b0205f", + createdAt: "2018-09-11T17:09:38.454Z", + }, + { + id: "8391119528927829411", + version: 0, + timestamp: 46584586, + height: 109, + reward: "0", + previousBlock: "9467089070361350584", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + blockSignature: + "30440220617749a2d215693ac015b19285cda89ac7b9c5d383f2a416d2e975d66084dbb5022058baac9686c5096e7f77172d3e66f036bfd43564c8388365437f874efd68f146", + createdAt: "2018-09-11T17:09:46.375Z", + }, + { + id: "16820242876782994066", + version: 0, + timestamp: 46584594, + height: 110, + reward: "0", + previousBlock: "8391119528927829411", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + blockSignature: + "304402206c0a3dd633fd14dfafaa3b4b35d196485dc0b0c9ef8006ed5ae412510170d28a02203b0caef052c8c0b25fae1af1661bb06c2a212dfabaf87a6689528e897b0938a5", + createdAt: "2018-09-11T17:09:54.417Z", + }, + { + id: "17811063050605622774", + version: 0, + timestamp: 46584602, + height: 111, + reward: "0", + previousBlock: "16820242876782994066", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + blockSignature: + "304502210091c76111ecfb88d30071d4c850a63899cfebfb0ab03d3dd9e2b14bf921fd444e02204c48b0a02acb3fdf9d76d6ede553c1b98580145135801e27c803b38fce79de95", + createdAt: "2018-09-11T17:10:02.366Z", + }, + { + id: "17705208927947214042", + version: 0, + timestamp: 46584610, + height: 112, + reward: "0", + previousBlock: "17811063050605622774", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + blockSignature: + "3044022066f31a536c2bc098c6ee7f74591b6564cd619d2f00734b2a5a5491cde33f9a740220085c9e8a5b1457215f33a23d4b94f671c1f0f5f8127682b5713d1a44633ab598", + createdAt: "2018-09-11T17:10:10.335Z", + }, + { + id: "3794170631479398111", + version: 0, + timestamp: 46584618, + height: 113, + reward: "0", + previousBlock: "17705208927947214042", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + blockSignature: + "304402204ccdf577d8cbec4ef62ce2ef68752915de0344093e27057ab05895aa8871e73a02202f27f5d537e0a21911e574c21bc1b80936ea98a5d0b49c693dd42fec41f6401f", + createdAt: "2018-09-11T17:10:18.285Z", + }, + { + id: "2000919467344312301", + version: 0, + timestamp: 46584626, + height: 114, + reward: "0", + previousBlock: "3794170631479398111", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + blockSignature: + "30440220479043956e097c3bafad3529b8335eadf295e0c9fd71e2a9ab04c151247743cd02201ebf345c07fc9c7be440ed0989a84b841a2bdaa032c2adee34534be2a67d6ac8", + createdAt: "2018-09-11T17:10:26.304Z", + }, + { + id: "17040727982508304752", + version: 0, + timestamp: 46584634, + height: 115, + reward: "0", + previousBlock: "2000919467344312301", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + blockSignature: + "304402202fa24bd38c39d4884fee63b739b1650358804a1fc703290133c5cc6b6269c39f0220105daf68cb3745f788c01cbd9d3e365e8ed9168e73974d168ae4968689ccd813", + createdAt: "2018-09-11T17:10:34.422Z", + }, + { + id: "9164793200684835761", + version: 0, + timestamp: 46584642, + height: 116, + reward: "0", + previousBlock: "17040727982508304752", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + blockSignature: + "3045022100e5b85d062e3fb760fb05e45ae32f8bdca3cf44541f3b2609011e99a5dd39595d022069af2fd27969ff7ade169d59e2384e10d59f316c3660beb7417e83bf5544e2be", + createdAt: "2018-09-11T17:10:42.265Z", + }, + { + id: "9702240278422456718", + version: 0, + timestamp: 46584650, + height: 117, + reward: "0", + previousBlock: "9164793200684835761", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + blockSignature: + "3045022100e3aec30648fb8443d1330d8934c116b7e063885e7ce4589b3a9a356b9788efd9022050f042f95dd7462bf6a01cf1c08193903f08c6fd0ba3b8b109a5f94ca2f044ee", + createdAt: "2018-09-11T17:10:50.387Z", + }, + { + id: "2372729513640015242", + version: 0, + timestamp: 46584658, + height: 118, + reward: "0", + previousBlock: "9702240278422456718", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + blockSignature: + "3045022100ecb8acd0440f7e7f4b4220c4e3e6a70d61c4e04d05a87642767f466facb6250302207707505aaa1863ec12966b183b421688e5d5195b51b21b2979246796b249b5b0", + createdAt: "2018-09-11T17:10:58.441Z", + }, + { + id: "3675127542764096771", + version: 0, + timestamp: 46584666, + height: 119, + reward: "0", + previousBlock: "2372729513640015242", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + blockSignature: + "30440220406326a74f91b99f9a3979de11e29ebd39df74e20d0dc5bcd666a9720b9121af02207103c201be49d9b93bdf286679fbc852410df35d3e1242b53d4115645f107914", + createdAt: "2018-09-11T17:11:06.309Z", + }, + { + id: "5358667162203289341", + version: 0, + timestamp: 46584674, + height: 120, + reward: "0", + previousBlock: "3675127542764096771", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + blockSignature: + "3045022100f1aa88f80f0ca725337174df11b6c38d1019c6920154c7884fcc2d853f7c2b6f02206ff94a9cfc11455e8a1d277ea3f705505fbaded49b3a53a297b062b4eeda670e", + createdAt: "2018-09-11T17:11:14.247Z", + }, + { + id: "18004288728431909564", + version: 0, + timestamp: 46584682, + height: 121, + reward: "0", + previousBlock: "5358667162203289341", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + blockSignature: + "304402200f52bafe85aa27cceac9de07660146a417745b3764ef3d8edc5566efd2a2137602200d589d6af456297a6c1e0af569f60c7c87bc33ca788fca0afc7aeaf5b937b42c", + createdAt: "2018-09-11T17:11:22.473Z", + }, + { + id: "6595564574956816872", + version: 0, + timestamp: 46584690, + height: 122, + reward: "0", + previousBlock: "18004288728431909564", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + blockSignature: + "3045022100eaf2e2a97742659b160ff43d5fecd82f8de7ce8e498d6692189a45225484e47a022011749a641345e689a26905037abe4cb19bd5cc1ead4b1d962a5df139f26c2d7c", + createdAt: "2018-09-11T17:11:30.413Z", + }, + { + id: "10066431236273363611", + version: 0, + timestamp: 46584698, + height: 123, + reward: "0", + previousBlock: "6595564574956816872", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + blockSignature: + "3044022027996c930ee580299cf4e506d913ee97ec3e33efe6a5311222a8e173cd8576b5022024601fd7cdbb0e0e6c34283a04dd66c11d4dd55518bf72f254d8bb5e0e808495", + createdAt: "2018-09-11T17:11:38.340Z", + }, + { + id: "4889016410282212954", + version: 0, + timestamp: 46584706, + height: 124, + reward: "0", + previousBlock: "10066431236273363611", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + blockSignature: + "3045022100cbb02b436b77d7d52e559b9e38e514188327fe6fd2a33285a6e46c0244b5adca02201cd4a6460a7fb29326625d68b31b6067de4739176066056fcd15339948c7847e", + createdAt: "2018-09-11T17:11:46.316Z", + }, + { + id: "1479697744155789665", + version: 0, + timestamp: 46584714, + height: 125, + reward: "0", + previousBlock: "4889016410282212954", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + blockSignature: + "3045022100caae762a80796c84e0d58e137b01494fe3520721b306917a3cfcd3bc6ddd19a5022026a294ad3d974a9b4e9eeae88967a79432ca559ca8f0273fd245469554e9de8e", + createdAt: "2018-09-11T17:11:54.512Z", + }, + { + id: "3211613713464451417", + version: 0, + timestamp: 46584722, + height: 126, + reward: "0", + previousBlock: "1479697744155789665", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + blockSignature: + "304402202d1b567d6c0141dfb2d7a55eadca8077be71337f351a4525f2ccf7a06314874a02204db6782f98f2827a7bad420a40b2a4bd00a7713e8f02a8b407d8f11bb32aee44", + createdAt: "2018-09-11T17:12:02.313Z", + }, + { + id: "14953374716924761457", + version: 0, + timestamp: 46584730, + height: 127, + reward: "0", + previousBlock: "3211613713464451417", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + blockSignature: + "3045022100efca475f4241c545a6e3f59547e0b4843999468ef803e940d00accc84886df5a022035536116d39f2ca3f0651cdc97b6304e66cef55c20dc07e5d3620fce85ece5fe", + createdAt: "2018-09-11T17:12:10.410Z", + }, + { + id: "3086909897235569982", + version: 0, + timestamp: 46584738, + height: 128, + reward: "0", + previousBlock: "14953374716924761457", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + blockSignature: + "3045022100a7d81f46a37cda7f99da390156371e7acdec498de689cc22395abef28285450f022001d1c2aabb1023b12074172d60c1c86deb7b324c3bf29e8c8c74a1597006c2d9", + createdAt: "2018-09-11T17:12:18.323Z", + }, + { + id: "11858031216720080832", + version: 0, + timestamp: 46584746, + height: 129, + reward: "0", + previousBlock: "3086909897235569982", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + blockSignature: + "3045022100a46abcfe70ca5e95daee7fc4120e75009501e15b7f63cac47e30aac256f2c4d102201c8c0b6e7f7f86459dfcde1fa58b7ee7503bace7a9d3681dfb169180b93c67d5", + createdAt: "2018-09-11T17:12:26.281Z", + }, + { + id: "10670801688829958309", + version: 0, + timestamp: 46584754, + height: 130, + reward: "0", + previousBlock: "11858031216720080832", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + blockSignature: + "3045022100d6c6fa51b3394aeac44552369cf4f26331435dc839c8ba7690ae551f55edf05102207bbbef4210e7b27c760957d2d72515e5561bb7478d1f6fba0ab5c616d5beb95c", + createdAt: "2018-09-11T17:12:34.345Z", + }, + { + id: "17447880863052605705", + version: 0, + timestamp: 46584762, + height: 131, + reward: "0", + previousBlock: "10670801688829958309", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + blockSignature: + "304402202df02cdcedadb9c34c5303ccb1ec572774a37d6649d869641499831c4455658e02200a92d9d63e6b8c3dfc6c97ca536cadb5d9100e0f834ba4bb252ecc1dda404b78", + createdAt: "2018-09-11T17:12:42.409Z", + }, + { + id: "14743301194148498313", + version: 0, + timestamp: 46584770, + height: 132, + reward: "0", + previousBlock: "17447880863052605705", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + blockSignature: + "3045022100f68768e22ec1b31f693ee723218cf382bd552903ea81d0ce5263582785039dab02201e89eb48a668b34c96d08f34ce84aa171aee87f860f4303b80c0bbeac2cdfe43", + createdAt: "2018-09-11T17:12:50.302Z", + }, + { + id: "16518342502287211733", + version: 0, + timestamp: 46584778, + height: 133, + reward: "0", + previousBlock: "14743301194148498313", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + blockSignature: + "3045022100f0732f4d73a509abe0fcf7ad950bed566a6387b51f77c3013647acd7058e5343022063ebff98016117241ddaa962eb17dd1ead55dd13323cb89bfbcd35294b5086a8", + createdAt: "2018-09-11T17:12:58.314Z", + }, + { + id: "3353127138299058636", + version: 0, + timestamp: 46584786, + height: 134, + reward: "0", + previousBlock: "16518342502287211733", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + blockSignature: + "3045022100fbe535970ab538c2e595c29d61e5efc288032bd663d4abf635c789b6a131632602207cca6065f02369324aad13744baedcd7a382243cff5d14beb66b72f58d43dd9a", + createdAt: "2018-09-11T17:13:06.404Z", + }, + { + id: "13175423574809505282", + version: 0, + timestamp: 46584794, + height: 135, + reward: "0", + previousBlock: "3353127138299058636", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + blockSignature: + "304402200896963614fba6bc8973e8c8be928c967e79c51f424ac42fc8ed7a77dc9211ec02205193a9cf0f0de3aa649d233c4fe23d33047beefdab31c73d28c3c906b17a3a10", + createdAt: "2018-09-11T17:13:14.314Z", + }, + { + id: "3055219214450264731", + version: 0, + timestamp: 46584802, + height: 136, + reward: "0", + previousBlock: "13175423574809505282", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + blockSignature: + "304402204f32800096d690cc2619fd971dce261c95c785647d8a577a231de9556dfb87ab0220227c64529eca59adbf5c744a13248b51bab87759543dfeb2bcd12cba79d5dec7", + createdAt: "2018-09-11T17:13:22.356Z", + }, + { + id: "2571351432366343511", + version: 0, + timestamp: 46584810, + height: 137, + reward: "0", + previousBlock: "3055219214450264731", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + blockSignature: + "304402200c238364bb210d0193429add99316ae42bdba0b5b8335480ea5c018ceae23b7802204fd755e41d4110a84837de79769c2cd95aefbe3dc3a8da18361119f01d176ea0", + createdAt: "2018-09-11T17:13:30.363Z", + }, + { + id: "6241265622100638768", + version: 0, + timestamp: 46584818, + height: 138, + reward: "0", + previousBlock: "2571351432366343511", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + blockSignature: + "3045022100b9fa19345ed45ac93c0eb6f6b939ec78b73eda64946da8a2f25c0618265df1ba0220147bca759e832db436f7240bde45938d1f43b65e8e11d46c0c7e4e499fd5153f", + createdAt: "2018-09-11T17:13:38.346Z", + }, + { + id: "18124529744536436230", + version: 0, + timestamp: 46584826, + height: 139, + reward: "0", + previousBlock: "6241265622100638768", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + blockSignature: + "3044022064c5588cc5cbed09662f7850cb610d940e8ebc54ae2607ea47f5a03c6d0b419b022040a2765191b7b2df155cf560d1841390e399919ac02b15782893c9c76bb79d40", + createdAt: "2018-09-11T17:13:46.397Z", + }, + { + id: "10444106797405289101", + version: 0, + timestamp: 46584834, + height: 140, + reward: "0", + previousBlock: "18124529744536436230", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + blockSignature: + "304402206a5e1d6d97dcf9ee1b6c37bfcd297f5e531a67890a36e3b73ba91861614ff4e00220637bfac65bc7c5156a06af632eab4dbeb76ba26a4cdb35d967d0e869138fd0fd", + createdAt: "2018-09-11T17:13:54.389Z", + }, + { + id: "9550675986057608428", + version: 0, + timestamp: 46584842, + height: 141, + reward: "0", + previousBlock: "10444106797405289101", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + blockSignature: + "3045022100cf7c530e0df3a6d78f5329e21c2c54febd6a6e040260df5c2f740dbc7bf497b8022021f498737f154a979ce29812e58bab3e04abc3e18296f75ee3c56dfa8e5de574", + createdAt: "2018-09-11T17:14:02.315Z", + }, + { + id: "14001735896815839031", + version: 0, + timestamp: 46584850, + height: 142, + reward: "0", + previousBlock: "9550675986057608428", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + blockSignature: + "3045022100873e93f03cdaa3ca9cee3d93075af754a5599bb54652b5bde42600811bbff21202206a8d479d2c3a7ed95575034321621acdfd377fd805c7d265945dec6962e180f5", + createdAt: "2018-09-11T17:14:10.260Z", + }, + { + id: "10633857440088301351", + version: 0, + timestamp: 46584858, + height: 143, + reward: "0", + previousBlock: "14001735896815839031", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + blockSignature: + "3044022067a82ec75db9a587e6f8890baa93037b647c6093e42de0cdf6703a3198bee78f02201b1083c9bd74488d37bbec3ff3f1b3e9c684d4e0e1dd3fe60ee2547d228eed3c", + createdAt: "2018-09-11T17:14:18.337Z", + }, + { + id: "10947047468505212355", + version: 0, + timestamp: 46584866, + height: 144, + reward: "0", + previousBlock: "10633857440088301351", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + blockSignature: + "3043022055bf8dea517e636fa46f0826706996307b49d0935891e58eec9d363bdade8fbe021f433b6ea919156f75084b4a502185d3000e91d378aae8ffabfebed7a8c5ca0d", + createdAt: "2018-09-11T17:14:26.275Z", + }, + { + id: "7416108013584846374", + version: 0, + timestamp: 46584874, + height: 145, + reward: "0", + previousBlock: "10947047468505212355", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + blockSignature: + "304402207500b2890ff54361434c2abf11f5aa587bd528d78756f40cc072a4b5260ec817022066558ab1b5d969992880d64c9e4f0966775b373c33903c03eca61220ff64d1fb", + createdAt: "2018-09-11T17:14:34.388Z", + }, + { + id: "4808775828130615656", + version: 0, + timestamp: 46584882, + height: 146, + reward: "0", + previousBlock: "7416108013584846374", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + blockSignature: + "3044022039efce2d80b670403fbb0f3a2011cff6e207178cc4f46cb0c6267f89528586f102205a6658c235e31de7781be8d248f12568699f17c72408946aa0c7bde43ece1688", + createdAt: "2018-09-11T17:14:42.542Z", + }, + { + id: "6351137783535009353", + version: 0, + timestamp: 46584890, + height: 147, + reward: "0", + previousBlock: "4808775828130615656", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + blockSignature: + "3045022100996f089968b240a3e72321b1a7f6f32d210c28aebbcf3978842a9a5d7beddf2d02201ae019ab437bdfddb416c234ca4364eda1658cf31c4da9bf2ac5c2c9a4c1b5bb", + createdAt: "2018-09-11T17:14:50.391Z", + }, + { + id: "12627548292318554118", + version: 0, + timestamp: 46584898, + height: 148, + reward: "0", + previousBlock: "6351137783535009353", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + blockSignature: + "304502210085c920053b6aa11fdcfc54ac7d2b73799e9c0a5a5a853115ed4186d5b420d14f0220127e87b4911deddeaf4a88af43cc6a2ae41a36a3109ed9942c3c88940d052154", + createdAt: "2018-09-11T17:14:58.246Z", + }, + { + id: "2084097592015010038", + version: 0, + timestamp: 46584906, + height: 149, + reward: "0", + previousBlock: "12627548292318554118", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + blockSignature: + "3045022100d0fe7aa7bd1b5ec46fcf82eff1a23f632e2fd8658df8a2cfaabb4bbb89a5bc63022041a11d99c92dfcb51108888e3b1fd80b4d169b605f1f177758a8dc875567b3ae", + createdAt: "2018-09-11T17:15:06.428Z", + }, + { + id: "289908998694171445", + version: 0, + timestamp: 46584914, + height: 150, + reward: "0", + previousBlock: "2084097592015010038", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + blockSignature: + "304502210095e6c2904ea13151400e946d07c2d993be0c5f25f85e7325bd0607f952d3f450022034fd2236a2973559844ce3ac2dda58f0d0a17a3f8df6d46627a15809ec2c1ec3", + createdAt: "2018-09-11T17:15:14.427Z", + }, + { + id: "8005210879399134536", + version: 0, + timestamp: 46584922, + height: 151, + reward: "0", + previousBlock: "289908998694171445", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + blockSignature: + "3045022100a6adbabb46d2a78ff68ecd4249460e42a60c5194f85fefe3997300af70c36e460220654d30777cd3f362067d6be444c3fc6c28610f32cda43414ae27dd945b7cb6f2", + createdAt: "2018-09-11T17:15:22.323Z", + }, + { + id: "16154614440238332956", + version: 0, + timestamp: 46584930, + height: 152, + reward: "0", + previousBlock: "8005210879399134536", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + blockSignature: + "304402207087465aa8fcd2ead5bb853c57d09f1722c140ebe8bd6f8eba3018f95dea98070220617a9722465e67d9e7ecb52f7a124daf7fb7edb0dbe1a3d817c20bbbe5bc3cb6", + createdAt: "2018-09-11T17:15:30.312Z", + }, + { + id: "4655700423809570268", + version: 0, + timestamp: 46584938, + height: 153, + reward: "0", + previousBlock: "16154614440238332956", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + blockSignature: + "3045022100d7441a6c9193dde35da5240454733a724ec0eff34c820a5c0d34308ab798e3c902200de18c320188f0c0f1844b3ec0f2eb3ced452410ddc04913610a8bc33c12c14d", + createdAt: "2018-09-11T17:15:38.549Z", + }, + { + id: "1945298362228106487", + version: 0, + timestamp: 46584946, + height: 154, + reward: "0", + previousBlock: "4655700423809570268", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + blockSignature: + "304402200dffa8d365fdca32e75598ca5ee84750f2dd606e3dc20a1da18cbb84cf80262402206ce23e4fa379fe1c9bce8810ddbaf700d04a27bf5249e8ffe99323be9ecd9989", + createdAt: "2018-09-11T17:15:46.313Z", + }, + { + id: "8368457960814709674", + version: 0, + timestamp: 46584954, + height: 155, + reward: "0", + previousBlock: "1945298362228106487", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + blockSignature: + "3045022100b7e6c2c4797d307ea7e66a7bd6b0008bfe871ad77dc525e13b3bf0da9ebb06d6022052c11d46e8238f1979dee6d3e4b6ccb1ad3be7c38a59632d21bae58e045ebb0c", + createdAt: "2018-09-11T17:15:54.342Z", + }, +]; diff --git a/packages/core-test-utils/src/fixtures/testnet/blocks2to100.ts b/packages/core-test-utils/src/fixtures/testnet/blocks2to100.ts new file mode 100644 index 0000000000..fca7df062b --- /dev/null +++ b/packages/core-test-utils/src/fixtures/testnet/blocks2to100.ts @@ -0,0 +1,1686 @@ +/* tslint:disable */ +export const blocks2to100 = [ + { + id: "17882607875259085966", + version: 0, + timestamp: 46583330, + height: 2, + reward: "0", + previousBlock: "17184958558311101492", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + blockSignature: + "3045022100e7385c6ea42bd950f7f6ab8c8619cf2f66a41d8f8f185b0bc99af032cb25f30d02200b6210176a6cedfdcbe483167fd91c21d740e0e4011d24d679c601fdd46b0de9", + createdAt: "2018-09-11T16:48:50.550Z", + }, + { + id: "7242383292164246617", + version: 0, + timestamp: 46583338, + height: 3, + reward: "0", + previousBlock: "17882607875259085966", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + blockSignature: + "304402204087bb1d2c82b9178b02b9b3f285de260cdf0778643064fe6c7aef27321d49520220594c57009c1fca543350126d277c6adeb674c00685a464c3e4bf0d634dc37e39", + createdAt: "2018-09-11T16:48:58.431Z", + }, + { + id: "6799129462450431489", + version: 0, + timestamp: 46583346, + height: 4, + reward: "0", + previousBlock: "7242383292164246617", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + blockSignature: + "304402201edab51a52d06b8c2e30fe334699239db1ff198172c11600b62fa063b7f74ef9022047649d94df2342707c1aeefc635260c6b3f642735588f4e04965c01db820df44", + createdAt: "2018-09-11T16:49:06.496Z", + }, + { + id: "12118504647305813914", + version: 0, + timestamp: 46583354, + height: 5, + reward: "0", + previousBlock: "6799129462450431489", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + blockSignature: + "3045022100e5a098f4a5b83c3550eeebfd65b04099b70f6cc1eb66a1f0e9cc8f516d6d229b02200610a07cd2fa908b7dbc19743f73f26623f7e9c36d46020bc1605653cb211bce", + createdAt: "2018-09-11T16:49:14.403Z", + }, + { + id: "15175173194595918016", + version: 0, + timestamp: 46583362, + height: 6, + reward: "0", + previousBlock: "12118504647305813914", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + blockSignature: + "304402201b82f934764e43f7ff45091e014ca3bd6c10d4300553818b63b6e80719625d6002207e4af15383c836835a5f61f79b8f50ff104cbd339bf92283107759a007c2a93f", + createdAt: "2018-09-11T16:49:22.314Z", + }, + { + id: "4904019637861251674", + version: 0, + timestamp: 46583370, + height: 7, + reward: "0", + previousBlock: "15175173194595918016", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + blockSignature: + "3044022056993f85cd7876109aa724f3c665323be14584c83457483c07ea36d6c7b75ebc02200c710bd09883471452fec45570bf6f5bbb06266feef3f8f2a9921cb4d8e13a37", + createdAt: "2018-09-11T16:49:30.295Z", + }, + { + id: "7856584295950436953", + version: 0, + timestamp: 46583378, + height: 8, + reward: "0", + previousBlock: "4904019637861251674", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + blockSignature: + "3045022100e45ec5b032e7762711c3727f30004fffe631605e9c323d1cd278b079714a9e30022002eea2c2c5234079e52326a69944c5f064f329ca163cbd6ef6a1272ec70f8524", + createdAt: "2018-09-11T16:49:38.366Z", + }, + { + id: "9519090140760236724", + version: 0, + timestamp: 46583386, + height: 9, + reward: "0", + previousBlock: "7856584295950436953", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + blockSignature: + "304402200cc7bb74b97d8fcabf46f638ae3272143fdf6aa752df7ec5a3f850c976dfd2580220344cf0dea89111b7a455d95266b541753c40e7560d65af256d7bc3c97971e10a", + createdAt: "2018-09-11T16:49:46.409Z", + }, + { + id: "16953205951793869073", + version: 0, + timestamp: 46583394, + height: 10, + reward: "0", + previousBlock: "9519090140760236724", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + blockSignature: + "3045022100ba9cf471fad1103fa05c92038dd7f930edcfe526dfb4a68160ce068aa30e207802207b4ba0383953948c1fc7ec9125e8845cc3dd1df5f25727c9165e85c35c81d3c6", + createdAt: "2018-09-11T16:49:54.277Z", + }, + { + id: "8196750727867107014", + version: 0, + timestamp: 46583402, + height: 11, + reward: "0", + previousBlock: "16953205951793869073", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + blockSignature: + "304402200f93d59646c5e0bbc5b1d59bc9630bdee2669646c21d4ddf4db80c0b8afc1c0302200aaa36850b010ef9395f0a1f63652d236ef0265794953816e714a659f0a77283", + createdAt: "2018-09-11T16:50:02.246Z", + }, + { + id: "11789312660453212991", + version: 0, + timestamp: 46583410, + height: 12, + reward: "0", + previousBlock: "8196750727867107014", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + blockSignature: + "3045022100bac34bc173c7b473db162b72e2b3a14c51d6b1e9132530e759d6312a16241d6f022027328c6c69907bd67dcd23163fbbb5a5fff817d2beb62f67f1b61e2c202405db", + createdAt: "2018-09-11T16:50:10.268Z", + }, + { + id: "883541050685133383", + version: 0, + timestamp: 46583418, + height: 13, + reward: "0", + previousBlock: "11789312660453212991", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + blockSignature: + "30450221008574bfed61362c0a89a27e168d2ea83c9221e60e65d45471063d9ee295342dc502203d07ae5cdc6f04c6cb32e9569c3d57a2918a20ecb7b1e821075764518a269257", + createdAt: "2018-09-11T16:50:18.367Z", + }, + { + id: "2576147034160793253", + version: 0, + timestamp: 46583426, + height: 14, + reward: "0", + previousBlock: "883541050685133383", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + blockSignature: + "30440220125c9e17f524e4153853449c9f86961751f3225c1f3fbe4c0bb8eb3e66d9a3d802202d7cef058d1b3f5ce7ac7840d23aaefa595228ecff9aa30be41c6b809c5e7997", + createdAt: "2018-09-11T16:50:26.340Z", + }, + { + id: "15862421549550122994", + version: 0, + timestamp: 46583434, + height: 15, + reward: "0", + previousBlock: "2576147034160793253", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + blockSignature: + "30440220224f65b1190ab981691879b3d6861e11703b5fe78bceb36f5f51a8170228634b02203a8262131cd75cd2f6d56db8f5f5e370fcb635c84ca7a4b319d534a85476e543", + createdAt: "2018-09-11T16:50:34.426Z", + }, + { + id: "4688274543361472830", + version: 0, + timestamp: 46583442, + height: 16, + reward: "0", + previousBlock: "15862421549550122994", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + blockSignature: + "304402201a3aa0ebb176ff0ccc8c876142cada83cf8c12dc8a45de36eaf139c7d4d78e05022064b5f4b4da27a1e625236db9dcec89788fb417c88a928e193dcf816ad71117db", + createdAt: "2018-09-11T16:50:42.534Z", + }, + { + id: "5883773764321361785", + version: 0, + timestamp: 46583450, + height: 17, + reward: "0", + previousBlock: "4688274543361472830", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + blockSignature: + "3045022100f7ed8590c1c31e40b41d44c9321fb03d2864ef6f651ce518bec5a0eda5343bb302205bc757186589556a30d53abb9469c387011b49634494108b688d75bf63f07761", + createdAt: "2018-09-11T16:50:50.441Z", + }, + { + id: "7698647406843990550", + version: 0, + timestamp: 46583458, + height: 18, + reward: "0", + previousBlock: "5883773764321361785", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + blockSignature: + "304402201519d3e2c7d9221d8efcb532bccad9e5ac538751e7a43fb0bee7e736361483c802205b0e58959f9047c1a5de8af79d89a43828f85457653cbf732e164bdeef429c7b", + createdAt: "2018-09-11T16:50:58.430Z", + }, + { + id: "17299323387642470917", + version: 0, + timestamp: 46583466, + height: 19, + reward: "0", + previousBlock: "7698647406843990550", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + blockSignature: + "304402201bc616ef4dce8261b202df53a00555452d3dbe67be0a1959d06b269a189f0b7a02202d742310a55b693694f3f47ccd143629199ffe62ad0636d2fad52b8fad077249", + createdAt: "2018-09-11T16:51:06.342Z", + }, + { + id: "13814549369794209203", + version: 0, + timestamp: 46583474, + height: 20, + reward: "0", + previousBlock: "17299323387642470917", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + blockSignature: + "3045022100efc8192c34bcc4ac7bd74ea784a3a590eb9293e8923ff5cbb3eea74589c6a62d02206359a30fa492e1725655333b769ab6543fed69ba3ba6a5066a26cced31bf4192", + createdAt: "2018-09-11T16:51:14.337Z", + }, + { + id: "2277077266570141420", + version: 0, + timestamp: 46583482, + height: 21, + reward: "0", + previousBlock: "13814549369794209203", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + blockSignature: + "3044022054ad1575cc1ee09284f3f5252f80e1d48a59a9a3db740d8053784699824692ef022060ee4c78ea07e5612d86d74a70f6e1d5a94de5c2ed12a41111630caeee30b081", + createdAt: "2018-09-11T16:51:22.244Z", + }, + { + id: "5168238890100617525", + version: 0, + timestamp: 46583490, + height: 22, + reward: "0", + previousBlock: "2277077266570141420", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + blockSignature: + "30450221008ccfb6b5c8cbf8831c82f8a86afa88c2a27eb63d42c0cbd5eb9abe729fda314b02203df3fc50902656cce3bab7d5eb0a46d67be4eaab65fd41b1788b3676301cd005", + createdAt: "2018-09-11T16:51:30.339Z", + }, + { + id: "13736455109678252775", + version: 0, + timestamp: 46583498, + height: 23, + reward: "0", + previousBlock: "5168238890100617525", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + blockSignature: + "304402206ea489d925646a1253e78df0c9e761dbb58a173da5c34911301e262bc141127202201072cd4af50e72b5d34ad04708712b658d9ebb22fb00cc86132b0759fe04deba", + createdAt: "2018-09-11T16:51:38.469Z", + }, + { + id: "4282003522259720405", + version: 0, + timestamp: 46583506, + height: 24, + reward: "0", + previousBlock: "13736455109678252775", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + blockSignature: + "304402200593032068e5c67d38f4bd6cda8f4f64675f7b4d0bd07c77bf6c401c43c7e98e0220621eb05a51f1e7799af8fb45846fc75aa2e91246ba9db732976511a2bf0b8c0d", + createdAt: "2018-09-11T16:51:46.331Z", + }, + { + id: "11402948775542429021", + version: 0, + timestamp: 46583514, + height: 25, + reward: "0", + previousBlock: "4282003522259720405", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + blockSignature: + "3045022100e523b1c3ca604d62cf8bcf7e59b5eb22ad05ee1170619e07ac6bf90c204901bf0220214c72bd57067526672a57ca71147c4de05072311aa0962e1c9ae2e3ee89d413", + createdAt: "2018-09-11T16:51:54.309Z", + }, + { + id: "10423041071728627937", + version: 0, + timestamp: 46583522, + height: 26, + reward: "0", + previousBlock: "11402948775542429021", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + blockSignature: + "3045022100934248e71acba0b36fa40872ef2c36c8d807a23108f6d741d53422b48456e63b0220763a120b4eb8a25aa7a3bac5345cdccde50864a1fc6a9334a7c73ec4e548aded", + createdAt: "2018-09-11T16:52:02.508Z", + }, + { + id: "8610162720362054175", + version: 0, + timestamp: 46583530, + height: 27, + reward: "0", + previousBlock: "10423041071728627937", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + blockSignature: + "304402203fee457c035eb6df8abd0116a2726f8cf9a14758a5a7719589696cc338eee379022002722dd5064c308d1c875c99f7b6fe2376a7b987b6e4762ef669ff36d86a195c", + createdAt: "2018-09-11T16:52:10.336Z", + }, + { + id: "12492391117174185461", + version: 0, + timestamp: 46583706, + height: 28, + reward: "0", + previousBlock: "8610162720362054175", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + blockSignature: + "304502210085a248b96899c9443795857d3ce47f0ca7af2b97b9331e0e2a89ba715f98d79c022022e2839597e282d49ca9afbab66d417d754cc7acfb9e501a9db18506e5bfe1d4", + createdAt: "2018-09-11T16:55:06.505Z", + }, + { + id: "16807996944516641692", + version: 0, + timestamp: 46583714, + height: 29, + reward: "0", + previousBlock: "12492391117174185461", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + blockSignature: + "304402204d71ed66ddada97c2c38cac4650b5cd162b3fee65875e943ed41a253aa3d5ac4022013a00da3750cf1760208d78ae3cea524cc02c5909e806643b2907ee98ac2017a", + createdAt: "2018-09-11T16:55:14.296Z", + }, + { + id: "13831680932126032361", + version: 0, + timestamp: 46583722, + height: 30, + reward: "0", + previousBlock: "16807996944516641692", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + blockSignature: + "304402204e3aaa4af5776b85fe79120ddd37ebba8389cf18ef56df0b48d7ce6b3897e25b0220146ec521a9d32284eb2cfa9010b3379ba5a65020e25639ad644586ce272d99b8", + createdAt: "2018-09-11T16:55:22.414Z", + }, + { + id: "12836676775100447088", + version: 0, + timestamp: 46583730, + height: 31, + reward: "0", + previousBlock: "13831680932126032361", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + blockSignature: + "3044022058ffb2ceae3807c1beb244ba3f45c64f19f774de94b9f496b115da7eb0f877490220760d4d2f6048f7e694aa8cd5460a59fac64d671affec0cfddb2b4062ef3fe023", + createdAt: "2018-09-11T16:55:30.537Z", + }, + { + id: "16288754112931452950", + version: 0, + timestamp: 46583738, + height: 32, + reward: "0", + previousBlock: "12836676775100447088", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + blockSignature: + "3045022100c1ab5f864e444ae93537967f1ff485fe491496cef9404a1152d79cdf4244527102201246d40ba8e7038911403da19f6a685e6e0a86703697cc52c79d62d665bc4af0", + createdAt: "2018-09-11T16:55:38.270Z", + }, + { + id: "13854880431048519276", + version: 0, + timestamp: 46583746, + height: 33, + reward: "0", + previousBlock: "16288754112931452950", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + blockSignature: + "3045022100ca3177eba7928227e814ec37d9f0f4926add2020aa6b6de5f91e3121a64e4a60022067d2b87d4d7657de5c826985c494db50eb4b9a60ba30b722bf47a4bf708d3286", + createdAt: "2018-09-11T16:55:46.255Z", + }, + { + id: "13840357199384695281", + version: 0, + timestamp: 46583754, + height: 34, + reward: "0", + previousBlock: "13854880431048519276", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + blockSignature: + "304402205de0e8135717890ba90b31b60bc47b896dadbe5f9eb1b3eff084e0fb75afa8cf02200a1ddd0121786658972b495a3f812a4cf61afd99df7c10c93cd21ff74e1c361e", + createdAt: "2018-09-11T16:55:54.380Z", + }, + { + id: "11244772664213279648", + version: 0, + timestamp: 46583762, + height: 35, + reward: "0", + previousBlock: "13840357199384695281", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + blockSignature: + "3045022100e9d6a88ed3f96c5c33be6487159c3fdbdac0497f4958e7a6956332943482891c02202ecffc19b8d8cafe66c1e32a2e032d8f105ff9a4cd2ad46304b18fa168708d5e", + createdAt: "2018-09-11T16:56:02.434Z", + }, + { + id: "15443428733962171330", + version: 0, + timestamp: 46583770, + height: 36, + reward: "0", + previousBlock: "11244772664213279648", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + blockSignature: + "3044022070f7635302affb1ca9f515ddf14d0357e21846a5fd2094c4c27879b93a9c8b9702204e79875847238f560c932354a067167c74823da5a85eb45b7fbdb6a7265f33e1", + createdAt: "2018-09-11T16:56:10.294Z", + }, + { + id: "7454266686990589007", + version: 0, + timestamp: 46583778, + height: 37, + reward: "0", + previousBlock: "15443428733962171330", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + blockSignature: + "3044022057258edc364989cda08dc60c7059813db1fcf71004519691301226c08457a3b20220750dbad6b2734e2c66d53a39f9a90bf11d13e7f597440911c120b01803b17b7d", + createdAt: "2018-09-11T16:56:18.498Z", + }, + { + id: "4597925663050872611", + version: 0, + timestamp: 46583786, + height: 38, + reward: "0", + previousBlock: "7454266686990589007", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + blockSignature: + "304402202004341ca61b0580246d06c77f1099ea50e264d3bed1005568e0ededa850250b0220472ec5c11c57f6510ef95cd54dcd124e6272569907e2765ee9cf1b237a2dd612", + createdAt: "2018-09-11T16:56:26.323Z", + }, + { + id: "4238729528436398513", + version: 0, + timestamp: 46583794, + height: 39, + reward: "0", + previousBlock: "4597925663050872611", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + blockSignature: + "3045022100abad03f7fb997fa8fed0830720e0cf1d0c84de4c9315adbc1440896b9b4d0ea302201e190b801e0945a6e7921d407d073a066f4e4b0eeba68cefc44135e08fb08dcc", + createdAt: "2018-09-11T16:56:34.513Z", + }, + { + id: "9197895153416047617", + version: 0, + timestamp: 46583802, + height: 40, + reward: "0", + previousBlock: "4238729528436398513", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + blockSignature: + "304402204a0d98b25ecf80f6bcc17474c62cbd1fa61988fb4aad4d7cd5e6eb9f3440446202206ec758d73ad3bc2fd612bb0d6dcc2b7b20f6dadf46afa968b32631aff013bb07", + createdAt: "2018-09-11T16:56:42.416Z", + }, + { + id: "924154762496380167", + version: 0, + timestamp: 46583816, + height: 41, + reward: "0", + previousBlock: "9197895153416047617", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + blockSignature: + "3045022100b81be618931cddee9ea14e40a96fa142c7954daf78b43924a9487f94eea71053022032de4cbc65040c40a859f2bd8abe82f6c2683607aac54bd7022129da178ed1c4", + createdAt: "2018-09-11T16:56:56.352Z", + }, + { + id: "15177934353618302368", + version: 0, + timestamp: 46583826, + height: 42, + reward: "0", + previousBlock: "924154762496380167", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + blockSignature: + "3045022100e461ebcdf96f5da7847d05de62a12503fee7fb3bcceb0dfdb142bc32c66dbab4022054a8f0b75d0ffdf46e167a44427bc5b931200f5fadbdd06d30002314434344aa", + createdAt: "2018-09-11T16:57:06.383Z", + }, + { + id: "924392217041273716", + version: 0, + timestamp: 46583834, + height: 43, + reward: "0", + previousBlock: "15177934353618302368", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + blockSignature: + "3045022100d978d3336898294151c583d7c2b8f4b578a9af41eba08ae399d02b16815c61da02204ce7f53790358a8bf48e9bbc9019de7e6f827fe0a7bf015fb7ded4579b3229b5", + createdAt: "2018-09-11T16:57:14.417Z", + }, + { + id: "16477448205939004004", + version: 0, + timestamp: 46583842, + height: 44, + reward: "0", + previousBlock: "924392217041273716", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + blockSignature: + "304402202d10704a95ce21dfb3e11956a29cf692e2d6220a5f7ad1521691e2e3cdd37c9b02205b670adf2e1c2961669f31f70d6dea35aa0cfa13ab04adf36ff80a4f47b60615", + createdAt: "2018-09-11T16:57:22.490Z", + }, + { + id: "12838684505709123370", + version: 0, + timestamp: 46583850, + height: 45, + reward: "0", + previousBlock: "16477448205939004004", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + blockSignature: + "3045022100f93f4b968f294e8bbe3d0cfe004a493749e9c8cb9dedc1f72a7bf7a351032e8002201b3564c0d996ec08d80069de99d88ac5d0db3cd6ea5f275ec4099b0e7ff7f88e", + createdAt: "2018-09-11T16:57:30.496Z", + }, + { + id: "5132859035237431345", + version: 0, + timestamp: 46583858, + height: 46, + reward: "0", + previousBlock: "12838684505709123370", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + blockSignature: + "3045022100cb32d6cb514ea08308a37a4c0fc9557d4b8380004a61a54cdda3ed8c143327ab022075578c0afa5ceb4c0ac3ca277efb1cede2d332168e7849b4c99e3ae63ee5da05", + createdAt: "2018-09-11T16:57:38.441Z", + }, + { + id: "7963682794246037175", + version: 0, + timestamp: 46583866, + height: 47, + reward: "0", + previousBlock: "5132859035237431345", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + blockSignature: + "304402200c65cac8594f02135f9c073e9786b8202f41b3fee67c32a50baf31d7cc90014e02207c4398cb34bcbbeeb25eef195e467a74bd3fa459a7e24e48aea3f8d7e56abe6b", + createdAt: "2018-09-11T16:57:46.440Z", + }, + { + id: "13070232553444551324", + version: 0, + timestamp: 46583874, + height: 48, + reward: "0", + previousBlock: "7963682794246037175", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + blockSignature: + "3045022100c7cd9708728c454006f1df0cb1de64c62a82e2df1d94b3fc2d8319fb78e7240d022066ce9e73f7039f949e412d19cb257c1ea818fb84d64d89d63409508ed802eacc", + createdAt: "2018-09-11T16:57:54.296Z", + }, + { + id: "625974805128499213", + version: 0, + timestamp: 46583882, + height: 49, + reward: "0", + previousBlock: "13070232553444551324", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + blockSignature: + "3045022100f12e139ee66c5936775bad938e1749117c8882e392c06596b1fc24959695a3c102203b708a0bcd11f51c7739ed22758a6e5235ea510756b285281f8e93a486609e04", + createdAt: "2018-09-11T17:01:38.244Z", + }, + { + id: "15904513475939308775", + version: 0, + timestamp: 46584112, + height: 50, + reward: "0", + previousBlock: "625974805128499213", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + blockSignature: + "304402202e0ae3e498efaf903740cedd72790f8d62aa7010ae5aa8e8382353a2d8577e940220714efec6305271e3c8b3f8076cf967ffd4167084bd9567a9f24300a8246bf197", + createdAt: "2018-09-11T17:01:52.512Z", + }, + { + id: "15510607002956264823", + version: 0, + timestamp: 46584122, + height: 51, + reward: "0", + previousBlock: "15904513475939308775", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + blockSignature: + "3044022078ec98d6895f1bc2a09623f060fd479bb4f75c37706d6c46c36209d84d54568402202c2a09000caafa25580e5b884fc973453574d80d7ee700517094e05f73f92d70", + createdAt: "2018-09-11T17:02:02.491Z", + }, + { + id: "17227544728161997595", + version: 0, + timestamp: 46584130, + height: 52, + reward: "0", + previousBlock: "15510607002956264823", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + blockSignature: + "30450221009a94f6cab02143fd03db3745d714dcafcddbfa28e440bc74a27fcef409d3069902207526c6626410f71224f89b7f7dd5136d92fd06aa0f95d714fd8ca62aa29867b1", + createdAt: "2018-09-11T17:02:10.367Z", + }, + { + id: "8285956439623855556", + version: 0, + timestamp: 46584138, + height: 53, + reward: "0", + previousBlock: "17227544728161997595", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + blockSignature: + "30450221008c8be43703dd3b513e2fab89ccbb307be40a2015c060b37f34252cb3c4463d82022071d817add1e17f5097850e905c9963bb87e56055fe69fa8e5f897f8e83dc98b7", + createdAt: "2018-09-11T17:02:18.360Z", + }, + { + id: "6896439417802463255", + version: 0, + timestamp: 46584146, + height: 54, + reward: "0", + previousBlock: "8285956439623855556", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + blockSignature: + "3044022041e2d1d9a3215a1cee4e6f24f2d0bf9ab8e9c8aad85306c01de38925b997d6da022004bceb2844ac828ce07d572238910be51fe5e9508f2c8a0c36719db5d954a0c7", + createdAt: "2018-09-11T17:02:26.339Z", + }, + { + id: "2902372601932188608", + version: 0, + timestamp: 46584154, + height: 55, + reward: "0", + previousBlock: "6896439417802463255", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + blockSignature: + "3045022100eaf1cd0c296484c50fa507fd8f375891b1acc6c0434cee6f93ce45f40a181ad60220460c1fce332bdf297c4675a806899187cd6c86337d9e2b03ed972c3fe7009b74", + createdAt: "2018-09-11T17:02:34.363Z", + }, + { + id: "10825219670382518509", + version: 0, + timestamp: 46584162, + height: 56, + reward: "0", + previousBlock: "2902372601932188608", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + blockSignature: + "304502210087bb4b53db33adb4de3f21412882f30e6e5391466d23a2f6940b2a4493d9123702201d581122e703af1552c3750d4c6a4cff0d82b16647e4edadd0c4e1dfc52291fc", + createdAt: "2018-09-11T17:02:42.403Z", + }, + { + id: "8203927523221479932", + version: 0, + timestamp: 46584170, + height: 57, + reward: "0", + previousBlock: "10825219670382518509", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + blockSignature: + "304402203be65a7c74ca18ac8661a447ab209fce85f76d8c87a30db642342d9c41d138a602201667db0713ad5977cd6b42a045707c19147d28dc9c86ef5e6f974ab68c1d3e28", + createdAt: "2018-09-11T17:02:50.411Z", + }, + { + id: "16715427056889121751", + version: 0, + timestamp: 46584178, + height: 58, + reward: "0", + previousBlock: "8203927523221479932", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + blockSignature: + "3045022100b35dec76883908f5a91be19b2d31bc55b0486cb9f9350ecd5d565a7614a2df0702203219fb7439379cd1ff82377d8dcfdc6bda6bda99d2cc4c2050925c064f71aba0", + createdAt: "2018-09-11T17:02:58.302Z", + }, + { + id: "16305880676178938140", + version: 0, + timestamp: 46584186, + height: 59, + reward: "0", + previousBlock: "16715427056889121751", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + blockSignature: + "3045022100e5896815ae064ca2244dcce1fbc3651527f0d7a8e09bad13fb37a8d5001bf1f302207b3d1b61dee0ff0c9c435a794b65d66d9bdaf982ac20c5eff1f718fbf95351d6", + createdAt: "2018-09-11T17:03:06.354Z", + }, + { + id: "7799778404770723520", + version: 0, + timestamp: 46584194, + height: 60, + reward: "0", + previousBlock: "16305880676178938140", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + blockSignature: + "30440220375cd7cde93652b17926414f4ec24dbd4bac9e8ec961d242d475aa1edf0e74240220341048d58153b1055daf632ede7d2fcc9b57a5fafbc9d60495c25f9e7f68f752", + createdAt: "2018-09-11T17:03:14.404Z", + }, + { + id: "5758761688279180444", + version: 0, + timestamp: 46584202, + height: 61, + reward: "0", + previousBlock: "7799778404770723520", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + blockSignature: + "30450221009c87a22235797fcfa108f318de14ec99d6491545f6161280bab17e225ad381860220682d3a0a367c77915768b8ef7524379644c283cf2297b34d619c03d8d361d2ff", + createdAt: "2018-09-11T17:03:22.394Z", + }, + { + id: "300976438207115612", + version: 0, + timestamp: 46584210, + height: 62, + reward: "0", + previousBlock: "5758761688279180444", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + blockSignature: + "3045022100de8fc5c21168063ffae3a319c94de8029d02b4fbec0e6128c2ec8c7b18ade08402201250ffc4ef5ba71f3b8f0d8e9b935c39779066b313f57051d3e51f013831f6ca", + createdAt: "2018-09-11T17:03:30.381Z", + }, + { + id: "2019154568349929038", + version: 0, + timestamp: 46584218, + height: 63, + reward: "0", + previousBlock: "300976438207115612", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + blockSignature: + "304402206d927629fde40bc224ba6c044d3b5966193ccc578998dd42cbf2c526871d55c302201f83ed237a60531f7bb02ce26085069839d42f3dafd4b6581095000d01ecefa8", + createdAt: "2018-09-11T17:03:38.403Z", + }, + { + id: "10330603764729699281", + version: 0, + timestamp: 46584226, + height: 64, + reward: "0", + previousBlock: "2019154568349929038", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + blockSignature: + "3045022100e7194d853550abc79c507b2f6338c9013d528e0fb031ca345f5c1cbdd153c82302205b556d61d9a91399f6e7128657dbd279ec715bca0d0912ca0cc9ad87da0753a9", + createdAt: "2018-09-11T17:03:46.516Z", + }, + { + id: "16855224566886101894", + version: 0, + timestamp: 46584234, + height: 65, + reward: "0", + previousBlock: "10330603764729699281", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + blockSignature: + "304402204ece7e930f42e06e457da426fc59a0dedf29c04d76126e94167b50225c179850022010104fe54b125e341f888656d997ece2092e5bf599877d3a7d295d4ea5e4faf5", + createdAt: "2018-09-11T17:03:54.345Z", + }, + { + id: "3100095040675947327", + version: 0, + timestamp: 46584242, + height: 66, + reward: "0", + previousBlock: "16855224566886101894", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + blockSignature: + "3045022100e5623db11c1e683ea406c8f0688fa569481b126a3422c3c3f13e4b5dbafa021f0220172d01bbb12544d17877b99a3548efaffc8fa6692fe4f5b0e8e11bf57ebbc345", + createdAt: "2018-09-11T17:04:02.407Z", + }, + { + id: "9113535137970315837", + version: 0, + timestamp: 46584250, + height: 67, + reward: "0", + previousBlock: "3100095040675947327", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + blockSignature: + "3045022100a1110d53b5b0750bc0e4631df4c77d6cb4c7e31bfeb9d3a296b02ea6a5749671022008fe9533a58542903619deeda2a84c9c4542bd837c5920eeb959cee668be0c7e", + createdAt: "2018-09-11T17:04:10.390Z", + }, + { + id: "13522109433438488118", + version: 0, + timestamp: 46584258, + height: 68, + reward: "0", + previousBlock: "9113535137970315837", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + blockSignature: + "3045022100a39ab29adf9c2606d634766b2094ff8c069aa0f3fed8e3379554ff083f1e6183022056addd110d11f7d053e14741c67bb0fc573ddc3ded85589b422768a50cede4c0", + createdAt: "2018-09-11T17:04:18.376Z", + }, + { + id: "4836027030925388062", + version: 0, + timestamp: 46584266, + height: 69, + reward: "0", + previousBlock: "13522109433438488118", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + blockSignature: + "304402206286621e85e27d2f0c79b7a85da3da5465b0b8737cf506155b702fcbf542ee51022013e5a747e4b34c28dfefe2cb2ed47dc81dc15c04cc9d1aeab8590af4ed6ee3a9", + createdAt: "2018-09-11T17:04:26.540Z", + }, + { + id: "1124208886475980765", + version: 0, + timestamp: 46584274, + height: 70, + reward: "0", + previousBlock: "4836027030925388062", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + blockSignature: + "304402204095cf9589d899c6cc10ec0b391a5d3d1a3c94247e91680a6ecdf8c69c2e8519022062386adfd6bd42d825cd903a91882d98bf7dcce7c6d9f5d9a6172c95e9f1b870", + createdAt: "2018-09-11T17:04:34.421Z", + }, + { + id: "2300021073741981330", + version: 0, + timestamp: 46584282, + height: 71, + reward: "0", + previousBlock: "1124208886475980765", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + blockSignature: + "3045022100b02997cd91021d9a06dd77079aabb04e01de890176e6544e724e9563552082dc022053adc616e133e010aab3aa138548aa36e554decb899374a6c60c36d8d92525a4", + createdAt: "2018-09-11T17:04:42.337Z", + }, + { + id: "1435082221279493221", + version: 0, + timestamp: 46584290, + height: 72, + reward: "0", + previousBlock: "2300021073741981330", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + blockSignature: + "3045022100d3badcd7c113fd44d3ae22b502e45f1e9d15bbf7b5fc8a81aff765cf326199320220126e625a1b021f640cb79e3ea833c733e88d0eafe6c8947f7639f205431eefa8", + createdAt: "2018-09-11T17:04:50.375Z", + }, + { + id: "5781105193399022760", + version: 0, + timestamp: 46584298, + height: 73, + reward: "0", + previousBlock: "1435082221279493221", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + blockSignature: + "304402200fd5424d5304c669c1a8455a92490f1aab3005ad435ba4efda443b28ef6fe34902200c38c4b23213b42022ba551cdf6b26b8e29a4239ab88fd939e72f2f096821ea7", + createdAt: "2018-09-11T17:04:58.343Z", + }, + { + id: "7306295778722296358", + version: 0, + timestamp: 46584306, + height: 74, + reward: "0", + previousBlock: "5781105193399022760", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + blockSignature: + "304402202d794978935d04f48f1bbca967242b764d0ad19529dbf72fd8f08a14e300e39202205d960169ae3c18f983c2034cf387bd6bbd7cf4c30043179fd8da05c3dfa25e21", + createdAt: "2018-09-11T17:05:06.468Z", + }, + { + id: "11621508727770821942", + version: 0, + timestamp: 46584314, + height: 75, + reward: "0", + previousBlock: "7306295778722296358", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + blockSignature: + "3045022100c8395b5aa7a8f70cc5f8af5571efa3ac61906f0ca38ecd929ba1b7ba80ba3ab80220212acf9dd8b793f5b06e6e58a4a9791d37f28f431af88bd7cc53867e453de8e7", + createdAt: "2018-09-11T17:05:14.510Z", + }, + { + id: "3633426813146358866", + version: 0, + timestamp: 46584322, + height: 76, + reward: "0", + previousBlock: "11621508727770821942", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + blockSignature: + "304402200430ed30340bdf3dadc437db984547a7159f2fe716b7f607ce793353be2ceefc02201995468fe1cf73e0257f6db115f6de6bef1bee3de01222dd55fcfc243bc96fd4", + createdAt: "2018-09-11T17:05:22.467Z", + }, + { + id: "10585574832161348147", + version: 0, + timestamp: 46584330, + height: 77, + reward: "0", + previousBlock: "3633426813146358866", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + blockSignature: + "3045022100a69a370fcf30a8e80b36916fe17c4404c63ff8f5f0f654d662656db662be95a4022061dece06170f0e59a63abb078c5809488b6a3548470a699434fd769eb3e2ae66", + createdAt: "2018-09-11T17:05:30.462Z", + }, + { + id: "16732819090621444724", + version: 0, + timestamp: 46584338, + height: 78, + reward: "0", + previousBlock: "10585574832161348147", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + blockSignature: + "30450221008e6bdd7ea4189ea751e02157ab0449651a70405e560b68efb489f36b90b3c20702203d5764e527965e8f0fffa192bd3ccc4fa10649674e9c52cc361accf0d0c53541", + createdAt: "2018-09-11T17:05:38.369Z", + }, + { + id: "13535887590163426042", + version: 0, + timestamp: 46584346, + height: 79, + reward: "0", + previousBlock: "16732819090621444724", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + blockSignature: + "3044022052171485017323ec5ee011d4fa110a9e9137b2043ee6d0ec5f6da56bbf713f1102203744e54783bf400353b0221b2454b09ebdbddebe30a3d1c62fd7a1fc4cd5dd7f", + createdAt: "2018-09-11T17:05:46.419Z", + }, + { + id: "7447301067999335250", + version: 0, + timestamp: 46584354, + height: 80, + reward: "0", + previousBlock: "13535887590163426042", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + blockSignature: + "3045022100c729ce8b683f619368f91e782009b2319babacdc5ddc6d51fcaaaf76042c09e90220278ff4db42f02241a05bd26e2ad6c1ba71f0396a4c7c66b015756854dc9119e0", + createdAt: "2018-09-11T17:05:54.345Z", + }, + { + id: "9843400248178032761", + version: 0, + timestamp: 46584362, + height: 81, + reward: "0", + previousBlock: "7447301067999335250", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + blockSignature: + "3045022100858015fd991a4c318801217c8051572d2f0ac2657ce9676d612f3bdc4ed9a46a0220523af034dd7b11c31843615ec670675782e2e1549a260ada819ae9c453481da7", + createdAt: "2018-09-11T17:06:02.454Z", + }, + { + id: "14466873879302081211", + version: 0, + timestamp: 46584370, + height: 82, + reward: "0", + previousBlock: "9843400248178032761", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + blockSignature: + "30440220532e47a63b905269436a28b24c12da027f822ecbeddfb02f10ae9f6f7d05eba60220468a4e4f91c7b613292a937c4e7297590e0e2c66f1cffef628041f77c82b8e92", + createdAt: "2018-09-11T17:06:10.415Z", + }, + { + id: "14557757316112343772", + version: 0, + timestamp: 46584378, + height: 83, + reward: "0", + previousBlock: "14466873879302081211", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + blockSignature: + "30440220332a364f25ef135fbf1b6329778e8a0f0484d900b98e2ea883a5d9381a39e50b02204b4b0fb1d2f6aac5f2b61204782f20335d99e71a854fb0b17a5799dd862dbeca", + createdAt: "2018-09-11T17:06:18.358Z", + }, + { + id: "15094374440231126484", + version: 0, + timestamp: 46584386, + height: 84, + reward: "0", + previousBlock: "14557757316112343772", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + blockSignature: + "304402207fb74f4b2665307dced45c14025b1583c95d718841c4a320f60b67f749731bb302206716b2733bec550c86db5669ece70790ccbc225dcd2745c8007772b39bd5b9e9", + createdAt: "2018-09-11T17:06:26.235Z", + }, + { + id: "5714424774144793592", + version: 0, + timestamp: 46584394, + height: 85, + reward: "0", + previousBlock: "15094374440231126484", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + blockSignature: + "3045022100988030c41476d2e52b6e303e0a3ac6b9c56fe10d33f9dd8396d1c911e14eb2b702200997ac462d60095fd1e157a13986da7b4dd079796806865d63b7278763ae6fbc", + createdAt: "2018-09-11T17:06:34.371Z", + }, + { + id: "464843749694128591", + version: 0, + timestamp: 46584402, + height: 86, + reward: "0", + previousBlock: "5714424774144793592", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + blockSignature: + "3045022100e47ede694b10ed629f7da387151154c80f3a8fd434fdc3ca1bfdbf509bc8830e02201f834cf3a0167e33ec1aec83cacb3294046dc93e1a7a847bf043ece5583fd13e", + createdAt: "2018-09-11T17:06:42.321Z", + }, + { + id: "1723867044168113856", + version: 0, + timestamp: 46584410, + height: 87, + reward: "0", + previousBlock: "464843749694128591", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + blockSignature: + "3045022100c2b565bf8049d85b8ea0354efb0d94bc620aab6dfb973b09cad100195e6d511802200b7e9fd6a3173eeeeb8d0fa39e646ee099d141601aaf52df6241e11d759f02fb", + createdAt: "2018-09-11T17:06:50.473Z", + }, + { + id: "7628284931710202890", + version: 0, + timestamp: 46584418, + height: 88, + reward: "0", + previousBlock: "1723867044168113856", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + blockSignature: + "3044022003d908ed5e60f42501ad80303275096c21c5a41c5f2405511dac6d9e441d7d010220297dfc8f90a868827fc224e4df4fdf53c5c39a8cfaf63b7b904af69882b08475", + createdAt: "2018-09-11T17:06:58.368Z", + }, + { + id: "3531996231287449620", + version: 0, + timestamp: 46584426, + height: 89, + reward: "0", + previousBlock: "7628284931710202890", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + blockSignature: + "3045022100c52c316937e914f5adfb68efa31fa4edec3fa742009d59b2b2e02593f8f7728a02203d17f18f07f67d7ca23044b98552030ac547c4b741648ef8bdeee043ae0b34cd", + createdAt: "2018-09-11T17:07:06.264Z", + }, + { + id: "7435167259407493217", + version: 0, + timestamp: 46584434, + height: 90, + reward: "0", + previousBlock: "3531996231287449620", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + blockSignature: + "304402204b65ae72b90172be9b257300ddef0342c6817823c006bdb38b257d0b0ef1db8d0220429eaa801b85b63c858ace7f0440ff43c51157141282bca446133cca96159dff", + createdAt: "2018-09-11T17:07:14.284Z", + }, + { + id: "5656841191275027942", + version: 0, + timestamp: 46584442, + height: 91, + reward: "0", + previousBlock: "7435167259407493217", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + blockSignature: + "3045022100f500a0d127dd995bf238ebf15450155fa4be6ba59ebda61e2699282dff81c88702205d44d5e6dee2715239b11865bdc2d1482536f5600ce8a4ea5229f04781322045", + createdAt: "2018-09-11T17:07:22.266Z", + }, + { + id: "6646555502142403115", + version: 0, + timestamp: 46584450, + height: 92, + reward: "0", + previousBlock: "5656841191275027942", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + blockSignature: + "304402202b22d9186ba9b9c7fc020ca4425d3fa93a8e937683396b84fd22d1085bdb586d02201e7d474fa56e1af239d9e5e5dcb1dd53500194ee58602792bb1e6bd312777a87", + createdAt: "2018-09-11T17:07:30.377Z", + }, + { + id: "8618021774665230437", + version: 0, + timestamp: 46584458, + height: 93, + reward: "0", + previousBlock: "6646555502142403115", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + blockSignature: + "3045022100d0daa10ce4872a19b6b4bb3b3d1ed5d41221102e4aa362dfd277b59bb86e560702207316be26ab1213b808ac439dae5d1708290b2d76fba92d55ccd6afeb1523f91b", + createdAt: "2018-09-11T17:07:38.423Z", + }, + { + id: "6558209391920917685", + version: 0, + timestamp: 46584466, + height: 94, + reward: "0", + previousBlock: "8618021774665230437", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + blockSignature: + "304402205e134c3b7e2f3766e9c6d021f06e3ab537c589d27694c877aea302900c0a20e3022015813d2f698cce19a4f6d86eb2499a70f5266156f5fde9fc0edc7a8df696ec59", + createdAt: "2018-09-11T17:07:46.451Z", + }, + { + id: "18098650501718304452", + version: 0, + timestamp: 46584474, + height: 95, + reward: "0", + previousBlock: "6558209391920917685", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + blockSignature: + "3045022100d890951e03cd628ad22fea648c4e190099a730d66190b59aaa0990f77a0e6db2022075c11de03bb0e0010a51cdaca740636f54a8e74078821a7b30ec8e3ba86f89cb", + createdAt: "2018-09-11T17:07:54.409Z", + }, + { + id: "3612743398896107056", + version: 0, + timestamp: 46584482, + height: 96, + reward: "0", + previousBlock: "18098650501718304452", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + blockSignature: + "304402206de9d9ece51e025039379235e3e39ec01f9b1951b9d435c898a11fcc0e31fca10220572172f3d678ffa04f13c8c3857c315e404d3e6e7cb073406713cdf7369a20aa", + createdAt: "2018-09-11T17:08:02.374Z", + }, + { + id: "3590346281108682583", + version: 0, + timestamp: 46584490, + height: 97, + reward: "0", + previousBlock: "3612743398896107056", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + blockSignature: + "3045022100ab8b12df4b19fa8a93544fbf452dc68d6804ea6ab8e88edfa58d6d939af109f4022003554d5524d26c12a11f3328dca2045a8c562d90f26f9d28f7647bdabc928936", + createdAt: "2018-09-11T17:08:10.443Z", + }, + { + id: "5940078448948565481", + version: 0, + timestamp: 46584498, + height: 98, + reward: "0", + previousBlock: "3590346281108682583", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + blockSignature: + "30450221008f89c040a9f9eefa7e0eda2866318336d14b39ead910e4ddd697312fcbde9f22022040e86e88a39ae0546d240b1000d0962fa5ed9b7da65e13ae06bfa078abb435b6", + createdAt: "2018-09-11T17:08:18.326Z", + }, + { + id: "2958406944863963956", + version: 0, + timestamp: 46584506, + height: 99, + reward: "0", + previousBlock: "5940078448948565481", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + blockSignature: + "3044022046573056dca01fc73b635e3e33592c8c180bb0ee676414b11aeb0deac20250d302203ee93fb91ebb50d22314b059cff09c2fc367557b9b0f51fd494e9f0495abe3f3", + createdAt: "2018-09-11T17:08:26.381Z", + }, + { + id: "6161515163793239359", + version: 0, + timestamp: 46584514, + height: 100, + reward: "0", + previousBlock: "2958406944863963956", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + blockSignature: + "3045022100e32d06b7e3d4ae078cb2cb7093e2502a71732173099cb2881d91b5c4e49459d3022063df0dc42d2c3cd2854cd68881662be767b192c34ad7d4e1a50c00f34750d248", + createdAt: "2018-09-11T17:08:34.469Z", + }, +]; diff --git a/packages/core-test-utils/src/fixtures/testnet/delegates.ts b/packages/core-test-utils/src/fixtures/testnet/delegates.ts new file mode 100644 index 0000000000..7f994bcf30 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/testnet/delegates.ts @@ -0,0 +1,26 @@ +import { client, crypto } from "@arkecosystem/crypto"; + +/** + * Get the testnet genesis delegates information + * @return {Array} array of objects like { secret, publicKey, address, balance } + */ + +client.getConfigManager().setFromPreset("testnet"); + +import { secrets } from "../../config/testnet/delegates.json"; +import { transactions as genesisTransactions } from "../../config/testnet/genesisBlock.json"; + +export const delegates: any = secrets.map(secret => { + const publicKey = crypto.getKeys(secret).publicKey; + const address = crypto.getAddress(publicKey); + const balance = genesisTransactions.find( + transaction => transaction.recipientId === address && transaction.type === 0, + ).amount; + return { + secret, + passphrase: secret, // just an alias for delegate secret + publicKey, + address, + balance, + }; +}); diff --git a/packages/core-test-utils/src/fixtures/testnet/index.ts b/packages/core-test-utils/src/fixtures/testnet/index.ts new file mode 100644 index 0000000000..80a151b23a --- /dev/null +++ b/packages/core-test-utils/src/fixtures/testnet/index.ts @@ -0,0 +1,4 @@ +export * from "./delegates"; +export * from "./blocks101to155"; +export * from "./blocks2to100"; +export * from "./passphrases"; diff --git a/packages/core-test-utils/src/fixtures/testnet/passphrases.ts b/packages/core-test-utils/src/fixtures/testnet/passphrases.ts new file mode 100644 index 0000000000..84fb29d340 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/testnet/passphrases.ts @@ -0,0 +1,3 @@ +import * as delegates from "../../config/testnet/delegates.json"; + +export const delegatesSecrets = delegates.secrets; diff --git a/packages/core-test-utils/src/fixtures/unitnet/blocks.ts b/packages/core-test-utils/src/fixtures/unitnet/blocks.ts new file mode 100644 index 0000000000..cd1d7f7f55 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/unitnet/blocks.ts @@ -0,0 +1,186 @@ +import * as genesisBlock from "../../config/unitnet/genesisBlock.json"; + +export { genesisBlock }; + +const block2 = { + blockSignature: + "304402203e01a7d6fffcc23807f0504d96e0e724d435fad8d5c91702ea86fe3d48171746022000ccf36a7a480a3e50ac4e9b2bcf5bcc4e5f63d4718af356cde33c77d30bfe68", + generatorPublicKey: "02778aa3d5b332965ea2a5ef6ac479ce2478535bc681a098dff1d683ff6eccc417", + height: 2, + id: "8791037955102127843", + idHex: "7a000a7178d6b6e3", + numberOfTransactions: 10, + payloadHash: "9e796f4f211be70f3df3cbd013322d1c6d4e678168f64b363f352ab43694ff55", + payloadLength: 320, + previousBlock: "4881670189836572019", + previousBlockHex: "43bf2d2c67d29573", + reward: 0, + timestamp: 1, + totalAmount: 20, + totalFee: 100000000, + transactions: [ + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "2a4fb2f25eb201a989d5f3634c1b0d15336d9580b58dfdcbaa133c963a390d67", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "304402203994e720906016de43beb11d39f82b5b3325eac56dcffed5b433982aea5fcb9f0220179c0e89353709a4715ec7f1b37081e47d976fae5ebf96f9d86e48e7bc4ba7bf", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 5", + vendorFieldHex: "54657374205472616e73616374696f6e2035", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "2a64b5fe72fb061c91ecb159e1d8d40176e06e9292d85492c8a49971add6b849", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "3044022023555b3becfa444fcbe68f39bd6d38e7b41546aed89a57ccb5078cea8e35d9cf02205a86d2a5c2bb9cfb0e72daab15762b4d8dd64c885d81c77175a169376ec6cae5", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 10", + vendorFieldHex: "54657374205472616e73616374696f6e203130", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "37ae8d0517fb5e65c27d85cd4c7211e4238854bf115f9b94879ddd63901276fd", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "304402201034be20d5fe44a23237e522e1618a2bb0dc53d2c0c38e18759e853c62a9bb8c02200d548c6ca9fcfc6cbdb85ad8c6ca58ca8c19580f108a1b3c6b5de1aa7a7e5745", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 8", + vendorFieldHex: "54657374205472616e73616374696f6e2038", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "40562743b6826a054809e9ab7924f67a3e58d5cf4beed80d3e5cfa344f59bb70", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "304402207e7aacb0e2ad13a0efe0e2528420f3599462d4f1fe487ad0916fbff3e4511a05022021370c03734b4e744d3f411399ac59581bfcf8b6aa13cfbfe6c2dbb359e24c38", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 3", + vendorFieldHex: "54657374205472616e73616374696f6e2033", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "6430c39e8716c94cfa86c615e8cfede4e734e5722c454b7cc46833ff2e27898b", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "3045022100e8dd3defac4355b7f770a59b5e9fa6a18380b0202aa249ed7ef53cdb2fd1b480022024fd05b6ec595a78ee1c6241a85367a06b07fc9ac9f3e8c37bb6acf160f71a8f", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 1", + vendorFieldHex: "54657374205472616e73616374696f6e2031", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "84654b97e5e1de1f750e98cb0f9b54561326d95c8ebd385579d28dbaf6b6f22c", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "3045022100bfbf22680ea697e6945f3ebe5a4969f57553e216078c01c6099556d8929ee7af02201f45d93c2835a4945b0db92cc7b048f3e2d3faae20998830e77cd3956938c06e", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 4", + vendorFieldHex: "54657374205472616e73616374696f6e2034", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "8780a5028b92d7978c135be25abc0f7feb5e4c3c6b921e350d2efae74bc93b20", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "3045022100b54f50019c9a88b19d6fd818c28fd599c1ced9642cf4f6dbcf27808ffecd16c402203592cf05dac4f391b63cb329b9912162160d3fe7b9d9431f9da8db3baaecc3e2", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 2", + vendorFieldHex: "54657374205472616e73616374696f6e2032", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "927ffb96dbc41f5ca986961393b148d30b8824d807d182d86d5e902325dd31d0", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "3044022063e545f2d7e76c0462ff77a15a37914dfcbd7fadfbcff4aaba6cb14266914401022062ea215e62c78ef0e52bb2d3772fa8741629b83e9588050f0442982641be21a1", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 9", + vendorFieldHex: "54657374205472616e73616374696f6e2039", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "bb80344684fb879bd11f162525618c72cea2e2aa9b09244dd7d07ea1121f550a", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "3045022100f34cb7450d21b4831294f2d86ebff36645aa90fe5c8e031025a8c210d8cbcd6f022031b53ad0a0163859a72c659ac45762980de5aca47e60ef1d9aea6c08d1c39cd7", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 6", + vendorFieldHex: "54657374205472616e73616374696f6e2036", + version: 1, + }, + { + amount: 2, + expiration: 0, + fee: 10000000, + id: "d0920f9e20f3946ea2530db4d9342b66848e759dc0d0ed9e4c9d3db035ffdb51", + network: 23, + recipientId: "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + senderPublicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + signature: + "304402207154f9daff22f4c91035d36fd29056e93f0914bd4b5a2ba30a009cd26f34835702206b77627e5d2f81d066d4c250cb29a6d6b05c5dea2396edcbd98f18a3cf807350", + timestamp: 58557033, + type: 0, + vendorField: "Test Transaction 7", + vendorFieldHex: "54657374205472616e73616374696f6e2037", + version: 1, + }, + ], + version: 0, +}; + +export { block2 }; diff --git a/packages/core-test-utils/src/fixtures/unitnet/delegates.ts b/packages/core-test-utils/src/fixtures/unitnet/delegates.ts new file mode 100644 index 0000000000..9e93ee1775 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/unitnet/delegates.ts @@ -0,0 +1,26 @@ +import { client, crypto } from "@arkecosystem/crypto"; + +/** + * Get the unitnet genesis delegates information + * @return {Array} array of objects like { secret, publicKey, address, balance } + */ + +client.getConfigManager().setFromPreset("unitnet"); + +import { secrets } from "../../config/unitnet/delegates.json"; +import { transactions as genesisTransactions } from "../../config/unitnet/genesisBlock.json"; + +export const delegates: any = secrets.map(secret => { + const publicKey = crypto.getKeys(secret).publicKey; + const address = crypto.getAddress(publicKey); + const balance = genesisTransactions.find( + transaction => transaction.recipientId === address && transaction.type === 0, + ).amount; + return { + secret, + passphrase: secret, // just an alias for delegate secret + publicKey, + address, + balance, + }; +}); diff --git a/packages/core-test-utils/src/fixtures/unitnet/index.ts b/packages/core-test-utils/src/fixtures/unitnet/index.ts new file mode 100644 index 0000000000..2b18cca44f --- /dev/null +++ b/packages/core-test-utils/src/fixtures/unitnet/index.ts @@ -0,0 +1,3 @@ +export * from "./delegates"; +export * from "./wallets"; +export * from "./blocks"; diff --git a/packages/core-test-utils/src/fixtures/unitnet/wallets.ts b/packages/core-test-utils/src/fixtures/unitnet/wallets.ts new file mode 100644 index 0000000000..a91f605701 --- /dev/null +++ b/packages/core-test-utils/src/fixtures/unitnet/wallets.ts @@ -0,0 +1,2 @@ +export { wallets } from "../../config/unitnet/wallets.json"; +export { wallets2ndSig } from "../../config/unitnet/wallets2ndSig.json"; diff --git a/packages/core-test-utils/src/generators/index.ts b/packages/core-test-utils/src/generators/index.ts new file mode 100644 index 0000000000..1416ec6ab3 --- /dev/null +++ b/packages/core-test-utils/src/generators/index.ts @@ -0,0 +1,2 @@ +export * from "./transactions"; +export * from "./wallets"; diff --git a/packages/core-test-utils/src/generators/transactions/delegate.ts b/packages/core-test-utils/src/generators/transactions/delegate.ts new file mode 100644 index 0000000000..d598303b61 --- /dev/null +++ b/packages/core-test-utils/src/generators/transactions/delegate.ts @@ -0,0 +1,29 @@ +import { constants } from "@arkecosystem/crypto"; +import { generateTransaction } from "./transaction"; + +const { DelegateRegistration } = constants.TransactionTypes; + +export const generateDelegateRegistration = ( + network, + passphrase, + quantity: number = 1, + getStruct: boolean = false, + username?: string, + fee?: number, +) => { + if (Array.isArray(passphrase)) { + return passphrase.map( + p => generateTransaction(network, DelegateRegistration, p, username, undefined, 1, getStruct, fee)[0], + ); + } + return generateTransaction( + network, + DelegateRegistration, + passphrase, + username, + undefined, + quantity, + getStruct, + fee, + ); +}; diff --git a/packages/core-test-utils/src/generators/transactions/index.ts b/packages/core-test-utils/src/generators/transactions/index.ts new file mode 100644 index 0000000000..4244c83812 --- /dev/null +++ b/packages/core-test-utils/src/generators/transactions/index.ts @@ -0,0 +1,5 @@ +export * from "./delegate"; +export * from "./signature"; +export * from "./transaction"; +export * from "./transfer"; +export * from "./vote"; diff --git a/packages/core-test-utils/src/generators/transactions/signature.ts b/packages/core-test-utils/src/generators/transactions/signature.ts new file mode 100644 index 0000000000..2874f1bc4b --- /dev/null +++ b/packages/core-test-utils/src/generators/transactions/signature.ts @@ -0,0 +1,19 @@ +import { constants } from "@arkecosystem/crypto"; +import { generateTransaction } from "./transaction"; + +const { SecondSignature } = constants.TransactionTypes; + +export const generateSecondSignature = ( + network, + passphrase, + quantity: number = 10, + getStruct: boolean = false, + fee?: number, +) => { + if (Array.isArray(passphrase)) { + return passphrase.map( + p => generateTransaction(network, SecondSignature, p, undefined, undefined, 1, getStruct, fee)[0], + ); + } + return generateTransaction(network, SecondSignature, passphrase, undefined, undefined, quantity, getStruct, fee); +}; diff --git a/packages/core-test-utils/src/generators/transactions/transaction.ts b/packages/core-test-utils/src/generators/transactions/transaction.ts new file mode 100644 index 0000000000..7757385296 --- /dev/null +++ b/packages/core-test-utils/src/generators/transactions/transaction.ts @@ -0,0 +1,95 @@ +import { client, constants, crypto } from "@arkecosystem/crypto"; +import superheroes from "superheroes"; +import { delegatesSecrets } from "../../fixtures/testnet/passphrases"; + +const defaultPassphrase = delegatesSecrets[0]; +const { Transfer, SecondSignature, DelegateRegistration, Vote } = constants.TransactionTypes; + +export const generateTransaction = ( + network, + type, + passphrase, + addressOrPublicKeyOrUsername, + amount: number = 2, + quantity: number = 10, + getStruct: boolean = false, + fee?: number, +) => { + network = network || "testnet"; + type = type || Transfer; + passphrase = passphrase || defaultPassphrase; + + if (!["mainnet", "devnet", "testnet", "unitnet"].includes(network)) { + throw new Error("Invalid network"); + } + + if (![Transfer, SecondSignature, DelegateRegistration, Vote].includes(type)) { + throw new Error("Invalid transaction type"); + } + + let secondPassphrase; + if (typeof passphrase === "object") { + secondPassphrase = passphrase.secondPassphrase; + passphrase = passphrase.passphrase; + } + + client.getConfigManager().setFromPreset(network); + + const transactions = []; + for (let i = 0; i < quantity; i++) { + let builder: any = client.getBuilder(); + switch (type) { + case Transfer: { + if (!addressOrPublicKeyOrUsername) { + addressOrPublicKeyOrUsername = crypto.getAddress(crypto.getKeys(passphrase).publicKey); + } + builder = builder + .transfer() + .recipientId(addressOrPublicKeyOrUsername) + .amount(amount) + .vendorField(`Test Transaction ${i + 1}`); + break; + } + case SecondSignature: { + builder = builder.secondSignature().signatureAsset(passphrase); + break; + } + case DelegateRegistration: { + const username = + addressOrPublicKeyOrUsername || + superheroes + .random() + .toLowerCase() + .replace(/[^a-z0-9]/g, "_") + .substring(0, 20); + builder = builder.delegateRegistration().usernameAsset(username); + break; + } + case Vote: { + if (!addressOrPublicKeyOrUsername) { + addressOrPublicKeyOrUsername = crypto.getKeys(passphrase).publicKey; + } + builder = builder.vote().votesAsset([`+${addressOrPublicKeyOrUsername}`]); + break; + } + default: { + throw new Error("Invalid transaction type"); + } + } + + if (fee || fee === 0) { + builder = builder.fee(fee); + } + + builder = builder.sign(passphrase); + + if (secondPassphrase) { + builder = builder.secondSign(secondPassphrase); + } + const tx = getStruct ? builder.getStruct() : builder.build(); + + transactions.push(tx); + } + + return transactions; +}; diff --git a/packages/core-test-utils/src/generators/transactions/transfer.ts b/packages/core-test-utils/src/generators/transactions/transfer.ts new file mode 100644 index 0000000000..742f87f77a --- /dev/null +++ b/packages/core-test-utils/src/generators/transactions/transfer.ts @@ -0,0 +1,21 @@ +import { constants } from "@arkecosystem/crypto"; +import { generateTransaction } from "./transaction"; + +const { Transfer } = constants.TransactionTypes; + +export const generateTransfers = ( + network: string, + passphrase: any = "secret passphrase", + address?: string, + amount: number = 2, + quantity: number = 10, + getStruct: boolean = false, + fee?: number, +) => { + if (Array.isArray(passphrase)) { + return passphrase.map( + p => generateTransaction(network, Transfer, passphrase, address, amount, 1, getStruct, fee)[0], + ); + } + return generateTransaction(network, Transfer, passphrase, address, amount, quantity, getStruct, fee); +}; diff --git a/packages/core-test-utils/src/generators/transactions/vote.ts b/packages/core-test-utils/src/generators/transactions/vote.ts new file mode 100644 index 0000000000..f05b9a73ed --- /dev/null +++ b/packages/core-test-utils/src/generators/transactions/vote.ts @@ -0,0 +1,18 @@ +import { constants } from "@arkecosystem/crypto"; +import { generateTransaction } from "./transaction"; + +const { Vote } = constants.TransactionTypes; + +export const generateVote = ( + network, + passphrase, + publicKey, + quantity: number = 10, + getStruct: boolean = false, + fee?: number, +) => { + if (Array.isArray(passphrase)) { + return passphrase.map(p => generateTransaction(network, Vote, p, publicKey, undefined, 1, getStruct, fee)[0]); + } + return generateTransaction(network, Vote, passphrase, publicKey, undefined, quantity, getStruct, fee); +}; diff --git a/packages/core-test-utils/src/generators/wallets.ts b/packages/core-test-utils/src/generators/wallets.ts new file mode 100644 index 0000000000..b779796bb7 --- /dev/null +++ b/packages/core-test-utils/src/generators/wallets.ts @@ -0,0 +1,22 @@ +import { client, crypto } from "@arkecosystem/crypto"; +import bip39 from "bip39"; + +export const generateWallets = (network, quantity = 10) => { + network = network || "testnet"; + if (!["testnet", "mainnet", "devnet", "unitnet"].includes(network)) { + throw new Error("Invalid network"); + } + + client.getConfigManager().setFromPreset(network); + + const wallets = []; + for (let i = 0; i < quantity; i++) { + const passphrase = bip39.generateMnemonic(); + const publicKey = crypto.getKeys(passphrase).publicKey; + const address = crypto.getAddress(publicKey); + + wallets.push({ address, passphrase, publicKey }); + } + + return wallets; +}; diff --git a/packages/core-test-utils/src/helpers/api.ts b/packages/core-test-utils/src/helpers/api.ts new file mode 100644 index 0000000000..08720ce440 --- /dev/null +++ b/packages/core-test-utils/src/helpers/api.ts @@ -0,0 +1,52 @@ +import "jest-extended"; + +export class ApiHelpers { + public static async request(server, method, url, headers, params = {}) { + // Build URL params from _params_ object for GET / DELETE requests + const getParams = Object.entries(params) + .map(([key, val]) => `${key}=${val}`) + .join("&"); + + // Injecting the request into Hapi server instead of using axios + const injectOptions = { + method, + url: ["GET", "DELETE"].includes(method) ? `${url}?${getParams}` : url, + headers, + payload: ["GET", "DELETE"].includes(method) ? {} : params, + }; + + const response = await server.inject(injectOptions); + const data = typeof response.result === "string" ? JSON.parse(response.result) : response.result; + Object.assign(response, { data, status: response.statusCode }); + return response; + } + + public static expectJson(response) { + expect(response.data).toBeObject(); + } + + public static expectStatus(response, code) { + expect(response.status).toBe(code); + } + + public static expectResource(response) { + expect(response.data.data).toBeObject(); + } + + public static expectCollection(response) { + expect(Array.isArray(response.data.data)).toBe(true); + } + + public static expectSuccessful(response, statusCode = 200) { + this.expectStatus(response, statusCode); + this.expectJson(response); + } + + public static expectError(response, statusCode = 404) { + this.expectStatus(response, statusCode); + this.expectJson(response); + expect(response.data.statusCode).toBeNumber(); + expect(response.data.error).toBeString(); + expect(response.data.message).toBeString(); + } +} diff --git a/packages/core-test-utils/src/helpers/blockchain.ts b/packages/core-test-utils/src/helpers/blockchain.ts new file mode 100644 index 0000000000..649d3c91bd --- /dev/null +++ b/packages/core-test-utils/src/helpers/blockchain.ts @@ -0,0 +1,17 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, TransactionPool } from "@arkecosystem/core-interfaces"; +export const resetBlockchain = async () => { + // Resets everything so that it can be used in beforeAll to start clean a test suite + // Now resets: blocks (remove blocks other than genesis), transaction pool + // TODO: reset rounds, transactions in db... + + // reset to block height 1 + const blockchain = app.resolvePlugin("blockchain"); + const height = blockchain.getLastBlock().data.height; + if (height) { + await blockchain.removeBlocks(height - 1); + } + + const transactionPool = app.resolvePlugin("transactionPool"); + transactionPool.flush(); +}; diff --git a/packages/core-test-utils/src/helpers/container.ts b/packages/core-test-utils/src/helpers/container.ts new file mode 100644 index 0000000000..d927478640 --- /dev/null +++ b/packages/core-test-utils/src/helpers/container.ts @@ -0,0 +1,72 @@ +import { app } from "@arkecosystem/core-container"; +import { Container } from "@arkecosystem/core-interfaces"; +import "@arkecosystem/core-jest-matchers"; +import { asValue } from "awilix"; +import isString from "lodash/isString"; +import * as path from "path"; + +export async function setUpContainer(options: any): Promise { + options.network = options.network || "testnet"; + await app.setUp( + "2.1.0", + { + data: options.data || "~/.core", + config: options.config ? options.config : path.resolve(__dirname, `../config/${options.network}`), + token: options.token || "ark", + network: options.network, + }, + options, + ); + return app; +} + +// copied from core-container registrars/plugin and slightly modified +export async function registerWithContainer(plugin, options = {}) { + if (!plugin.register) { + return; + } + + const name = plugin.name || plugin.pkg.name; + const version = plugin.version || plugin.pkg.version; + const defaults = plugin.defaults || plugin.pkg.defaults; + const alias = plugin.alias || plugin.pkg.alias; + + options = applyToDefaults(name, defaults, options); + + const pluginRegistered = await plugin.register(app, options || {}); + app.register( + alias || name, + asValue({ + name, + version, + plugin: pluginRegistered, + options, + }), + ); + + return pluginRegistered; +} + +// copied from core-container registrars/plugin and slightly modified +function applyToDefaults(name, defaults, options) { + if (defaults) { + options = Object.assign(defaults, options); + } + + return castOptions(options); +} + +// copied from core-container registrars/plugin +function castOptions(options) { + const blacklist: any = []; + const regex = new RegExp(/^\d+$/); + + Object.keys(options).forEach(key => { + const value = options[key]; + if (isString(value) && !blacklist.includes(key) && regex.test(value)) { + options[key] = +value; + } + }); + + return options; +} diff --git a/packages/core-test-utils/src/helpers/index.ts b/packages/core-test-utils/src/helpers/index.ts new file mode 100644 index 0000000000..f46448d585 --- /dev/null +++ b/packages/core-test-utils/src/helpers/index.ts @@ -0,0 +1,3 @@ +export * from "./api"; +export * from "./blockchain"; +export * from "./container"; diff --git a/packages/core-test-utils/src/index.ts b/packages/core-test-utils/src/index.ts new file mode 100644 index 0000000000..1edd203f01 --- /dev/null +++ b/packages/core-test-utils/src/index.ts @@ -0,0 +1,7 @@ +import "@arkecosystem/core-jest-matchers"; + +import * as fixtures from "./fixtures"; +import * as generators from "./generators"; +import * as helpers from "./helpers"; + +export { fixtures, generators, helpers }; diff --git a/packages/core-test-utils/tsconfig.json b/packages/core-test-utils/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-test-utils/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-tester-cli/CHANGELOG.md b/packages/core-tester-cli/CHANGELOG.md deleted file mode 100644 index dd6949dd30..0000000000 --- a/packages/core-tester-cli/CHANGELOG.md +++ /dev/null @@ -1,37 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Option for `smartBridge` values -- Default values for ports -- Multi Signature support -- Vote support -- Flood option - -### Changed - -- Moved from modules to classes to reduce duplication -- Use the v2 API for any API calls -- Handle duplicate transaction IDs -- Dropped node.js 9 as minimum requirement in favour of node.js 10 -- Use human-readable values as input for amounts and fees - -### Fixed - -- Undefined passphrases for the `overridingPassphrase` option in transfers -- Display of human-readable numbers - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-tester-cli/LICENSE b/packages/core-tester-cli/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-tester-cli/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-tester-cli/README.md b/packages/core-tester-cli/README.md index 878f26dd44..7242fb5d9c 100644 --- a/packages/core-tester-cli/README.md +++ b/packages/core-tester-cli/README.md @@ -14,9 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Alex Barnsley](https://github.com/alexbarnsley) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-tester-cli/__tests__/.gitkeep b/packages/core-tester-cli/__tests__/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-1.json b/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-1.json index 00b929e29f..259580076b 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-1.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-1.json @@ -1,90 +1,90 @@ { - "meta": { - "count": 3, - "pageCount": 2, - "totalCount": 6, - "next": "/api/v2/delegates?page=2", - "previous": null, - "self": "/api/v2/delegates?page=1&limit=100", - "first": "/api/v2/delegates?page=1&limit=100", - "last": "/api/v2/delegates?page=2&limit=100" - }, - "data": [ - { - "username": "arkx", - "address": "DNjuJEDQkhrJ7cA9FZ2iVXt5anYiM8Jtc9", - "publicKey": "03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec", - "votes": 43424464264923, - "rank": 1, - "blocks": { - "produced": 15605, - "missed": 709, - "last": { - "id": "9508080167740922063", - "timestamp": { - "epoch": 52479984, - "unix": 1542581184, - "human": "2018-11-18T22:46:24Z" - } - } - }, - "production": { - "approval": 0.34, - "productivity": 95.65 - }, - "forged": { - "fees": 74694705072, - "rewards": 3078200000000, - "total": 3152894705072 - } + "meta": { + "count": 3, + "pageCount": 2, + "totalCount": 6, + "next": "/api/v2/delegates?page=2", + "previous": null, + "self": "/api/v2/delegates?page=1&limit=100", + "first": "/api/v2/delegates?page=1&limit=100", + "last": "/api/v2/delegates?page=2&limit=100" }, - { - "username": "obiwan", - "address": "D71HuRcEGH8q15hZnaqZ1oSfgZjqYscgav", - "publicKey": "03a2f2ce2aebd426d353bf3abf2c00a85e3122070bbeaa04b73eba2a6119dbc620", - "votes": 7827580000000, - "rank": 2, - "blocks": { - "produced": 15104, - "missed": 493, - "last": { - "id": "6408769526029163610", - "timestamp": { - "epoch": 52479946, - "unix": 1542581146, - "human": "2018-11-18T22:45:46Z" - } + "data": [ + { + "username": "arkx", + "address": "DNjuJEDQkhrJ7cA9FZ2iVXt5anYiM8Jtc9", + "publicKey": "03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec", + "votes": 43424464264923, + "rank": 1, + "blocks": { + "produced": 15605, + "missed": 709, + "last": { + "id": "9508080167740922063", + "timestamp": { + "epoch": 52479984, + "unix": 1542581184, + "human": "2018-11-18T22:46:24Z" + } + } + }, + "production": { + "approval": 0.34, + "productivity": 95.65 + }, + "forged": { + "fees": 74694705072, + "rewards": 3078200000000, + "total": 3152894705072 + } + }, + { + "username": "obiwan", + "address": "D71HuRcEGH8q15hZnaqZ1oSfgZjqYscgav", + "publicKey": "03a2f2ce2aebd426d353bf3abf2c00a85e3122070bbeaa04b73eba2a6119dbc620", + "votes": 7827580000000, + "rank": 2, + "blocks": { + "produced": 15104, + "missed": 493, + "last": { + "id": "6408769526029163610", + "timestamp": { + "epoch": 52479946, + "unix": 1542581146, + "human": "2018-11-18T22:45:46Z" + } + } + }, + "production": { + "approval": 0.06, + "productivity": 96.84 + }, + "forged": { + "fees": 50980000000, + "rewards": 2986600000000, + "total": 3037580000000 + } + }, + { + "username": "yo", + "address": "DTFWtjocUWuiwqxcPh2Qi6SWU9CJKvPpsD", + "publicKey": "02e345079aca0567db96ec0ba3acf859b7cfd15134a855671f9c0fe8b1173767bd", + "votes": 6343253260000, + "rank": 3, + "blocks": { + "produced": 4165, + "missed": 785 + }, + "production": { + "approval": 0.05, + "productivity": 84.14 + }, + "forged": { + "fees": 3550000000, + "rewards": 833000000000, + "total": 836550000000 + } } - }, - "production": { - "approval": 0.06, - "productivity": 96.84 - }, - "forged": { - "fees": 50980000000, - "rewards": 2986600000000, - "total": 3037580000000 - } - }, - { - "username": "yo", - "address": "DTFWtjocUWuiwqxcPh2Qi6SWU9CJKvPpsD", - "publicKey": "02e345079aca0567db96ec0ba3acf859b7cfd15134a855671f9c0fe8b1173767bd", - "votes": 6343253260000, - "rank": 3, - "blocks": { - "produced": 4165, - "missed": 785 - }, - "production": { - "approval": 0.05, - "productivity": 84.14 - }, - "forged": { - "fees": 3550000000, - "rewards": 833000000000, - "total": 836550000000 - } - } - ] + ] } diff --git a/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-2.json b/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-2.json index ac81fa58f2..c3d3a340e1 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-2.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/delegates-page-2.json @@ -1,82 +1,82 @@ { - "meta": { - "count": 3, - "pageCount": 2, - "totalCount": 6, - "next": null, - "previous": "/api/v2/delegates?page=1", - "self": "/api/v2/delegates?page=2&limit=100", - "first": "/api/v2/delegates?page=1&limit=100", - "last": "/api/v2/delegates?page=2&limit=100" - }, - "data": [ - { - "username": "cam", - "address": "DACFobAjNEKsc1CtPSMyp5uA4wp6uC3fgC", - "publicKey": "032cfbb18f4e49952c6d6475e8adc6d0cba00b81ef6606cc4927b78c6c50558beb", - "votes": 6070791889600, - "rank": 4, - "blocks": { - "produced": 1551, - "missed": 180, - "last": { - "id": "3658545612136752676", - "timestamp": { - "epoch": 52479330, - "unix": 1542580530, - "human": "2018-11-18T22:35:30Z" - } - } - }, - "production": { - "approval": 0.05, - "productivity": 89.6 - }, - "forged": { - "fees": 5060000000, - "rewards": 310200000000, - "total": 315260000000 - } - }, - { - "username": "arklabs", - "address": "D9z1ZUcCUHHeiFLEtYhwk6z8kmbCCHTRFd", - "publicKey": "0394685435d7331d178effe91d6b101ce7c4a6e03d2a96cfd5be1fffb0ae156e58", - "votes": 4500000000000, - "rank": 5, - "blocks": { - "produced": 14572, - "missed": 772 - }, - "production": { - "approval": 0.04, - "productivity": 94.97 - }, - "forged": { - "fees": 29670020960, - "rewards": 2884000000000, - "total": 2913670020960 - } + "meta": { + "count": 3, + "pageCount": 2, + "totalCount": 6, + "next": null, + "previous": "/api/v2/delegates?page=1", + "self": "/api/v2/delegates?page=2&limit=100", + "first": "/api/v2/delegates?page=1&limit=100", + "last": "/api/v2/delegates?page=2&limit=100" }, - { - "username": "jeremig", - "address": "D7h9uZHJAA37j9UnSs9zbhkEv6QtKGrSGL", - "publicKey": "02c6a267650795a4f4bd95e32a65aa5aa8e384d218e8be7b1f9c3a5f21661e85db", - "votes": 4013860773791, - "rank": 6, - "blocks": { - "produced": 2650, - "missed": 950 - }, - "production": { - "approval": 0.03, - "productivity": 73.61 - }, - "forged": { - "fees": 3000000000, - "rewards": 530000000000, - "total": 533000000000 - } - } - ] + "data": [ + { + "username": "cam", + "address": "DACFobAjNEKsc1CtPSMyp5uA4wp6uC3fgC", + "publicKey": "032cfbb18f4e49952c6d6475e8adc6d0cba00b81ef6606cc4927b78c6c50558beb", + "votes": 6070791889600, + "rank": 4, + "blocks": { + "produced": 1551, + "missed": 180, + "last": { + "id": "3658545612136752676", + "timestamp": { + "epoch": 52479330, + "unix": 1542580530, + "human": "2018-11-18T22:35:30Z" + } + } + }, + "production": { + "approval": 0.05, + "productivity": 89.6 + }, + "forged": { + "fees": 5060000000, + "rewards": 310200000000, + "total": 315260000000 + } + }, + { + "username": "arklabs", + "address": "D9z1ZUcCUHHeiFLEtYhwk6z8kmbCCHTRFd", + "publicKey": "0394685435d7331d178effe91d6b101ce7c4a6e03d2a96cfd5be1fffb0ae156e58", + "votes": 4500000000000, + "rank": 5, + "blocks": { + "produced": 14572, + "missed": 772 + }, + "production": { + "approval": 0.04, + "productivity": 94.97 + }, + "forged": { + "fees": 29670020960, + "rewards": 2884000000000, + "total": 2913670020960 + } + }, + { + "username": "jeremig", + "address": "D7h9uZHJAA37j9UnSs9zbhkEv6QtKGrSGL", + "publicKey": "02c6a267650795a4f4bd95e32a65aa5aa8e384d218e8be7b1f9c3a5f21661e85db", + "votes": 4013860773791, + "rank": 6, + "blocks": { + "produced": 2650, + "missed": 950 + }, + "production": { + "approval": 0.03, + "productivity": 73.61 + }, + "forged": { + "fees": 3000000000, + "rewards": 530000000000, + "total": 533000000000 + } + } + ] } diff --git a/packages/core-tester-cli/__tests__/__fixtures__/transaction-1.json b/packages/core-tester-cli/__tests__/__fixtures__/transaction-1.json index 3304cc29cd..f05a5b2168 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/transaction-1.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/transaction-1.json @@ -1,17 +1,17 @@ { - "id": "02c0061df475cceb6e80bc2c5ae1719960bbc214fbf662620cc2491f0f06e51b", - "blockId": "2099984314112825055", - "type": 0, - "amount": 2543518349, - "fee": 10000000, - "sender": "DGuuCwJYoEheBAC4PZTBSBasaDHxg2e6j7", - "recipient": "D7seWn8JLVwX4nHd9hh2Lf7gvZNiRJ7qLk", - "signature": "3044022065386285cb4dbcd042ac370e1a9f3034b7cdf68d9b97e8e4b0cb219087cbf79f02202e0fd295f3633c1c0503b892643fe212a22a1d290036227e0e9ba61ac2ba6677", - "vendorField": "darkzen reward", - "confirmations": 13, - "timestamp": { - "epoch": 52481490, - "unix": 1542582690, - "human": "2018-11-18T23:11:30Z" - } + "id": "02c0061df475cceb6e80bc2c5ae1719960bbc214fbf662620cc2491f0f06e51b", + "blockId": "2099984314112825055", + "type": 0, + "amount": 2543518349, + "fee": 10000000, + "sender": "DGuuCwJYoEheBAC4PZTBSBasaDHxg2e6j7", + "recipient": "D7seWn8JLVwX4nHd9hh2Lf7gvZNiRJ7qLk", + "signature": "3044022065386285cb4dbcd042ac370e1a9f3034b7cdf68d9b97e8e4b0cb219087cbf79f02202e0fd295f3633c1c0503b892643fe212a22a1d290036227e0e9ba61ac2ba6677", + "vendorField": "darkzen reward", + "confirmations": 13, + "timestamp": { + "epoch": 52481490, + "unix": 1542582690, + "human": "2018-11-18T23:11:30Z" + } } diff --git a/packages/core-tester-cli/__tests__/__fixtures__/transaction-response-1.json b/packages/core-tester-cli/__tests__/__fixtures__/transaction-response-1.json index f0fe3da998..ceb03ad90f 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/transaction-response-1.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/transaction-response-1.json @@ -1,10 +1,6 @@ { - "accept": [ - "4743f493f3f0e119b8330ea95de500e9823e51802ca3391ece96c326634e183a" - ], - "excess": [], - "invalid": [], - "broadcast": [ - "4743f493f3f0e119b8330ea95de500e9823e51802ca3391ece96c326634e183a" - ] + "accept": ["4743f493f3f0e119b8330ea95de500e9823e51802ca3391ece96c326634e183a"], + "excess": [], + "invalid": [], + "broadcast": ["4743f493f3f0e119b8330ea95de500e9823e51802ca3391ece96c326634e183a"] } diff --git a/packages/core-tester-cli/__tests__/__fixtures__/voters-page-1.json b/packages/core-tester-cli/__tests__/__fixtures__/voters-page-1.json index 264578c79b..4d903ed48a 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/voters-page-1.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/voters-page-1.json @@ -1,38 +1,38 @@ { - "meta": { - "count": 3, - "pageCount": 2, - "totalCount": 6, - "next": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100", - "previous": null, - "self": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", - "first": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", - "last": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100" - }, - "data": [ - { - "address": "D598HVxTLLyjnpvpj1voo5CePGYxpw9kvy", - "publicKey": "033c0b8ca9e01cacd539cb512ac8d576a37c287fea8fd769fc3cad46889e5a708c", - "username": null, - "secondPublicKey": null, - "balance": 100000000, - "isDelegate": false + "meta": { + "count": 3, + "pageCount": 2, + "totalCount": 6, + "next": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100", + "previous": null, + "self": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", + "first": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", + "last": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100" }, - { - "address": "D59atbRCJ5UijDPMHj9opgU3gxbffzFRLe", - "publicKey": "026554b83729a2f2928f47fb7f0ce55cf462c7828792f9216bce60851eda938d98", - "username": null, - "secondPublicKey": null, - "balance": 100000000, - "isDelegate": false - }, - { - "address": "D59r2xBatFUjRY9BMwjuUdXNBSCraVGKWh", - "publicKey": "03e6c5aa5835eec16fc48a913f2c9622c69b2ed7eb828b5b892509934031e985e5", - "username": null, - "secondPublicKey": null, - "balance": 100000000, - "isDelegate": false - } - ] + "data": [ + { + "address": "D598HVxTLLyjnpvpj1voo5CePGYxpw9kvy", + "publicKey": "033c0b8ca9e01cacd539cb512ac8d576a37c287fea8fd769fc3cad46889e5a708c", + "username": null, + "secondPublicKey": null, + "balance": 100000000, + "isDelegate": false + }, + { + "address": "D59atbRCJ5UijDPMHj9opgU3gxbffzFRLe", + "publicKey": "026554b83729a2f2928f47fb7f0ce55cf462c7828792f9216bce60851eda938d98", + "username": null, + "secondPublicKey": null, + "balance": 100000000, + "isDelegate": false + }, + { + "address": "D59r2xBatFUjRY9BMwjuUdXNBSCraVGKWh", + "publicKey": "03e6c5aa5835eec16fc48a913f2c9622c69b2ed7eb828b5b892509934031e985e5", + "username": null, + "secondPublicKey": null, + "balance": 100000000, + "isDelegate": false + } + ] } diff --git a/packages/core-tester-cli/__tests__/__fixtures__/voters-page-2.json b/packages/core-tester-cli/__tests__/__fixtures__/voters-page-2.json index 114b36ccdf..bc96dd98b3 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/voters-page-2.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/voters-page-2.json @@ -1,38 +1,38 @@ { - "meta": { - "count": 3, - "pageCount": 2, - "totalCount": 6, - "next": null, - "previous": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", - "self": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100", - "first": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", - "last": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100" - }, - "data": [ - { - "address": "D5A1b37Yr291pxwomH2gHZeekoks2tLwhk", - "publicKey": "023f3312ca84373adf6a219eb4bc70545263338810afc33331a86634d3d80ae72d", - "username": null, - "secondPublicKey": null, - "balance": 100000000, - "isDelegate": false + "meta": { + "count": 3, + "pageCount": 2, + "totalCount": 6, + "next": null, + "previous": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", + "self": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100", + "first": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=1&limit=100", + "last": "/api/v2/delegates/03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec/voters?page=2&limit=100" }, - { - "address": "D5a3upUoaEfLUkwCVkspJNQJq4UCR1ytth", - "publicKey": "03bf5acdc2a8dfd0f46a6353dc39fcc1ee896232f3a470e196dfbc4c9f7cbf6318", - "username": null, - "secondPublicKey": null, - "balance": 100000000, - "isDelegate": false - }, - { - "address": "D5a9L47PQEKk9JCnYzhSoXFf9wAn1k8Y3E", - "publicKey": "02cf021ef0687ca93a102244b88b733cec0851875993b040aaaa72a95eb3927f6d", - "username": null, - "secondPublicKey": null, - "balance": 100000000, - "isDelegate": false - } - ] + "data": [ + { + "address": "D5A1b37Yr291pxwomH2gHZeekoks2tLwhk", + "publicKey": "023f3312ca84373adf6a219eb4bc70545263338810afc33331a86634d3d80ae72d", + "username": null, + "secondPublicKey": null, + "balance": 100000000, + "isDelegate": false + }, + { + "address": "D5a3upUoaEfLUkwCVkspJNQJq4UCR1ytth", + "publicKey": "03bf5acdc2a8dfd0f46a6353dc39fcc1ee896232f3a470e196dfbc4c9f7cbf6318", + "username": null, + "secondPublicKey": null, + "balance": 100000000, + "isDelegate": false + }, + { + "address": "D5a9L47PQEKk9JCnYzhSoXFf9wAn1k8Y3E", + "publicKey": "02cf021ef0687ca93a102244b88b733cec0851875993b040aaaa72a95eb3927f6d", + "username": null, + "secondPublicKey": null, + "balance": 100000000, + "isDelegate": false + } + ] } diff --git a/packages/core-tester-cli/__tests__/__fixtures__/wallet-1.json b/packages/core-tester-cli/__tests__/__fixtures__/wallet-1.json index cf8846d5d8..725ccf7a4b 100644 --- a/packages/core-tester-cli/__tests__/__fixtures__/wallet-1.json +++ b/packages/core-tester-cli/__tests__/__fixtures__/wallet-1.json @@ -1,8 +1,8 @@ { - "address": "DNjuJEDQkhrJ7cA9FZ2iVXt5anYiM8Jtc9", - "publicKey": "03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec", - "username": "arkx", - "secondPublicKey": null, - "balance": 42159020792234, - "isDelegate": true + "address": "DNjuJEDQkhrJ7cA9FZ2iVXt5anYiM8Jtc9", + "publicKey": "03bbfb43ecb5a54a1e227bb37b5812b5321213838d376e2b455b6af78442621dec", + "username": "arkx", + "secondPublicKey": null, + "balance": 42159020792234, + "isDelegate": true } diff --git a/packages/core-tester-cli/__tests__/commands/command.test.js b/packages/core-tester-cli/__tests__/commands/command.test.js deleted file mode 100644 index 4c7333fb14..0000000000 --- a/packages/core-tester-cli/__tests__/commands/command.test.js +++ /dev/null @@ -1,433 +0,0 @@ -const clipboardy = require('clipboardy') -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const fill = require('lodash/fill') -const Command = require('../../lib/commands/command') -const logger = require('../../lib/utils/logger') - -const mockAxios = new MockAdapter(axios) - -let command -beforeEach(() => { - command = new Command() - mockAxios.reset() -}) - -describe('Command Base', () => { - describe('Init', () => { - it('should be a function', () => { - expect(Command.init).toBeFunction() - }) - it('init w/ option', async () => { - mockAxios - .onGet('http://test_baseUrl:1234/api/v2/node/configuration') - .reply(200, { data: { constants: {} } }) - mockAxios - .onGet('http://test_baseUrl:4321/config') - .reply(200, { data: { network: {} } }) - command = await Command.init({ - baseUrl: 'http://test_baseUrl', - apiPort: 1234, - p2pPort: 4321, - passphrase: 'test_passphrase', - secondPassphrase: 'test_secondPassphrase', - }) - expect(command.config).toContainEntries([ - ['baseUrl', 'http://test_baseUrl'], - ['apiPort', 1234], - ['p2pPort', 4321], - ['passphrase', 'test_passphrase'], - ['secondPassphrase', 'test_secondPassphrase'], - ]) - }) - }) - - describe('Copy to Clipboard', () => { - it('should be a function', () => { - expect(command.copyToClipboard).toBeFunction() - }) - - it('should contain the copied content', () => { - command.copyToClipboard([ - { - key: 'value', - serialized: '00', - }, - ]) - - expect(JSON.parse(clipboardy.readSync())).toEqual([ - { - key: 'value', - serialized: '00', - }, - ]) - }) - }) - - describe('Run', () => { - it('should be a function', () => { - expect(command.run).toBeFunction() - }) - it('throw expception', () => { - expect(command.run).toThrowWithMessage( - Error, - 'Method [run] not implemented!', - ) - }) - }) - - describe('Generate Wallets', () => { - it('should be a function', () => { - expect(command.generateWallets).toBeFunction() - }) - it('generate wallets', () => { - command.config = { - network: { - version: 1, - }, - } - const wallets = command.generateWallets(10) - expect(wallets).toBeArrayOfSize(10) - wallets.forEach(wallet => { - expect(wallet).toContainAllKeys(['address', 'keys', 'passphrase']) - }) - }) - }) - - describe('getDelegates', () => { - it('should be a function', () => { - expect(command.getDelegates).toBeFunction() - }) - it('should get delegates', async () => { - const delegatePage1Fixture = require('../__fixtures__/delegates-page-1.json') - const delegatePage2Fixture = require('../__fixtures__/delegates-page-2.json') - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios - .onGet('http://baseUrl:1234/api/v2/delegates?page=1') - .reply(200, delegatePage1Fixture) - mockAxios - .onGet('http://baseUrl:1234/api/v2/delegates?page=2') - .reply(200, delegatePage2Fixture) - - expect(await command.getDelegates()).toIncludeSameMembers([ - ...delegatePage1Fixture.data, - ...delegatePage2Fixture.data, - ]) - }) - }) - - describe('getTransactionDelaySeconds', () => { - it('should be a function', () => { - expect(command.getTransactionDelaySeconds).toBeFunction() - }) - it('should delay correct', () => { - command.config = { - constants: { - blocktime: 8, - block: { - maxTransactions: 10, - }, - }, - } - - // 1 Block - expect(command.getTransactionDelaySeconds(fill(Array(5), true))).toBe(20) - expect(command.getTransactionDelaySeconds(fill(Array(10), true))).toBe(20) - // 2 Block - expect(command.getTransactionDelaySeconds(fill(Array(15), true))).toBe(40) - // 10 Block - expect(command.getTransactionDelaySeconds(fill(Array(100), true))).toBe( - 200, - ) - }) - }) - - describe('getTransaction', () => { - it('should be a function', () => { - expect(command.getTransaction).toBeFunction() - }) - it('should get transaction', async () => { - const transactionFixture = require('../__fixtures__/transaction-1.json') - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios - .onGet( - `http://baseUrl:1234/api/v2/transactions/${transactionFixture.id}`, - ) - .reply(200, { - data: transactionFixture, - }) - - expect(await command.getTransaction(transactionFixture.id)).toEqual( - transactionFixture, - ) - }) - }) - - describe('getVoters', () => { - it('should be a function', () => { - expect(command.getVoters).toBeFunction() - }) - it('should get voters', async () => { - const voterPage1Fixture = require('../__fixtures__/voters-page-1.json') - const voterPage2Fixture = require('../__fixtures__/voters-page-2.json') - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios - .onGet('http://baseUrl:1234/api/v2/delegates/1/voters?page=1') - .reply(200, voterPage1Fixture) - mockAxios - .onGet('http://baseUrl:1234/api/v2/delegates/1/voters?page=2') - .reply(200, voterPage2Fixture) - - expect(await command.getVoters(1)).toIncludeSameMembers([ - ...voterPage1Fixture.data, - ...voterPage2Fixture.data, - ]) - }) - }) - - describe('getWalletBalance', () => { - it('should be a function', () => { - expect(command.getWalletBalance).toBeFunction() - }) - it('should get transaction', async () => { - const walletFixture = require('../__fixtures__/wallet-1.json') - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios - .onGet(`http://baseUrl:1234/api/v2/wallets/${walletFixture.address}`) - .reply(200, { - data: walletFixture, - }) - - expect( - (await command.getWalletBalance(walletFixture.address)).toNumber(), - ).toBe(walletFixture.balance) - }) - }) - - describe('getWallet', () => { - it('should be a function', () => { - expect(command.getWallet).toBeFunction() - }) - it('should get transaction', async () => { - const walletFixture = require('../__fixtures__/wallet-1.json') - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios - .onGet(`http://baseUrl:1234/api/v2/wallets/${walletFixture.address}`) - .reply(200, { - data: walletFixture, - }) - - expect(await command.getWallet(walletFixture.address)).toEqual( - walletFixture, - ) - }) - }) - - describe('static parseFee', () => { - it('should be a function', () => { - expect(Command.parseFee).toBeFunction() - }) - it('should give arktoshi', () => { - expect(Command.parseFee(0.1).toString()).toBe('10000000') - expect(Command.parseFee(1).toString()).toBe('100000000') - expect(Command.parseFee(10).toString()).toBe('1000000000') - expect(Command.parseFee('0.1').toString()).toBe('10000000') - expect(Command.parseFee('1').toString()).toBe('100000000') - expect(Command.parseFee('10').toString()).toBe('1000000000') - expect(Command.parseFee('0.001-0.005').toNumber()).toBeWithin( - 100000, - 500000, - ) - }) - }) - - describe('sendTransactions', () => { - it('should be a function', () => { - expect(command.sendTransactions).toBeFunction() - }) - it('should send and wait', async () => { - const responseFixture = require('../__fixtures__/transaction-response-1.json') - const loggerInfo = logger.info - logger.info = jest.fn() - command.getTransactionDelaySeconds = jest.fn(() => 1) - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios.onPost(`http://baseUrl:1234/api/v2/transactions`).reply(200, { - data: responseFixture, - }) - - const start = new Date().getTime() - const response = await command.sendTransactions([], 'test') - const end = new Date().getTime() - - expect(response).toEqual(responseFixture) - expect(command.getTransactionDelaySeconds).toHaveBeenCalledTimes(1) - expect(Math.round((end - start) / 1000)).toBeGreaterThanOrEqual(1) - expect(logger.info).toHaveBeenCalledWith( - 'Waiting 1 seconds to apply test transactions', - ) - logger.info = loggerInfo - }) - }) - - describe('postTransactions', () => { - it('should be a function', () => { - expect(command.postTransactions).toBeFunction() - }) - it('should send transaction', async () => { - const responseFixture = require('../__fixtures__/transaction-response-1.json') - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios.onPost(`http://baseUrl:1234/api/v2/transactions`).reply(200, { - data: responseFixture, - }) - - expect(await command.postTransactions([])).toEqual(responseFixture) - }) - }) - - describe('__applyConfig', () => { - it('should be a function', () => { - expect(command.__applyConfig).toBeFunction() - }) - it('should sets constant', () => { - command.options = { - baseUrl: 'http://baseUrl///', - apiPort: 1234, - p2pPort: 4321, - passphrase: 'test_passphrase', - secondPassphrase: 'test_secondPassphrase', - } - - command.__applyConfig() - - expect(command.config.baseUrl).toBe('http://baseUrl') - expect(command.config.apiPort).toBe(1234) - expect(command.config.p2pPort).toBe(4321) - expect(command.config.passphrase).toBe('test_passphrase') - expect(command.config.secondPassphrase).toBe('test_secondPassphrase') - }) - }) - - describe('__loadConstants', () => { - it('should be a function', () => { - expect(command.__loadConstants).toBeFunction() - }) - it('should sets constant', async () => { - command.config = { - baseUrl: 'http://baseUrl', - apiPort: 1234, - } - mockAxios - .onGet('http://baseUrl:1234/api/v2/node/configuration') - .reply(200, { - data: { - constants: { - testConstant: true, - testConstant2: 'test', - }, - }, - }) - - await command.__loadConstants() - - expect(command.config.constants).toContainAllEntries([ - ['testConstant', true], - ['testConstant2', 'test'], - ]) - }) - }) - - describe('__loadNetworkConfig', () => { - it('should be a function', () => { - expect(command.__loadNetworkConfig).toBeFunction() - }) - it('should sets constant', async () => { - command.config = { - baseUrl: 'http://baseUrl', - p2pPort: 4321, - } - mockAxios.onGet('http://baseUrl:4321/config').reply(200, { - data: { - network: { - testConfig: true, - testConfig2: 'test', - }, - }, - }) - - await command.__loadNetworkConfig() - - expect(command.config.network).toContainAllEntries([ - ['testConfig', true], - ['testConfig2', 'test'], - ]) - }) - }) - - describe('static __arkToArktoshi', () => { - it('should be a function', () => { - expect(Command.__arkToArktoshi).toBeFunction() - }) - it('should give arktoshi', () => { - expect(Command.__arkToArktoshi(0.00000001).toString()).toBe('1') - expect(Command.__arkToArktoshi(0.1).toString()).toBe('10000000') - expect(Command.__arkToArktoshi(1).toString()).toBe('100000000') - expect(Command.__arkToArktoshi(10).toString()).toBe('1000000000') - }) - }) - - describe('static __arktoshiToArk', () => { - it('should be a function', () => { - expect(Command.__arktoshiToArk).toBeFunction() - }) - it('should give ark', () => { - expect(Command.__arktoshiToArk(1)).toBe('0.00000001 DѦ') - expect(Command.__arktoshiToArk(10000000)).toBe('0.1 DѦ') - expect(Command.__arktoshiToArk(100000000)).toBe('1 DѦ') - expect(Command.__arktoshiToArk(1000000000)).toBe('10 DѦ') - }) - }) - - describe('__problemSendingTransactions', () => { - it('should be a function', () => { - expect(command.__problemSendingTransactions).toBeFunction() - }) - it('should log message and exit', () => { - const processExit = process.exit - const loggerError = logger.error - process.exit = jest.fn() - logger.error = jest.fn() - const message = '__problemSendingTransactions message' - command.__problemSendingTransactions({ - message, - }) - expect(logger.error).toHaveBeenCalledTimes(1) - expect(logger.error).toHaveBeenCalledWith( - `There was a problem sending transactions: ${message}`, - ) - expect(process.exit).toHaveBeenCalledTimes(1) - process.exit = processExit - logger.error = loggerError - }) - }) -}) diff --git a/packages/core-tester-cli/__tests__/commands/command.test.ts b/packages/core-tester-cli/__tests__/commands/command.test.ts new file mode 100644 index 0000000000..4753621140 --- /dev/null +++ b/packages/core-tester-cli/__tests__/commands/command.test.ts @@ -0,0 +1,15 @@ +import "jest-extended"; + +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { BaseCommand } from "../../src/commands/command"; + +const mockAxios = new MockAdapter(axios); + +beforeEach(() => { + mockAxios.reset(); +}); + +describe("Command Base", () => { + it.skip("", () => false); +}); diff --git a/packages/core-tester-cli/__tests__/commands/delegate-registration.test.js b/packages/core-tester-cli/__tests__/commands/delegate-registration.test.js deleted file mode 100644 index d21c8e25b0..0000000000 --- a/packages/core-tester-cli/__tests__/commands/delegate-registration.test.js +++ /dev/null @@ -1,70 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const superheroes = require('superheroes') -const DelegateRegistrationCommand = require('../../lib/commands/delegate-registration') - -const mockAxios = new MockAdapter(axios) - -const defaultOpts = { - skipTesting: true, - skipValidation: true, -} -beforeEach(() => { - // Just passthru. We'll test the Command class logic in its own test file more thoroughly - mockAxios - .onGet('http://localhost:4003/api/v2/node/configuration') - .reply(200, { data: { constants: {} } }) - mockAxios - .onGet('http://localhost:4000/config') - .reply(200, { data: { network: {} } }) - jest.spyOn(axios, 'get') - jest.spyOn(axios, 'post') -}) - -afterEach(() => { - mockAxios.reset() -}) - -describe('Commands - Delegate Registration', () => { - it('should be a function', () => { - expect(DelegateRegistrationCommand).toBeFunction() - }) - - it('should register as delegate', async () => { - const opts = { - ...defaultOpts, - delegateFee: 1, - number: 1, - } - const command = await DelegateRegistrationCommand.init(opts) - const expectedDelegateName = 'mr_bojangles' - // call to delegates/{publicKey}/voters returns zero delegates - mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates/).reply(200, { - meta: { pageCount: 1 }, - data: [], - }) - jest - .spyOn(superheroes, 'random') - .mockImplementation(() => expectedDelegateName) - - await command.run() - - expect(axios.post).toHaveBeenNthCalledWith( - 2, - 'http://localhost:4003/api/v2/transactions', - { - transactions: [ - expect.objectContaining({ - fee: DelegateRegistrationCommand.__arkToArktoshi(opts.delegateFee), - asset: { - delegate: { - username: expectedDelegateName, - }, - }, - }), - ], - }, - expect.any(Object), - ) - }) -}) diff --git a/packages/core-tester-cli/__tests__/commands/delegate-registration.test.ts b/packages/core-tester-cli/__tests__/commands/delegate-registration.test.ts new file mode 100644 index 0000000000..29cc2512b1 --- /dev/null +++ b/packages/core-tester-cli/__tests__/commands/delegate-registration.test.ts @@ -0,0 +1,62 @@ +import "jest-extended"; + +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import superheroes from "superheroes"; +import { DelegateRegistrationCommand } from "../../src/commands/delegate-registration"; +import { arkToArktoshi } from "../../src/utils"; +import { toFlags } from "../shared"; + +const mockAxios = new MockAdapter(axios); + +beforeEach(() => { + // Just passthru. We'll test the Command class logic in its own test file more thoroughly + mockAxios.onGet("http://localhost:4003/api/v2/node/configuration").reply(200, { data: { constants: {} } }); + mockAxios.onGet("http://localhost:4000/config").reply(200, { data: { network: {} } }); + jest.spyOn(axios, "get"); + jest.spyOn(axios, "post"); +}); + +afterEach(() => { + mockAxios.reset(); +}); + +describe("Commands - Delegate Registration", () => { + it("should register as delegate", async () => { + const opts = { + delegateFee: 1, + number: 1, + }; + + const expectedDelegateName = "mr_bojangles"; + // call to delegates/{publicKey}/voters returns zero delegates + mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates/).reply(200, { + meta: { pageCount: 1 }, + data: [], + }); + jest.spyOn(superheroes, "random").mockImplementation(() => expectedDelegateName); + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + + const flags = toFlags(opts); + await DelegateRegistrationCommand.run(flags); + + expect(axios.post).toHaveBeenNthCalledWith( + 4, + "http://localhost:4003/api/v2/transactions", + { + transactions: [ + expect.objectContaining({ + fee: arkToArktoshi(opts.delegateFee), + asset: { + delegate: { + username: expectedDelegateName, + }, + }, + }), + ], + }, + expect.any(Object), + ); + }); +}); diff --git a/packages/core-tester-cli/__tests__/commands/multi-signature.test.js b/packages/core-tester-cli/__tests__/commands/multi-signature.test.js deleted file mode 100644 index e2825db958..0000000000 --- a/packages/core-tester-cli/__tests__/commands/multi-signature.test.js +++ /dev/null @@ -1,7 +0,0 @@ -const vote = require('../../lib/commands/multi-signature') - -describe('Commands - Multi-signature', () => { - it('should be a function', () => { - expect(vote).toBeFunction() - }) -}) diff --git a/packages/core-tester-cli/__tests__/commands/second-signature.test.js b/packages/core-tester-cli/__tests__/commands/second-signature.test.js deleted file mode 100644 index f4753b9c94..0000000000 --- a/packages/core-tester-cli/__tests__/commands/second-signature.test.js +++ /dev/null @@ -1,63 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const SecondSignatureCommand = require('../../lib/commands/second-signature') - -const mockAxios = new MockAdapter(axios) - -const defaultOpts = { - skipTesting: true, - skipValidation: true, -} -beforeEach(() => { - // Just passthru. We'll test the Command class logic in its own test file more thoroughly - mockAxios - .onGet('http://localhost:4003/api/v2/node/configuration') - .reply(200, { data: { constants: {} } }) - mockAxios - .onGet('http://localhost:4000/config') - .reply(200, { data: { network: {} } }) - jest.spyOn(axios, 'get') - jest.spyOn(axios, 'post') -}) - -afterEach(() => { - mockAxios.reset() -}) - -describe('Commands - Second signature', () => { - it('should be a function', () => { - expect(SecondSignatureCommand).toBeFunction() - }) - - it('should apply second signature', async () => { - const opts = { - ...defaultOpts, - signatureFee: 1, - number: 1, - } - const command = await SecondSignatureCommand.init(opts) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - - await command.run() - - expect(axios.post).toHaveBeenNthCalledWith( - 2, - 'http://localhost:4003/api/v2/transactions', - { - transactions: [ - expect.objectContaining({ - fee: SecondSignatureCommand.__arkToArktoshi(opts.signatureFee), - asset: { - signature: { - publicKey: expect.any(String), - }, - }, - }), - ], - }, - expect.any(Object), - ) - }) -}) diff --git a/packages/core-tester-cli/__tests__/commands/second-signature.test.ts b/packages/core-tester-cli/__tests__/commands/second-signature.test.ts new file mode 100644 index 0000000000..33a0b8b300 --- /dev/null +++ b/packages/core-tester-cli/__tests__/commands/second-signature.test.ts @@ -0,0 +1,53 @@ +import "jest-extended"; + +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { SecondSignatureCommand } from "../../src/commands/second-signature"; +import { arkToArktoshi } from "../../src/utils"; +import { toFlags } from "../shared"; + +const mockAxios = new MockAdapter(axios); + +beforeEach(() => { + // Just passthru. We'll test the Command class logic in its own test file more thoroughly + mockAxios.onGet("http://localhost:4003/api/v2/node/configuration").reply(200, { data: { constants: {} } }); + mockAxios.onGet("http://localhost:4000/config").reply(200, { data: { network: {} } }); + jest.spyOn(axios, "get"); + jest.spyOn(axios, "post"); +}); + +afterEach(() => { + mockAxios.reset(); +}); + +describe("Commands - Second signature", () => { + it("should apply second signature", async () => { + const opts = { + signatureFee: 1, + number: 1, + }; + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + + const flags = toFlags(opts); + await SecondSignatureCommand.run(flags); + + expect(axios.post).toHaveBeenNthCalledWith( + 4, + "http://localhost:4003/api/v2/transactions", + { + transactions: [ + expect.objectContaining({ + fee: arkToArktoshi(opts.signatureFee), + asset: { + signature: { + publicKey: expect.any(String), + }, + }, + }), + ], + }, + expect.any(Object), + ); + }); +}); diff --git a/packages/core-tester-cli/__tests__/commands/transfer.test.js b/packages/core-tester-cli/__tests__/commands/transfer.test.js deleted file mode 100644 index 233b81fff3..0000000000 --- a/packages/core-tester-cli/__tests__/commands/transfer.test.js +++ /dev/null @@ -1,150 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const TransferCommand = require('../../lib/commands/transfer') - -const mockAxios = new MockAdapter(axios) - -const defaultOpts = { - skipTesting: true, - skipValidation: true, -} -beforeEach(() => { - // Just passthru. We'll test the Command class logic in its own test file more thoroughly - mockAxios - .onGet('http://localhost:4003/api/v2/node/configuration') - .reply(200, { data: { constants: {} } }) - mockAxios - .onGet('http://localhost:4000/config') - .reply(200, { data: { network: {} } }) - jest.spyOn(axios, 'get') - jest.spyOn(axios, 'post') -}) - -afterEach(() => { - mockAxios.reset() -}) - -afterAll(() => mockAxios.restore()) - -describe('Commands - Transfer', () => { - it('should be a function', () => { - expect(TransferCommand).toBeFunction() - }) - - it('should postTransactions using custom smartBridge value', async () => { - const expectedRecipientId = 'DFyUhQW52sNB5PZdS7VD9HknwYrSNHPQDq' - const expectedTransactionAmount = 2 - const expectedFee = 0.1 - const opts = { - ...defaultOpts, - amount: expectedTransactionAmount, - transferFee: expectedFee, - number: 1, - smartBridge: 'foo bar', - recipient: expectedRecipientId, - } - const command = await TransferCommand.init(opts) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - let expectedTransactions = [] - jest.spyOn(axios, 'post').mockImplementation((uri, { transactions }) => { - expectedTransactions = transactions - }) - - await command.run() - - expect(expectedTransactions).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - vendorField: 'foo bar', - amount: TransferCommand.__arkToArktoshi(expectedTransactionAmount), - fee: TransferCommand.__arkToArktoshi(expectedFee), - recipientId: expectedRecipientId, - }), - ]), - ) - }) - - it('should generate n transactions', async () => { - const expectedTxCount = 5 - const opts = { - ...defaultOpts, - amount: TransferCommand.__arkToArktoshi(2), - transferFee: TransferCommand.__arkToArktoshi(0.1), - number: expectedTxCount, - } - const command = await TransferCommand.init(opts) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - let expectedTransactions = [] - jest.spyOn(axios, 'post').mockImplementation((uri, { transactions }) => { - expectedTransactions = transactions - }) - - await command.run() - - expect(expectedTransactions).toHaveLength(expectedTxCount) - for (const t of expectedTransactions) { - expect(t.vendorField).toMatch(/Transaction \d/) - expect(t.amount).toBeDefined() - expect(t.fee).toBeDefined() - } - }) - - it('should send n transactions to specified recipient', async () => { - const expectedTxCount = 10 - const expectedRecipientId = 'DFyUhQW52sNB5PZdS7VD9HknwYrSNHPQDq' - const opts = { - ...defaultOpts, - amount: TransferCommand.__arkToArktoshi(2), - transferFee: TransferCommand.__arkToArktoshi(0.1), - number: expectedTxCount, - recipient: expectedRecipientId, - } - const command = await TransferCommand.init(opts) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - let expectedTransactions = [] - jest.spyOn(axios, 'post').mockImplementation((uri, { transactions }) => { - expectedTransactions = transactions - }) - - await command.run() - - expect(expectedTransactions).toHaveLength(expectedTxCount) - for (const t of expectedTransactions) { - expect(t.recipientId).toEqual(expectedRecipientId) - } - }) - - it('should sign with 2nd passphrase if specified', async () => { - const expectedTransactionAmount = TransferCommand.__arkToArktoshi(2) - const expectedFee = TransferCommand.__arkToArktoshi(0.1) - const opts = { - ...defaultOpts, - amount: expectedTransactionAmount, - transferFee: expectedFee, - number: 1, - secondPassphrase: 'she sells sea shells down by the sea shore', - } - const command = await TransferCommand.init(opts) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - let expectedTransactions = [] - jest.spyOn(axios, 'post').mockImplementation((uri, { transactions }) => { - expectedTransactions = transactions - }) - - await command.run() - - expect(expectedTransactions).toHaveLength(1) - for (const t of expectedTransactions) { - expect(t.secondSignature).toBeDefined() - expect(t.signSignature).toEqual(t.secondSignature) - } - }) -}) diff --git a/packages/core-tester-cli/__tests__/commands/transfer.test.ts b/packages/core-tester-cli/__tests__/commands/transfer.test.ts new file mode 100644 index 0000000000..1952ee635c --- /dev/null +++ b/packages/core-tester-cli/__tests__/commands/transfer.test.ts @@ -0,0 +1,139 @@ +import "jest-extended"; + +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { TransferCommand } from "../../src/commands/transfer"; +import { arkToArktoshi } from "../../src/utils"; +import { toFlags } from "../shared"; + +const mockAxios = new MockAdapter(axios); + +beforeEach(() => { + // Just passthru. We'll test the Command class logic in its own test file more thoroughly + mockAxios.onGet("http://localhost:4003/api/v2/node/configuration").reply(200, { data: { constants: {} } }); + mockAxios.onGet("http://localhost:4000/config").reply(200, { data: { network: {} } }); + + jest.spyOn(axios, "post"); +}); + +afterEach(() => { + mockAxios.reset(); +}); + +afterAll(() => mockAxios.restore()); + +describe("Commands - Transfer", () => { + it("should postTransactions using custom smartBridge value", async () => { + const expectedRecipientId = "DFyUhQW52sNB5PZdS7VD9HknwYrSNHPQDq"; + const expectedTransactionAmount = 2; + const expectedFee = arkToArktoshi(0.1); + const opts = { + amount: expectedTransactionAmount, + transferFee: expectedFee, + number: 1, + smartBridge: "foo bar", + recipient: expectedRecipientId, + }; + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + let expectedTransactions = []; + jest.spyOn(axios, "post").mockImplementation((uri, { transactions }) => { + expectedTransactions = transactions; + }); + + const flags = toFlags(opts); + await TransferCommand.run(flags); + + expect(expectedTransactions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + vendorField: "foo bar", + amount: arkToArktoshi(expectedTransactionAmount), + fee: arkToArktoshi(expectedFee), + recipientId: expectedRecipientId, + }), + ]), + ); + }); + + it("should generate n transactions", async () => { + const expectedTxCount = 5; + const expectedRecipientId = "DFyUhQW52sNB5PZdS7VD9HknwYrSNHPQDq"; + const opts = { + amount: arkToArktoshi(2), + transferFee: arkToArktoshi(2), + number: expectedTxCount, + recipient: expectedRecipientId, + }; + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + let expectedTransactions = []; + jest.spyOn(axios, "post").mockImplementation((uri, { transactions }) => { + expectedTransactions = transactions; + }); + + const flags = toFlags(opts); + await TransferCommand.run(flags); + + expect(expectedTransactions).toHaveLength(expectedTxCount); + for (const t of expectedTransactions) { + expect(t.vendorField).toMatch(/Transaction \d/); + expect(t.amount).toBeDefined(); + expect(t.fee).toBeDefined(); + } + }); + + it("should send n transactions to specified recipient", async () => { + const expectedTxCount = 10; + const expectedRecipientId = "DFyUhQW52sNB5PZdS7VD9HknwYrSNHPQDq"; + const opts = { + amount: arkToArktoshi(2), + transferFee: arkToArktoshi(0.1), + number: expectedTxCount, + recipient: expectedRecipientId, + }; + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + let expectedTransactions = []; + jest.spyOn(axios, "post").mockImplementation((uri, { transactions }) => { + expectedTransactions = transactions; + }); + + const flags = toFlags(opts); + await TransferCommand.run(flags); + + expect(expectedTransactions).toHaveLength(expectedTxCount); + for (const t of expectedTransactions) { + expect(t.recipientId).toEqual(expectedRecipientId); + } + }); + + it("should sign with 2nd passphrase if specified", async () => { + const expectedTransactionAmount = arkToArktoshi(2); + const expectedFee = arkToArktoshi(0.1); + const expectedRecipientId = "DFyUhQW52sNB5PZdS7VD9HknwYrSNHPQDq"; + + const opts = { + amount: expectedTransactionAmount, + transferFee: expectedFee, + number: 1, + secondPassphrase: "she sells sea shells down by the sea shore", + recipient: expectedRecipientId, + }; + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + let expectedTransactions = []; + jest.spyOn(axios, "post").mockImplementation((uri, { transactions }) => { + expectedTransactions = transactions; + }); + + const flags = toFlags(opts); + await TransferCommand.run(flags); + + expect(expectedTransactions).toHaveLength(1); + for (const t of expectedTransactions) { + expect(t.secondSignature).toBeDefined(); + expect(t.signSignature).toEqual(t.secondSignature); + } + }); +}); diff --git a/packages/core-tester-cli/__tests__/commands/vote.test.js b/packages/core-tester-cli/__tests__/commands/vote.test.js deleted file mode 100644 index 601afdde1a..0000000000 --- a/packages/core-tester-cli/__tests__/commands/vote.test.js +++ /dev/null @@ -1,106 +0,0 @@ -const axios = require('axios') -const MockAdapter = require('axios-mock-adapter') -const VoteCommand = require('../../lib/commands/vote') - -const mockAxios = new MockAdapter(axios) - -const defaultOpts = { - skipTesting: true, - skipValidation: true, -} -beforeEach(() => { - // Just passthru. We'll test the Command class logic in its own test file more thoroughly - mockAxios - .onGet('http://localhost:4003/api/v2/node/configuration') - .reply(200, { data: { constants: {} } }) - mockAxios - .onGet('http://localhost:4000/config') - .reply(200, { data: { network: {} } }) - jest.spyOn(axios, 'get') - jest.spyOn(axios, 'post') -}) - -afterEach(() => { - mockAxios.reset() -}) - -afterAll(() => mockAxios.restore()) - -describe('Commands - Vote', () => { - it('should be a function', () => { - expect(VoteCommand).toBeFunction() - }) - - it('should vote for specified delegate', async () => { - const expectedDelegate = '03f294777f7376e970b2bd4805b4a90c8449b5935d530bdb566d02800ac44a4c00' - const opts = { - ...defaultOpts, - number: 1, - voteFee: 1, - delegate: expectedDelegate, - } - const command = await VoteCommand.init(opts) - mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates.*/).reply(200) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - - await command.run() - - expect(axios.post).toHaveBeenNthCalledWith( - 2, - 'http://localhost:4003/api/v2/transactions', - { - transactions: [ - expect.objectContaining({ - fee: VoteCommand.__arkToArktoshi(opts.voteFee), - asset: { - votes: [`+${expectedDelegate}`], - }, - }), - ], - }, - expect.any(Object), - ) - }) - - it('should vote random delegate if non specified', async () => { - const expectedDelegate = '03f294777f7376e970b2bd4805b4a90c8449b5935d530bdb566d02800ac44a4c00' - const opts = { - ...defaultOpts, - number: 1, - voteFee: 1, - delegate: null, - } - const command = await VoteCommand.init(opts) - mockAxios - .onPost('http://localhost:4003/api/v2/transactions') - .reply(200, { data: {} }) - mockAxios - .onGet(/http:\/\/localhost:4003\/api\/v2\/delegates\/.*/) - .reply(200) // call to delegates/{publicKey}/voters - // call to /delegates - mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates/).reply(200, { - meta: { pageCount: 1 }, - data: [{ publicKey: expectedDelegate }], - }) - - await command.run() - - expect(axios.post).toHaveBeenNthCalledWith( - 2, - 'http://localhost:4003/api/v2/transactions', - { - transactions: [ - expect.objectContaining({ - fee: VoteCommand.__arkToArktoshi(opts.voteFee), - asset: { - votes: [`+${expectedDelegate}`], - }, - }), - ], - }, - expect.any(Object), - ) - }) -}) diff --git a/packages/core-tester-cli/__tests__/commands/vote.test.ts b/packages/core-tester-cli/__tests__/commands/vote.test.ts new file mode 100644 index 0000000000..beb80071a8 --- /dev/null +++ b/packages/core-tester-cli/__tests__/commands/vote.test.ts @@ -0,0 +1,91 @@ +import "jest-extended"; + +import axios from "axios"; +import MockAdapter from "axios-mock-adapter"; +import { VoteCommand } from "../../src/commands/vote"; +import { arkToArktoshi } from "../../src/utils"; +import { toFlags } from "../shared"; + +const mockAxios = new MockAdapter(axios); + +beforeEach(() => { + // Just passthru. We'll test the Command class logic in its own test file more thoroughly + mockAxios.onGet("http://localhost:4003/api/v2/node/configuration").reply(200, { data: { constants: {} } }); + mockAxios.onGet("http://localhost:4000/config").reply(200, { data: { network: {} } }); + jest.spyOn(axios, "get"); + jest.spyOn(axios, "post"); +}); + +afterEach(() => { + mockAxios.reset(); +}); + +afterAll(() => mockAxios.restore()); + +describe("Commands - Vote", () => { + it("should vote for specified delegate", async () => { + const expectedDelegate = "03f294777f7376e970b2bd4805b4a90c8449b5935d530bdb566d02800ac44a4c00"; + const opts = { + number: 1, + voteFee: 1, + delegate: expectedDelegate, + }; + + mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates.*/).reply(200); + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + + const flags = toFlags(opts); + await VoteCommand.run(flags); + + expect(axios.post).toHaveBeenNthCalledWith( + 4, + "http://localhost:4003/api/v2/transactions", + { + transactions: [ + expect.objectContaining({ + fee: arkToArktoshi(opts.voteFee), + asset: { + votes: [`+${expectedDelegate}`], + }, + }), + ], + }, + expect.any(Object), + ); + }); + + it("should vote random delegate if non specified", async () => { + const expectedDelegate = "03f294777f7376e970b2bd4805b4a90c8449b5935d530bdb566d02800ac44a4c00"; + const opts = { + number: 1, + voteFee: 1, + }; + + mockAxios.onPost("http://localhost:4003/api/v2/transactions").reply(200, { data: {} }); + mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates\/.*/).reply(200); // call to delegates/{publicKey}/voters + // call to /delegates + mockAxios.onGet(/http:\/\/localhost:4003\/api\/v2\/delegates/).reply(200, { + meta: { pageCount: 1 }, + data: [{ publicKey: expectedDelegate }], + }); + + const flags = toFlags(opts); + await VoteCommand.run(flags); + + expect(axios.post).toHaveBeenNthCalledWith( + 4, + "http://localhost:4003/api/v2/transactions", + { + transactions: [ + expect.objectContaining({ + fee: arkToArktoshi(opts.voteFee), + asset: { + votes: [`+${expectedDelegate}`], + }, + }), + ], + }, + expect.any(Object), + ); + }); +}); diff --git a/packages/core-tester-cli/__tests__/config.test.ts b/packages/core-tester-cli/__tests__/config.test.ts new file mode 100644 index 0000000000..cc7e95f973 --- /dev/null +++ b/packages/core-tester-cli/__tests__/config.test.ts @@ -0,0 +1,14 @@ +import "jest-extended"; +import { config } from "../src/config"; + +describe("Config", () => { + it("should have specific data", () => { + expect(config).toEqual({ + apiPort: 4003, + p2pPort: 4000, + baseUrl: "http://localhost", + passphrase: "prison tobacco acquire stone dignity palace note decade they current lesson robot", + secondPassphrase: "", + }); + }); +}); diff --git a/packages/core-tester-cli/__tests__/config/index.test.js b/packages/core-tester-cli/__tests__/config/index.test.js deleted file mode 100644 index 213eb37ca4..0000000000 --- a/packages/core-tester-cli/__tests__/config/index.test.js +++ /dev/null @@ -1,18 +0,0 @@ -const config = require('../../lib/config') - -describe('Config', () => { - it('should be an object', () => { - expect(config).toBeObject() - }) - - it('should have specific data', () => { - expect(config).toEqual({ - apiPort: 4003, - p2pPort: 4000, - baseUrl: 'http://localhost', - passphrase: - 'prison tobacco acquire stone dignity palace note decade they current lesson robot', - secondPassphrase: '', - }) - }) -}) diff --git a/packages/core-tester-cli/__tests__/shared.ts b/packages/core-tester-cli/__tests__/shared.ts new file mode 100644 index 0000000000..1562862cb6 --- /dev/null +++ b/packages/core-tester-cli/__tests__/shared.ts @@ -0,0 +1,7 @@ +const defaultOpts = ["--skipTesting", "--skipValidation"]; + +export const toFlags = (opts: object): string[] => { + return Object.keys(opts) + .map(k => [`--${k}`, String(opts[k])]) + .reduce((a, b) => a.concat(b), defaultOpts); +}; diff --git a/packages/core-tester-cli/__tests__/utils.test.ts b/packages/core-tester-cli/__tests__/utils.test.ts new file mode 100644 index 0000000000..91a246e088 --- /dev/null +++ b/packages/core-tester-cli/__tests__/utils.test.ts @@ -0,0 +1,33 @@ +import { arkToArktoshi, arktoshiToArk, parseFee } from "../src/utils"; + +describe("Utils", () => { + describe("parseFee", () => { + it("should give arktoshi", () => { + expect(parseFee(0.1).toString()).toBe("10000000"); + expect(parseFee(1).toString()).toBe("100000000"); + expect(parseFee(10).toString()).toBe("1000000000"); + expect(parseFee("0.1").toString()).toBe("10000000"); + expect(parseFee("1").toString()).toBe("100000000"); + expect(parseFee("10").toString()).toBe("1000000000"); + expect(parseFee("0.001-0.005").toNumber()).toBeWithin(100000, 500000); + }); + }); + + describe("arkToArktoshi", () => { + it("should give arktoshi", () => { + expect(arkToArktoshi(0.00000001).toString()).toBe("1"); + expect(arkToArktoshi(0.1).toString()).toBe("10000000"); + expect(arkToArktoshi(1).toString()).toBe("100000000"); + expect(arkToArktoshi(10).toString()).toBe("1000000000"); + }); + }); + + describe("arktoshiToArk", () => { + it("should give ark", () => { + expect(arktoshiToArk(1)).toBe("0.00000001 DѦ"); + expect(arktoshiToArk(10000000)).toBe("0.1 DѦ"); + expect(arktoshiToArk(100000000)).toBe("1 DѦ"); + expect(arktoshiToArk(1000000000)).toBe("10 DѦ"); + }); + }); +}); diff --git a/packages/core-tester-cli/bin/run b/packages/core-tester-cli/bin/run new file mode 100755 index 0000000000..30b14e1773 --- /dev/null +++ b/packages/core-tester-cli/bin/run @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +require('@oclif/command').run() +.then(require('@oclif/command/flush')) +.catch(require('@oclif/errors/handle')) diff --git a/packages/core-tester-cli/bin/run.cmd b/packages/core-tester-cli/bin/run.cmd new file mode 100644 index 0000000000..968fc30758 --- /dev/null +++ b/packages/core-tester-cli/bin/run.cmd @@ -0,0 +1,3 @@ +@echo off + +node "%~dp0\run" %* diff --git a/packages/core-tester-cli/bin/tester b/packages/core-tester-cli/bin/tester deleted file mode 100755 index a5a791af5b..0000000000 --- a/packages/core-tester-cli/bin/tester +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env node - -const app = require('commander') - -app.version(require('../package.json').version) - -const registerCommand = (name, description) => { - return app - .command(name) - .description(description) - .option('-n, --number ', 'number of wallets', 10) - .option('-a, --amount ', 'initial wallet token amount', 2) - .option('--transfer-fee ', 'transfer fee', 0.1) - .option('--base-url ', 'base api url') - .option('--api-port ', 'base api port', 4003) - .option('--p2p-port ', 'base p2p port', 4002) - .option('-p, --passphrase ', 'passphrase of initial wallet') - .option('-s, --second-passphrase ', 'second passphrase of initial wallet') - .option('--skip-validation', 'skip transaction validations', false) - .option('-c, --copy', 'copy the transactions to the clipboard', false) -} - -registerCommand('transfer', 'send multiple transactions') - .option('--flood-attempts ', 'flood node with same transactions', 0) - .option('--recipient ', 'recipient address') - .option('--skip-second-run', 'skip second sending of transactions', false) - .option('--smart-bridge ', 'smart-bridge value to use') - .action(async (options) => { - const command = await require('../lib/commands/transfer').init(options) - await command.run() - }) - -registerCommand('second-signature', 'create wallets with second signature') - .option('--signature-fee ', 'second signature fee', 5) - .action(async (options) => { - const command = await require('../lib/commands/second-signature').init(options) - await command.run() - }) - -registerCommand('delegate-registration', 'create multiple delegates') - .option('--delegate-fee ', 'delegate registration fee', 25) - .action(async (options) => { - const command = await require('../lib/commands/delegate-registration').init(options) - await command.run() - }) - -registerCommand('vote', 'create multiple votes for a delegate') - .option('--vote-fee ', 'vote fee', 1) - .option('-d, --delegate ', 'delegate public key') - .action(async (options) => { - const command = await require('../lib/commands/vote').init(options) - await command.run() - }) - -registerCommand('multi-signature', 'create multiple multisig wallets') - .option('--multisig-fee ', 'multisig fee', 5) - .option('-m, --min ', 'minimum number of signatures per transaction', 2) - .option('-l, --lifetime ', 'lifetime of transaction', 72) - .option('-q, --quantity ', 'number of signatures per wallet', 3) - .option('--skip-tests', 'skip transaction tests', false) - .action(async (options) => { - const command = await require('../lib/commands/multi-signature').init(options) - await command.run() - }) - -app - .command('*') - .action(env => { - app.help() - }) - -app.parse(process.argv) - -if (app.args.length === 0) { - app.help() -} diff --git a/packages/core-tester-cli/jest.config.js b/packages/core-tester-cli/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-tester-cli/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-tester-cli/lib/commands/command.js b/packages/core-tester-cli/lib/commands/command.js deleted file mode 100644 index 1d3d9adf98..0000000000 --- a/packages/core-tester-cli/lib/commands/command.js +++ /dev/null @@ -1,352 +0,0 @@ -const { Bignum, crypto, formatArktoshi } = require('@arkecosystem/crypto') -const { bignumify } = require('@arkecosystem/core-utils') -const bip39 = require('bip39') -const clipboardy = require('clipboardy') -const delay = require('delay') -const fs = require('fs') -const path = require('path') -const pluralize = require('pluralize') -const config = require('../config') -const { logger, paginate, request } = require('../utils') - -module.exports = class Command { - /** - * Init new instance of command. - * @param {Object} options - * @return {*} - */ - static async init(options) { - const command = new this() - command.options = options - command.__applyConfig() - await command.__loadConstants() - await command.__loadNetworkConfig() - - return command - } - - /** - * Run command. - * @param {Object} options Used to pass options to TransferCommand - * @throws Method [run] not implemented! - */ - run(options) { - throw new Error('Method [run] not implemented!') - } - - /** - * Copy transactions to clipboard. - * @param {Object[]} transactions - * @return {void} - */ - copyToClipboard(transactions) { - for (const transaction of transactions) { - transaction.serialized = transaction.serialized.toString('hex') - } - - clipboardy.writeSync(JSON.stringify(transactions)) - logger.info(`Copied ${pluralize('transaction', transactions.length, true)}`) - } - - /** - * Generate wallets based on quantity. - * @param {Number} [quantity] - * @return {Object[]} - */ - generateWallets(quantity) { - if (!quantity) { - quantity = this.options.number - } - - const wallets = [] - for (let i = 0; i < quantity; i++) { - const passphrase = bip39.generateMnemonic() - const keys = crypto.getKeys(passphrase) - const address = crypto.getAddress( - keys.publicKey, - this.config.network.version, - ) - - wallets.push({ address, keys, passphrase }) - } - - const testWalletsPath = path.resolve(__dirname, '../../test-wallets') - fs.appendFileSync( - testWalletsPath, - `${new Date().toLocaleDateString()} ${'-'.repeat(70)}\n`, - ) - for (const wallet of wallets) { - fs.appendFileSync( - testWalletsPath, - `${wallet.address}: ${wallet.passphrase}\n`, - ) - } - - return wallets - } - - /** - * Get delegate API response. - * @return {Object[]} - * @throws 'Could not get delegates' - */ - async getDelegates() { - try { - const delegates = await paginate(this.config, '/api/v2/delegates') - - return delegates - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - throw new Error(`Could not get delegates: ${message}`) - } - } - - /** - * Determine how long to wait for transactions to process. - * @param {Object[]} transactions - * @return {Number} - */ - getTransactionDelaySeconds(transactions) { - const waitPerBlock = Math.round(this.config.constants.blocktime / 10) * 20 - - return ( - waitPerBlock * - Math.ceil( - transactions.length / this.config.constants.block.maxTransactions, - ) - ) - } - - /** - * Get transaction from API by ID. - * @param {String} id - * @return {(Object|null)} - */ - async getTransaction(id) { - try { - const response = await request(this.config).get( - `/api/v2/transactions/${id}`, - ) - - if (response.data) { - return response.data - } - } catch (error) { - // - } - - return null - } - - /** - * Get delegate voters by public key. - * @param {String} publicKey - * @return {Object[]} - */ - async getVoters(publicKey) { - try { - return paginate(this.config, `/api/v2/delegates/${publicKey}/voters`) - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - throw new Error(`Could not get voters for '${publicKey}': ${message}`) - } - } - - /** - * Get wallet balance by address. - * @param {String} address - * @return {Bignum} - */ - async getWalletBalance(address) { - try { - return bignumify((await this.getWallet(address)).balance) - } catch (error) { - // - } - - return Bignum.ZERO - } - - /** - * Get wallet by address. - * @param {String} address - * @return {Object} - */ - async getWallet(address) { - try { - const response = await request(this.config).get( - `/api/v2/wallets/${address}`, - ) - - if (response.data) { - return response.data - } - - return null - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - throw new Error(`Could not get wallet for '${address}': ${message}`) - } - } - - /** - * Parse fee based on input. - * @param {(String|Number)} fee - * @return {Bignum} - */ - static parseFee(fee) { - if (typeof fee === 'string' && fee.indexOf('-') !== -1) { - const feeRange = fee.split('-').map( - f => - +bignumify(f) - .times(1e8) - .toFixed(), - ) - if (feeRange[1] < feeRange[0]) { - return feeRange[0] - } - - return bignumify( - Math.floor( - Math.random() * (feeRange[1] - feeRange[0] + 1) + feeRange[0], - ), - ) - } - - return bignumify(fee).times(1e8) - } - - /** - * Send transactions to API and wait for response. - * @param {Object[]} transactions - * @param {String} [transactionType] - * @param {Boolean} [wait=true] - * @return {Object} - */ - async sendTransactions(transactions, transactionType, wait = true) { - const response = await this.postTransactions(transactions) - - if (wait) { - const delaySeconds = this.getTransactionDelaySeconds(transactions) - transactionType = `${ - transactionType ? `${transactionType} ` : '' - }transactions` - logger.info(`Waiting ${delaySeconds} seconds to apply ${transactionType}`) - await delay(delaySeconds * 1000) - } - - return response - } - - /** - * Send transactions to API. - * @param {Object[]} transactions - * @return {Object} - */ - async postTransactions(transactions) { - try { - const response = await request(this.config).post('/api/v2/transactions', { - transactions, - }) - return response.data - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - throw new Error(`Could not post transactions: ${message}`) - } - } - - /** - * Apply options to config. - * @return {void} - */ - __applyConfig() { - this.config = { ...config } - if (this.options.baseUrl) { - this.config.baseUrl = this.options.baseUrl.replace(/\/+$/, '') - } - - if (this.options.apiPort) { - this.config.apiPort = this.options.apiPort - } - - if (this.options.p2pPort) { - this.config.p2pPort = this.options.p2pPort - } - - if (this.options.passphrase) { - this.config.passphrase = this.options.passphrase - } - - if (this.options.secondPassphrase) { - this.config.secondPassphrase = this.options.secondPassphrase - } - } - - /** - * Load constants from API and apply to config. - * @return {void} - */ - async __loadConstants() { - try { - this.config.constants = (await request(this.config).get( - '/api/v2/node/configuration', - )).data.constants - } catch (error) { - logger.error('Failed to get constants: ', error.message) - process.exit(1) - } - } - - /** - * Load network from API and apply to config. - * @return {void} - */ - async __loadNetworkConfig() { - try { - this.config.network = (await request(this.config).get( - '/config', - true, - )).data.network - } catch (error) { - logger.error('Failed to get network config: ', error.message) - process.exit(1) - } - } - - /** - * Convert ARK to Arktoshi. - * @param {Number} ark - * @return {Bignum} - */ - static __arkToArktoshi(ark) { - return bignumify(ark * 1e8) - } - - /** - * Convert Arktoshi to ARK. - * @param {Bignum} arktoshi - * @return {String} - */ - static __arktoshiToArk(arktoshi) { - return formatArktoshi(arktoshi) - } - - /** - * Quit command and output error when problem sending transactions. - * @param {Error} error - * @return {void} - */ - __problemSendingTransactions(error) { - const message = error.response ? error.response.data.message : error.message - logger.error(`There was a problem sending transactions: ${message}`) - process.exit(1) - } -} diff --git a/packages/core-tester-cli/lib/commands/delegate-registration.js b/packages/core-tester-cli/lib/commands/delegate-registration.js deleted file mode 100644 index 72135756f5..0000000000 --- a/packages/core-tester-cli/lib/commands/delegate-registration.js +++ /dev/null @@ -1,111 +0,0 @@ -const { client } = require('@arkecosystem/crypto') -const pluralize = require('pluralize') -const superheroes = require('superheroes') -const { logger } = require('../utils') -const Command = require('./command') -const Transfer = require('./transfer') - -module.exports = class DelegateRegistrationCommand extends Command { - /** - * Run delegate-registration command. - * @return {void} - */ - async run() { - const wallets = this.generateWallets() - - const transfer = await Transfer.init(this.options) - await transfer.run({ - wallets, - amount: this.options.amount || 25, - skipTesting: true, - }) - - const delegates = await this.getDelegates() - - logger.info( - `Sending ${this.options.number} delegate registration ${pluralize( - 'transaction', - this.options.number, - )}`, - ) - - if (!this.options.skipValidation) { - logger.info(`Starting delegate count: ${delegates.length}`) - } - - const transactions = [] - const usedDelegateNames = delegates.map(delegate => delegate.username) - - wallets.forEach((wallet, i) => { - while (!wallet.username || usedDelegateNames.includes(wallet.username)) { - wallet.username = superheroes.random() - } - - wallet.username = wallet.username.toLowerCase().replace(/ /g, '_') - usedDelegateNames.push(wallet.username) - - const transaction = client - .getBuilder() - .delegateRegistration() - .fee(Command.parseFee(this.options.delegateFee)) - .usernameAsset(wallet.username) - .network(this.config.network.version) - .sign(wallet.passphrase) - .secondSign(this.config.secondPassphrase) - .build() - - transactions.push(transaction) - - logger.info( - `${i} ==> ${transaction.id}, ${ - wallet.address - } (fee: ${Command.__arktoshiToArk(transaction.fee)}, username: ${ - wallet.username - })`, - ) - }) - - if (this.options.copy) { - this.copyToClipboard(transactions) - return - } - - const expectedDelegates = delegates.length + wallets.length - if (!this.options.skipValidation) { - logger.info(`Expected end delegate count: ${expectedDelegates}`) - } - - try { - await this.sendTransactions( - transactions, - 'delegate', - !this.options.skipValidation, - ) - - if (this.options.skipValidation) { - return - } - - const targetDelegates = await this.getDelegates() - logger.info( - `All transactions have been sent! Total delegates: ${ - targetDelegates.length - }`, - ) - - if (targetDelegates.length !== expectedDelegates) { - logger.error( - `Delegate count incorrect. '${ - targetDelegates.length - }' but should be '${expectedDelegates}'`, - ) - } - } catch (error) { - logger.error( - `There was a problem sending transactions: ${ - error.response ? error.response.data.message : error - }`, - ) - } - } -} diff --git a/packages/core-tester-cli/lib/commands/multi-signature.js b/packages/core-tester-cli/lib/commands/multi-signature.js deleted file mode 100644 index c7475c7329..0000000000 --- a/packages/core-tester-cli/lib/commands/multi-signature.js +++ /dev/null @@ -1,433 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const { client } = require('@arkecosystem/crypto') -const pluralize = require('pluralize') -const take = require('lodash/take') -const { logger } = require('../utils') -const Command = require('./command') -const Transfer = require('./transfer') - -module.exports = class MultiSignatureCommand extends Command { - /** - * Run multi-signature command. - * @return {void} - */ - async run() { - const approvalWallets = this.generateWallets(this.options.quantity) - const publicKeys = approvalWallets.map( - wallet => `+${wallet.keys.publicKey}`, - ) - const min = this.options.min - ? Math.min(this.options.min, publicKeys.length) - : publicKeys.length - - const testCosts = this.options.skipTests ? 1 : 2 - const wallets = this.generateWallets() - - const transfer = await Transfer.init(this.options) - await transfer.run({ - wallets, - amount: (publicKeys.length + 1) * 5 + testCosts, - skipTesting: true, - }) - - const transactions = this.generateTransactions( - wallets, - approvalWallets, - publicKeys, - min, - ) - - if (this.options.copy) { - this.copyToClipboard(transactions) - - return // eslint-disable-line no-unreachable - } - - try { - const response = await this.sendTransactions( - transactions, - 'multi-signature', - !this.options.skipValidation, - ) - - if (!this.options.skipValidation) { - let hasUnprocessed = false - for (const transaction of transactions) { - if (!response.accept.includes(transaction.id)) { - hasUnprocessed = true - logger.error( - `Multi-signature transaction '${ - transaction.id - }' was not processed`, - ) - } - } - if (hasUnprocessed) { - process.exit(1) - } - - for (const transaction of transactions) { - const tx = await this.getTransaction(transaction.id) - if (!tx) { - logger.error( - `Transaction '${transaction.id}' should be on the blockchain`, - ) - } - } - } - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - logger.error( - `There was a problem sending multi-signature transactions: ${message}`, - ) - process.exit(1) - } - - if (this.options.skipTests || this.options.skipValidation) { - return - } - - await this.__testSendWithSignatures(transfer, wallets, approvalWallets) - await this.__testSendWithMinSignatures( - transfer, - wallets, - approvalWallets, - min, - ) - await this.__testSendWithBelowMinSignatures( - transfer, - wallets, - approvalWallets, - min, - ) - await this.__testSendWithoutSignatures(transfer, wallets) - await this.__testSendWithEmptySignatures(transfer, wallets) - await this.__testNewMultiSignatureRegistration( - wallets, - approvalWallets, - publicKeys, - min, - ) - } - - /** - * Generate batch of transactions based on wallets - * @param {Object[]} wallets - * @param {Object[]} [approvalWallets=[]] - * @param {String[]} [publicKeys=[]] - * @param {Number} [min=2] - * @param {Boolean} [log=true] - * @return {Object[]} - */ - generateTransactions( - wallets, - approvalWallets = [], - publicKeys = [], - min = 2, - log = true, - ) { - const transactions = [] - wallets.forEach((wallet, i) => { - const builder = client.getBuilder().multiSignature() - - builder - .fee(Command.parseFee(this.options.multisigFee)) - .multiSignatureAsset({ - lifetime: this.options.lifetime, - keysgroup: publicKeys, - min, - }) - .network(this.config.network.version) - .sign(wallet.passphrase) - - if (wallet.secondPassphrase || this.config.secondPassphrase) { - builder.secondSign( - wallet.secondPassphrase || this.config.secondPassphrase, - ) - } - - if (approvalWallets) { - for (let j = approvalWallets.length - 1; j >= 0; j--) { - builder.multiSignatureSign(approvalWallets[j].passphrase) - } - } - - const transaction = builder.build() - transactions.push(transaction) - - if (log) { - logger.info( - `${i} ==> ${transaction.id}, ${ - wallet.address - } (fee: ${Command.__arktoshiToArk(transaction.fee)})`, - ) - } - }) - - return transactions - } - - /** - * Send transactions with approver signatures. - * @param {TransferCommand} transfer - * @param {Object[]} wallets - * @param {Object[]} [approvalWallets=[]] - * @return {void} - */ - async __testSendWithSignatures(transfer, wallets, approvalWallets = []) { - logger.info('Sending transactions with signatures') - - const transactions = transfer.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - approvalWallets, - ) - - try { - await this.sendTransactions(transactions) - for (const transaction of transactions) { - const tx = await this.getTransaction(transaction.id) - if (!tx) { - logger.error( - `Transaction '${transaction.id}' should be on the blockchain`, - ) - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } - - /** - * Send transactions with min approver signatures. - * @param {TransferCommand} transfer - * @param {Object[]} wallets - * @param {Object[]} [approvalWallets=[]] - * @param {Number} [min=2] - * @return {void} - */ - async __testSendWithMinSignatures( - transfer, - wallets, - approvalWallets = [], - min = 2, - ) { - logger.info( - `Sending transactions with ${min} (min) of ${pluralize( - 'signature', - approvalWallets.length, - true, - )}`, - ) - - const transactions = transfer.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - take(approvalWallets, min), - ) - - try { - await this.sendTransactions(transactions) - for (const transaction of transactions) { - const tx = await this.getTransaction(transaction.id) - if (!tx) { - logger.error( - `Transaction '${transaction.id}' should be on the blockchain`, - ) - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } - - /** - * Send transactions with below min approver signatures. - * @param {TransferCommand} transfer - * @param {Object[]} wallets - * @param {Object[]} [approvalWallets=[]] - * @param {Number} [min=2] - * @return {void} - */ - async __testSendWithBelowMinSignatures( - transfer, - wallets, - approvalWallets = [], - min = 2, - ) { - const max = min - 1 - logger.info( - `Sending transactions with ${max} (below min) of ${pluralize( - 'signature', - approvalWallets.length, - true, - )}`, - ) - - const transactions = transfer.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - take(approvalWallets, max), - ) - - try { - await this.sendTransactions(transactions) - for (const transaction of transactions) { - try { - const tx = await this.getTransaction(transaction.id) - if (tx) { - logger.error( - `Transaction '${transaction.id}' should not be on the blockchain`, - ) - } - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - if (message !== 'Transaction not found') { - logger.error( - `Failed to check transaction '${transaction.id}': ${message}`, - ) - } - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } - - /** - * Send transactions without approver signatures. - * @param {TransferCommand} transfer - * @param {Object[]} wallets - * @return {void} - */ - async __testSendWithoutSignatures(transfer, wallets) { - logger.info('Sending transactions without signatures') - - const transactions = transfer.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - ) - - try { - await this.sendTransactions(transactions) - for (const transaction of transactions) { - try { - const tx = await this.getTransaction(transaction.id) - if (tx) { - logger.error( - `Transaction '${transaction.id}' should not be on the blockchain`, - ) - } - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - if (message !== 'Transaction not found') { - logger.error( - `Failed to check transaction '${transaction.id}': ${message}`, - ) - } - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } - - /** - * Send transactions with empty approver signatures. - * @param {TransferCommand} transfer - * @param {Object[]} wallets - * @return {void} - */ - async __testSendWithEmptySignatures(transfer, wallets) { - logger.info('Sending transactions with empty signatures') - - const transactions = transfer.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - ) - for (const transaction of transactions) { - transaction.data.signatures = [] - } - - try { - await this.sendTransactions(transactions) - for (const transaction of transactions) { - try { - const tx = await this.getTransaction(transaction.id) - if (tx) { - logger.error( - `Transaction '${transaction.id}' should not be on the blockchain`, - ) - } - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - if (message !== 'Transaction not found') { - logger.error( - `Failed to check transaction '${transaction.id}': ${message}`, - ) - } - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } - - /** - * Send transactions to re-register multi-signature wallets. - * @param {Object[]} wallets - * @param {Object[]} [approvalWallets=[]] - * @param {Object[]} [publicKeys=[]] - * @param {Number} [min=2] - * @return {void} - */ - async __testNewMultiSignatureRegistration( - wallets, - approvalWallets = [], - publicKeys = [], - min = 2, - ) { - logger.info('Sending transactions to re-register multi-signature') - - const transactions = this.generateTransactions( - wallets, - approvalWallets, - publicKeys, - min, - ) - - try { - await this.sendTransactions(transactions) - for (const transaction of transactions) { - try { - const tx = await this.getTransaction(transaction.id) - if (tx) { - logger.error( - `Transaction '${transaction.id}' should not be on the blockchain`, - ) - } - } catch (error) { - const message = error.response - ? error.response.data.message - : error.message - if (message !== 'Transaction not found') { - logger.error( - `Failed to check transaction '${transaction.id}': ${message}`, - ) - } - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } -} diff --git a/packages/core-tester-cli/lib/commands/second-signature.js b/packages/core-tester-cli/lib/commands/second-signature.js deleted file mode 100644 index 264ac809d8..0000000000 --- a/packages/core-tester-cli/lib/commands/second-signature.js +++ /dev/null @@ -1,89 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const { client } = require('@arkecosystem/crypto') -const pluralize = require('pluralize') -const { logger } = require('../utils') -const Command = require('./command') -const Transfer = require('./transfer') - -module.exports = class DelegateRegistrationCommand extends Command { - /** - * Run second-signature command. - * @return {void} - */ - async run() { - const wallets = this.generateWallets() - - const transfer = await Transfer.init(this.options) - await transfer.run({ - wallets, - amount: this.options.amount || 5, - skipTesting: true, - }) - - logger.info( - `Sending ${this.options.number} second signature ${pluralize( - 'transaction', - this.options.number, - )}`, - ) - - const transactions = [] - wallets.forEach((wallet, i) => { - wallet.secondPassphrase = - this.config.secondPassphrase || wallet.passphrase - const transaction = client - .getBuilder() - .secondSignature() - .fee(Command.parseFee(this.options.signatureFee)) - .signatureAsset(wallet.secondPassphrase) - .network(this.config.network.version) - .sign(wallet.passphrase) - .build() - - wallet.publicKey = transaction.senderPublicKey - wallet.secondPublicKey = transaction.asset.signature.publicKey - transactions.push(transaction) - - logger.info( - `${i} ==> ${transaction.id}, ${ - wallet.address - } (fee: ${Command.__arktoshiToArk(transaction.fee)})`, - ) - }) - - if (this.options.copy) { - this.copyToClipboard(transactions) - return - } - - try { - await this.sendTransactions( - transactions, - 'second-signature', - !this.options.skipValidation, - ) - - if (this.options.skipValidation) { - return - } - - for (const walletObject of wallets) { - const wallet = await this.getWallet(walletObject.address) - - if ( - wallet.secondPublicKey !== walletObject.secondPublicKey || - wallet.publicKey !== walletObject.publicKey - ) { - logger.error(`Invalid second signature for ${walletObject.address}.`) - } - } - } catch (error) { - logger.error( - `There was a problem sending transactions: ${ - error.response ? error.response.data.message : error - }`, - ) - } - } -} diff --git a/packages/core-tester-cli/lib/commands/transfer.js b/packages/core-tester-cli/lib/commands/transfer.js deleted file mode 100644 index 939b86ff16..0000000000 --- a/packages/core-tester-cli/lib/commands/transfer.js +++ /dev/null @@ -1,403 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const { Bignum, client, crypto } = require('@arkecosystem/crypto') -const delay = require('delay') -const pluralize = require('pluralize') -const unique = require('lodash/uniq') -const { logger } = require('../utils') -const Command = require('./command') - -module.exports = class TransferCommand extends Command { - /** - * Run transfer command. - * @param {Object} options - * @return {void} - */ - async run(options) { - this.options = { ...this.options, ...options } - - const primaryAddress = crypto.getAddress( - crypto.getKeys(this.config.passphrase).publicKey, - this.config.network.version, - ) - - let wallets = this.options.wallets - if (wallets === undefined) { - wallets = this.generateWallets() - } - - logger.info( - `Sending ${wallets.length} transfer ${pluralize( - 'transaction', - wallets.length, - )}`, - ) - - const walletBalance = await this.getWalletBalance(primaryAddress) - - if (!this.options.skipValidation) { - logger.info( - `Sender starting balance: ${Command.__arktoshiToArk(walletBalance)}`, - ) - } - - let totalDeductions = Bignum.ZERO - const transactionAmount = Command.__arkToArktoshi(this.options.amount || 2) - - const transactions = this.generateTransactions( - transactionAmount, - wallets, - null, - true, - ) - for (const transaction of transactions) { - totalDeductions = totalDeductions - .plus(transactionAmount) - .plus(transaction.fee) - } - - if (this.options.copy) { - this.copyToClipboard(transactions) - return - } - - const expectedSenderBalance = new Bignum(walletBalance).minus( - totalDeductions, - ) - if (!this.options.skipValidation) { - logger.info( - `Sender expected ending balance: ${Command.__arktoshiToArk( - expectedSenderBalance, - )}`, - ) - } - - const runOptions = { - primaryAddress, - transactions, - wallets, - transactionAmount, - expectedSenderBalance, - skipValidation: this.options.skipValidation, - } - - try { - if (!this.options.floodAttempts) { - const successfulTest = await this.__performRun(runOptions, 1) - if ( - successfulTest && - !this.options.skipSecondRun && - !this.options.skipValidation && - !this.options.skipTesting - ) { - await this.__performRun(runOptions, 2, false, true) - } - } else { - const attempts = this.options.floodAttempts - for (let i = attempts; i > 0; i--) { - await this.__performRun( - runOptions, - attempts - i + 1, - i !== 1, - i !== attempts, - ) - } - } - } catch (error) { - const message = error.response ? error.response.data.message : error - logger.error(`There was a problem sending transactions: ${message}`) - } - - if (this.options.skipValidation) { - return - } - - await this.__testVendorField(wallets) - await this.__testEmptyVendorField(wallets) - } - - /** - * Generate batch of transactions based on wallets. - * @param {Bignum} transactionAmount - * @param {Object[]} wallets - * @param {Object[]} [approvalWallets=[]] - * @param {Boolean} [overridePassphrase=false] - * @param {String} [vendorField] - * @param {Boolean} [log=true] - * @return {Object[]} - */ - generateTransactions( - transactionAmount, - wallets, - approvalWallets = [], - overridePassphrase = false, - vendorField, - log = true, - ) { - vendorField = vendorField || this.options.smartBridge - const transactions = [] - wallets.forEach((wallet, i) => { - const builder = client.getBuilder().transfer() - // noinspection JSCheckFunctionSignatures - builder - .fee(Command.parseFee(this.options.transferFee)) - .recipientId(this.options.recipient || wallet.address) - .network(this.config.network.version) - .amount(transactionAmount) - .vendorField( - vendorField === undefined ? `Transaction ${i + 1}` : vendorField, - ) - .sign(overridePassphrase ? this.config.passphrase : wallet.passphrase) - - if (wallet.secondPassphrase || this.config.secondPassphrase) { - builder.secondSign( - wallet.secondPassphrase || this.config.secondPassphrase, - ) - } - - if (approvalWallets) { - for (let j = approvalWallets.length - 1; j >= 0; j--) { - builder.multiSignatureSign(approvalWallets[j].passphrase) - } - } - - const transaction = builder.build() - transactions.push(transaction) - - if (log) { - logger.info( - `${i} ==> ${transaction.id}, ${ - transaction.recipientId - } (fee: ${Command.__arktoshiToArk(transaction.fee)})`, - ) - } - }) - - return transactions - } - - /** - * Perform a run of transactions. - * @param {Object} runOptions - * @param {Number} [runNumber=1] - * @param {Boolean} [skipWait=false] - * @param {Boolean} [isSubsequentRun=false] - * @return {Boolean} - */ - async __performRun( - runOptions, - runNumber = 1, - skipWait = false, - isSubsequentRun = false, - ) { - if (skipWait) { - runOptions.skipValidation = true - this.__sendTransactionsWithResults(runOptions, isSubsequentRun) - - return - } - - if (await this.__sendTransactionsWithResults(runOptions, isSubsequentRun)) { - logger.info( - `All transactions have been received and forged for run ${runNumber}!`, - ) - - return true - } - - logger.error(`Test failed on run ${runNumber}`) - - return false - } - - /** - * Send transactions and validate results. - * @param {Object} runOptions - * @param {Boolean} isSubsequentRun - * @return {Boolean} - */ - async __sendTransactionsWithResults(runOptions, isSubsequentRun) { - let successfulTest = true - - let postResponse - try { - postResponse = await this.postTransactions(runOptions.transactions) - } catch (error) { - if (runOptions.skipValidation) { - return true - } - - const message = error.response ? error.response.data.error : error.message - logger.error(`Transaction request failed: ${message}`) - - return false - } - - if (runOptions.skipValidation) { - return true - } - - if (!isSubsequentRun && !postResponse.accept.length) { - return false - } - - if (!isSubsequentRun) { - for (const transaction of runOptions.transactions) { - if (!postResponse.accept.includes(transaction.id)) { - logger.error( - `Transaction '${ - transaction.id - }' didn't get approved on the network`, - ) - - successfulTest = false - } - } - } - - for (const key of Object.keys(postResponse)) { - if (key === 'success') { - continue - } - - const dataLength = postResponse[key].length - const uniqueLength = unique(postResponse[key]).length - if (dataLength !== uniqueLength) { - logger.error( - `Response data for '${key}' has ${dataLength - - uniqueLength} duplicate transaction ids`, - ) - successfulTest = false - } - } - - const delaySeconds = this.getTransactionDelaySeconds( - runOptions.transactions, - ) - logger.info( - `Waiting ${delaySeconds} seconds to apply transfer transactions`, - ) - await delay(delaySeconds * 1000) - - for (const transaction of runOptions.transactions) { - const transactionResponse = await this.getTransaction(transaction.id) - if (transactionResponse && transactionResponse.id !== transaction.id) { - logger.error( - `Transaction '${transaction.id}' didn't get applied on the network`, - ) - - successfulTest = false - } - } - - if (runOptions.primaryAddress && runOptions.expectedSenderBalance) { - const walletBalance = await this.getWalletBalance( - runOptions.primaryAddress, - ) - if (!walletBalance.isEqualTo(runOptions.expectedSenderBalance)) { - successfulTest = false - logger.error( - `Sender balance incorrect: '${Command.__arktoshiToArk( - walletBalance, - )}' but should be '${Command.__arktoshiToArk( - runOptions.expectedSenderBalance, - )}'`, - ) - } - } - - for (const wallet of runOptions.wallets) { - const balance = await this.getWalletBalance(wallet.address) - if (!balance.isEqualTo(runOptions.transactionAmount)) { - successfulTest = false - logger.error( - `Incorrect destination balance for ${ - wallet.address - }. Should be '${Command.__arktoshiToArk( - runOptions.transactionAmount, - )}' but is '${Command.__arktoshiToArk(balance)}'`, - ) - } - } - - return successfulTest - } - - /** - * Test vendor field is set correctly on blockchain. - * @param {Object[]} wallets - * @return {void} - */ - async __testVendorField(wallets) { - logger.info('Testing VendorField value is set correctly') - - const transactions = this.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - null, - null, - 'Testing VendorField', - ) - - try { - await this.sendTransactions(transactions) - - for (const transaction of transactions) { - const tx = await this.getTransaction(transaction.id) - if (!tx) { - logger.error( - `Transaction '${transaction.id}' should be on the blockchain`, - ) - } - if (tx.vendorField !== 'Testing VendorField') { - logger.error( - `Transaction '${ - transaction.id - }' does not have correct vendorField value`, - ) - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } - - /** - * Test empty vendor field is set correctly on blockchain. - * @param {Object[]} wallets - * @return {void} - */ - async __testEmptyVendorField(wallets) { - logger.info('Testing empty VendorField value') - - const transactions = this.generateTransactions( - Command.__arkToArktoshi(2), - wallets, - null, - null, - null, - ) - - try { - await this.sendTransactions(transactions) - - for (const transaction of transactions) { - const tx = await this.getTransaction(transaction.id) - if (!tx) { - logger.error( - `Transaction '${transaction.id}' should be on the blockchain`, - ) - } - if (tx.vendorField) { - logger.error( - `Transaction '${ - transaction.id - }' should not have vendorField value '${tx.vendorField}'`, - ) - } - } - } catch (error) { - this.__problemSendingTransactions(error) - } - } -} diff --git a/packages/core-tester-cli/lib/commands/vote.js b/packages/core-tester-cli/lib/commands/vote.js deleted file mode 100644 index 638d016ae9..0000000000 --- a/packages/core-tester-cli/lib/commands/vote.js +++ /dev/null @@ -1,102 +0,0 @@ -const { client } = require('@arkecosystem/crypto') -const pluralize = require('pluralize') -const sample = require('lodash/sample') -const { logger } = require('../utils') -const Command = require('./command') -const Transfer = require('./transfer') - -module.exports = class VoteCommand extends Command { - /** - * Run vote command. - * @return {void} - */ - async run() { - const wallets = this.generateWallets() - - const transfer = await Transfer.init(this.options) - await transfer.run({ - wallets, - amount: 2, - skipTesting: true, - }) - - let delegate = this.options.delegate - if (!delegate) { - try { - delegate = sample(await this.getDelegates()).publicKey - } catch (error) { - logger.error(error) - return - } - } - - const voters = await this.getVoters(delegate) - logger.info( - `Sending ${this.options.number} vote ${pluralize( - 'transaction', - this.options.number, - )}`, - ) - - const transactions = [] - wallets.forEach((wallet, i) => { - const transaction = client - .getBuilder() - .vote() - .fee(Command.parseFee(this.options.voteFee)) - .votesAsset([`+${delegate}`]) - .network(this.config.network.version) - .sign(wallet.passphrase) - .secondSign(this.config.secondPassphrase) - .build() - - transactions.push(transaction) - - logger.info( - `${i} ==> ${transaction.id}, ${ - wallet.address - } (fee: ${Command.__arktoshiToArk(transaction.fee)})`, - ) - }) - - if (this.options.copy) { - this.copyToClipboard(transactions) - return - } - - const expectedVoterCount = voters.length + wallets.length - if (!this.options.skipValidation) { - logger.info(`Expected end voters: ${expectedVoterCount}`) - } - - try { - await this.sendTransactions( - transactions, - 'vote', - !this.options.skipValidation, - ) - - if (this.options.skipValidation) { - return - } - - const voterCount = (await this.getVoters(delegate)).length - - logger.info( - `All transactions have been sent! Total voters: ${voterCount}`, - ) - - if (voterCount !== expectedVoterCount) { - logger.error( - `Delegate voter count incorrect. '${voterCount}' but should be '${expectedVoterCount}'`, - ) - } - } catch (error) { - logger.error( - `There was a problem sending transactions: ${ - error.response ? error.response.data.message : error - }`, - ) - } - } -} diff --git a/packages/core-tester-cli/lib/config/index.js b/packages/core-tester-cli/lib/config/index.js deleted file mode 100644 index 92eb0eb0e2..0000000000 --- a/packages/core-tester-cli/lib/config/index.js +++ /dev/null @@ -1,10 +0,0 @@ -const config = { - apiPort: 4003, - p2pPort: 4000, - baseUrl: 'http://localhost', - passphrase: - 'prison tobacco acquire stone dignity palace note decade they current lesson robot', - secondPassphrase: '', -} - -module.exports = Object.freeze(config) diff --git a/packages/core-tester-cli/lib/utils/index.js b/packages/core-tester-cli/lib/utils/index.js deleted file mode 100644 index 6305859902..0000000000 --- a/packages/core-tester-cli/lib/utils/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - logger: require('./logger'), - paginate: require('./paginate'), - request: require('./request'), -} diff --git a/packages/core-tester-cli/lib/utils/logger.js b/packages/core-tester-cli/lib/utils/logger.js deleted file mode 100644 index 1659945fe0..0000000000 --- a/packages/core-tester-cli/lib/utils/logger.js +++ /dev/null @@ -1,7 +0,0 @@ -const pino = require('pino') - -module.exports = pino({ - name: 'ark-tester-cli', - safe: true, - prettyPrint: true, -}) diff --git a/packages/core-tester-cli/lib/utils/paginate.js b/packages/core-tester-cli/lib/utils/paginate.js deleted file mode 100644 index 671fb7cb7b..0000000000 --- a/packages/core-tester-cli/lib/utils/paginate.js +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const request = require('./request') - -module.exports = async (config, endpoint, limit) => { - const data = [] - let page = 1 - let maxPages = null - while (maxPages === null || page <= maxPages) { - const response = await request(config).get(`${endpoint}?page=${page}`) - if (response) { - page++ - maxPages = response.meta.pageCount - data.push(...response.data) - } else { - break - } - } - - return data -} diff --git a/packages/core-tester-cli/lib/utils/request.js b/packages/core-tester-cli/lib/utils/request.js deleted file mode 100644 index a8b4e2455d..0000000000 --- a/packages/core-tester-cli/lib/utils/request.js +++ /dev/null @@ -1,28 +0,0 @@ -const axios = require('axios') - -module.exports = config => { - const headers = {} - if (config && config.network) { - headers.nethash = config.network.nethash - headers.version = '2.0.0' - headers.port = config.p2pPort - headers['Content-Type'] = 'application/json' - } - - return { - get: async (endpoint, isP2P) => { - const baseUrl = `${config.baseUrl}:${ - isP2P ? config.p2pPort : config.apiPort - }` - - return (await axios.get(baseUrl + endpoint, { headers })).data - }, - post: async (endpoint, data, isP2P) => { - const baseUrl = `${config.baseUrl}:${ - isP2P ? config.p2pPort : config.apiPort - }` - - return (await axios.post(baseUrl + endpoint, data, { headers })).data - }, - } -} diff --git a/packages/core-tester-cli/package.json b/packages/core-tester-cli/package.json index 45fadeb55f..e621560123 100644 --- a/packages/core-tester-cli/package.json +++ b/packages/core-tester-cli/package.json @@ -1,46 +1,80 @@ { - "name": "@arkecosystem/core-tester-cli", - "description": "Tester CLI for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust ", - "Alex Barnsley " - ], - "license": "MIT", - "main": "lib/index.js", - "bin": { - "ark:tester": "./bin/tester" - }, - "scripts": { - "start": "./bin/tester", - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "axios": "^0.18.0", - "bip39": "^2.5.0", - "clipboardy": "^1.2.3", - "commander": "^2.19.0", - "delay": "^4.1.0", - "lodash.fill": "^3.4.0", - "pino": "^5.9.0", - "pino-pretty": "^2.3.0", - "pluralize": "^7.0.0", - "superheroes": "^2.0.0" - }, - "devDependencies": { - "axios-mock-adapter": "^1.15.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-tester-cli", + "description": "Tester CLI for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust ", + "Alex Barnsley " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "/bin", + "/dist", + "/oclif.manifest.json" + ], + "bin": { + "tester": "./bin/run" + }, + "scripts": { + "tester": "./bin/run", + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "prepack": "oclif-dev manifest && npm shrinkwrap", + "postpack": "rm -f oclif.manifest.json", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@oclif/command": "^1.5.8", + "@oclif/config": "^1.12.4", + "@oclif/plugin-help": "^2.1.6", + "@oclif/plugin-not-found": "^1.2.2", + "@types/bip39": "^2.4.1", + "@types/clipboardy": "^1.1.0", + "@types/lodash.fill": "^3.4.4", + "@types/pino": "^5.8.3", + "@types/pluralize": "^0.0.29", + "axios": "^0.18.0", + "bip39": "^2.5.0", + "clipboardy": "^1.2.3", + "delay": "^4.1.0", + "lodash.fill": "^3.4.0", + "pino": "^5.10.1", + "pino-pretty": "^2.5.0", + "pluralize": "^7.0.0", + "superheroes": "^2.0.0" + }, + "devDependencies": { + "axios-mock-adapter": "^1.15.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + }, + "oclif": { + "commands": "./dist/commands", + "bin": "snapshot", + "plugins": [ + "@oclif/plugin-help", + "@oclif/plugin-not-found" + ] + } } diff --git a/packages/core-tester-cli/src/commands/command.ts b/packages/core-tester-cli/src/commands/command.ts new file mode 100644 index 0000000000..208133a5da --- /dev/null +++ b/packages/core-tester-cli/src/commands/command.ts @@ -0,0 +1,318 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import { Bignum, crypto } from "@arkecosystem/crypto"; +import Command, { flags } from "@oclif/command"; +import bip39 from "bip39"; +import clipboardy from "clipboardy"; +import delay from "delay"; +import fs from "fs"; +import path from "path"; +import pluralize from "pluralize"; +import { config } from "../config"; +import { customFlags } from "../flags"; +import { logger, paginate, request } from "../utils"; + +export abstract class BaseCommand extends Command { + public static flags = { + number: flags.integer({ + description: "number of wallets", + default: 10, + }), + amount: customFlags.number({ + description: "initial wallet token amount", + default: 2, + }), + transferFee: customFlags.number({ + description: "transfer fee", + default: 0.1, + }), + baseUrl: flags.string({ + description: "base api url", + }), + apiPort: flags.integer({ + description: "base api port", + default: 4003, + }), + p2pPort: flags.integer({ + description: "base p2p port", + default: 4002, + }), + passphrase: flags.string({ + description: "passphrase of initial wallet", + }), + secondPassphrase: flags.string({ + description: "second passphrase of initial wallet", + }), + skipValidation: flags.boolean({ + description: "skip transaction validations", + }), + skipTesting: flags.boolean({ + description: "skip testing", + }), + copy: flags.boolean({ + description: "copy the transactions to the clipboard", + }), + }; + + public options: any; + public config: any; + + /** + * Init new instance of command. + * @param {Object} options + * @return {*} + */ + public async initialize(command): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(command); + + this.options = flags; + this.applyConfig(); + await this.loadConstants(); + await this.loadNetworkConfig(); + + return { flags }; + } + + /** + * Copy transactions to clipboard. + * @param {Object[]} transactions + * @return {void} + */ + public copyToClipboard(transactions) { + for (const transaction of transactions) { + transaction.serialized = transaction.serialized.toString("hex"); + } + + clipboardy.writeSync(JSON.stringify(transactions)); + logger.info(`Copied ${pluralize("transaction", transactions.length, true)}`); + } + + /** + * Generate wallets based on quantity. + * @param {Number} [quantity] + * @return {Object[]} + */ + public generateWallets(quantity: any = null) { + if (!quantity) { + quantity = this.options.number; + } + + const wallets = []; + for (let i = 0; i < quantity; i++) { + const passphrase = bip39.generateMnemonic(); + const keys = crypto.getKeys(passphrase); + const address = crypto.getAddress(keys.publicKey, this.config.network.version); + + wallets.push({ address, keys, passphrase }); + } + + const testWalletsPath = path.resolve(__dirname, "../../test-wallets"); + fs.appendFileSync(testWalletsPath, `${new Date().toLocaleDateString()} ${"-".repeat(70)}\n`); + for (const wallet of wallets) { + fs.appendFileSync(testWalletsPath, `${wallet.address}: ${wallet.passphrase}\n`); + } + + return wallets; + } + + /** + * Get delegate API response. + * @return {Object[]} + * @throws 'Could not get delegates' + */ + public async getDelegates() { + try { + const delegates = await paginate(this.config, "/api/v2/delegates"); + + return delegates; + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + throw new Error(`Could not get delegates: ${message}`); + } + } + + /** + * Get transaction from API by ID. + * @param {String} id + * @return {(Object|null)} + */ + public async getTransaction(id) { + try { + const response = await request(this.config).get(`/api/v2/transactions/${id}`); + + if (response.data) { + return response.data; + } + } catch (error) { + // + } + + return null; + } + + /** + * Get delegate voters by public key. + * @param {String} publicKey + * @return {Object[]} + */ + public async getVoters(publicKey) { + try { + return paginate(this.config, `/api/v2/delegates/${publicKey}/voters`); + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + throw new Error(`Could not get voters for '${publicKey}': ${message}`); + } + } + + /** + * Get wallet balance by address. + * @param {String} address + * @return {Bignum} + */ + public async getWalletBalance(address) { + try { + return bignumify((await this.getWallet(address)).balance); + } catch (error) { + // + } + + return Bignum.ZERO; + } + + /** + * Get wallet by address. + * @param {String} address + * @return {Object} + */ + public async getWallet(address) { + try { + const response = await request(this.config).get(`/api/v2/wallets/${address}`); + + if (response.data) { + return response.data; + } + + return null; + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + throw new Error(`Could not get wallet for '${address}': ${message}`); + } + } + + /** + * Send transactions to API and wait for response. + * @param {Object[]} transactions + * @param {String} [transactionType] + * @param {Boolean} [wait=true] + * @return {Object} + */ + public async sendTransactions(transactions, transactionType: any = null, wait = true) { + const response = await this.postTransactions(transactions); + + if (wait) { + const delaySeconds = this.getTransactionDelaySeconds(transactions); + transactionType = `${transactionType ? `${transactionType} ` : ""}transactions`; + logger.info(`Waiting ${delaySeconds} seconds to apply ${transactionType}`); + await delay(delaySeconds * 1000); + } + + return response; + } + + /** + * Send transactions to API. + * @param {Object[]} transactions + * @return {Object} + */ + public async postTransactions(transactions) { + try { + const response = await request(this.config).post("/api/v2/transactions", { + transactions, + }); + return response.data; + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + throw new Error(`Could not post transactions: ${message}`); + } + } + + /** + * Load constants from API and apply to config. + * @return {void} + */ + public async loadConstants() { + try { + this.config.constants = (await request(this.config).get("/api/v2/node/configuration")).data.constants; + } catch (error) { + logger.error("Failed to get constants: ", error.message); + process.exit(1); + } + } + + /** + * Load network from API and apply to config. + * @return {void} + */ + public async loadNetworkConfig() { + try { + this.config.network = (await request(this.config).get("/config", true)).data.network; + } catch (error) { + logger.error("Failed to get network config: ", error.message); + process.exit(1); + } + } + + /** + * Apply options to config. + * @return {void} + */ + protected applyConfig() { + this.config = { ...config }; + + if (this.options.baseUrl) { + this.config.baseUrl = this.options.baseUrl.replace(/\/+$/, ""); + } + + if (this.options.apiPort) { + this.config.apiPort = this.options.apiPort; + } + + if (this.options.p2pPort && process.env.NODE_ENV !== "test") { + this.config.p2pPort = this.options.p2pPort; + } + + if (this.options.passphrase) { + this.config.passphrase = this.options.passphrase; + } + + if (this.options.secondPassphrase) { + this.config.secondPassphrase = this.options.secondPassphrase; + } + } + + /** + * Quit command and output error when problem sending transactions. + * @param {Error} error + * @return {void} + */ + protected problemSendingTransactions(error) { + const message = error.response ? error.response.data.message : error.message; + logger.error(`There was a problem sending transactions: ${message}`); + process.exit(1); + } + + /** + * Determine how long to wait for transactions to process. + * @param {Object[]} transactions + * @return {Number} + */ + protected getTransactionDelaySeconds(transactions) { + if (process.env.NODE_ENV === "test") { + return 0; + } + + const waitPerBlock = Math.round(this.config.constants.blocktime / 10) * 20; + + return waitPerBlock * Math.ceil(transactions.length / this.config.constants.block.maxTransactions); + } +} diff --git a/packages/core-tester-cli/src/commands/delegate-registration.ts b/packages/core-tester-cli/src/commands/delegate-registration.ts new file mode 100644 index 0000000000..4ba3ea2358 --- /dev/null +++ b/packages/core-tester-cli/src/commands/delegate-registration.ts @@ -0,0 +1,111 @@ +import { client } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import pluralize from "pluralize"; +import superheroes from "superheroes"; +import { customFlags } from "../flags"; +import { arktoshiToArk, logger, parseFee } from "../utils"; +import { BaseCommand } from "./command"; +import { TransferCommand } from "./transfer"; + +export class DelegateRegistrationCommand extends BaseCommand { + public static description: string = "create multiple delegates"; + + public static flags = { + ...BaseCommand.flags, + delegateFee: customFlags.number({ + description: "delegate registration fee", + default: 25, + }), + }; + + /** + * Run delegate-registration command. + * @return {void} + */ + public async run(): Promise { + await this.initialize(DelegateRegistrationCommand); + + const wallets = this.generateWallets(); + + for (const wallet of wallets) { + await TransferCommand.run([ + "--amount", + String(this.options.amount || 25), + "--recipient", + wallet.address, + "--skipTesting", + ]); + } + + const delegates = await this.getDelegates(); + + logger.error( + `Sending ${this.options.number} delegate registration ${pluralize("transaction", this.options.number)}`, + ); + + if (!this.options.skipValidation) { + logger.error(`Starting delegate count: ${delegates.length}`); + } + + const transactions = []; + const usedDelegateNames = delegates.map(delegate => delegate.username); + + wallets.forEach((wallet, i) => { + while (!wallet.username || usedDelegateNames.includes(wallet.username)) { + wallet.username = superheroes.random(); + } + + wallet.username = wallet.username.toLowerCase().replace(/ /g, "_"); + usedDelegateNames.push(wallet.username); + + const transaction = client + .getBuilder() + .delegateRegistration() + .fee(parseFee(this.options.delegateFee)) + .usernameAsset(wallet.username) + .network(this.config.network.version) + .sign(wallet.passphrase) + .secondSign(this.config.secondPassphrase) + .build(); + + transactions.push(transaction); + + logger.info( + `${i} ==> ${transaction.id}, ${wallet.address} (fee: ${arktoshiToArk(transaction.fee)}, username: ${ + wallet.username + })`, + ); + }); + + if (this.options.copy) { + this.copyToClipboard(transactions); + return; + } + + const expectedDelegates = delegates.length + wallets.length; + if (!this.options.skipValidation) { + logger.info(`Expected end delegate count: ${expectedDelegates}`); + } + + try { + await this.sendTransactions(transactions, "delegate", !this.options.skipValidation); + + if (this.options.skipValidation) { + return; + } + + const targetDelegates = await this.getDelegates(); + logger.info(`All transactions have been sent! Total delegates: ${targetDelegates.length}`); + + if (targetDelegates.length !== expectedDelegates) { + logger.error( + `Delegate count incorrect. '${targetDelegates.length}' but should be '${expectedDelegates}'`, + ); + } + } catch (error) { + logger.error( + `There was a problem sending transactions: ${error.response ? error.response.data.message : error}`, + ); + } + } +} diff --git a/packages/core-tester-cli/src/commands/multi-signature.ts b/packages/core-tester-cli/src/commands/multi-signature.ts new file mode 100644 index 0000000000..655a2d57e3 --- /dev/null +++ b/packages/core-tester-cli/src/commands/multi-signature.ts @@ -0,0 +1,349 @@ +import { client } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import take from "lodash/take"; +import pluralize from "pluralize"; +import { customFlags } from "../flags"; +import { arkToArktoshi, arktoshiToArk, generateTransactions, logger, parseFee } from "../utils"; +import { BaseCommand } from "./command"; +import { TransferCommand } from "./transfer"; + +export class MultiSignatureCommand extends BaseCommand { + public static description: string = "create multiple multisig wallets"; + + public static flags = { + ...BaseCommand.flags, + multisigFee: customFlags.number({ + description: "multisig fee", + default: 5, + }), + min: flags.integer({ + description: "minimum number of signatures per transaction", + default: 2, + }), + lifetime: flags.integer({ + description: "lifetime of transaction", + default: 72, + }), + quantity: flags.integer({ + description: "number of signatures per wallet", + default: 3, + }), + skipTests: flags.boolean({ + description: "skip transaction tests", + }), + }; + + /** + * Run multi-signature command. + * @return {void} + */ + public async run(): Promise { + this.initialize(MultiSignatureCommand); + + const approvalWallets = this.generateWallets(this.options.quantity); + const publicKeys = approvalWallets.map(wallet => `+${wallet.keys.publicKey}`); + const min = this.options.min ? Math.min(this.options.min, publicKeys.length) : publicKeys.length; + + const testCosts = this.options.skipTests ? 1 : 2; + const wallets = this.generateWallets(); + + for (const wallet of wallets) { + await TransferCommand.run([ + "--recipient", + wallet.address, + "--amount", + (publicKeys.length + 1) * 5 + testCosts, + "--skipTesting", + ]); + } + + const transactions = this.generateTransactions(wallets, approvalWallets, publicKeys, min); + + if (this.options.copy) { + this.copyToClipboard(transactions); + + return; + } + + try { + const response = await this.sendTransactions(transactions, "multi-signature", !this.options.skipValidation); + + if (!this.options.skipValidation) { + let hasUnprocessed = false; + for (const transaction of transactions) { + if (!response.accept.includes(transaction.id)) { + hasUnprocessed = true; + logger.error(`Multi-signature transaction '${transaction.id}' was not processed`); + } + } + if (hasUnprocessed) { + process.exit(1); + } + + for (const transaction of transactions) { + const tx = await this.getTransaction(transaction.id); + if (!tx) { + logger.error(`Transaction '${transaction.id}' should be on the blockchain`); + } + } + } + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + logger.error(`There was a problem sending multi-signature transactions: ${message}`); + process.exit(1); + } + + if (this.options.skipTests || this.options.skipValidation) { + return; + } + + await this.testSendWithSignatures(wallets, approvalWallets); + await this.testSendWithMinSignatures(wallets, approvalWallets, min); + await this.testSendWithBelowMinSignatures(wallets, approvalWallets, min); + await this.testSendWithoutSignatures(wallets); + await this.testSendWithEmptySignatures(wallets); + await this.testNewMultiSignatureRegistration(wallets, approvalWallets, publicKeys, min); + } + + /** + * Generate batch of transactions based on wallets + * @param {Object[]} wallets + * @param {Object[]} [approvalWallets=[]] + * @param {String[]} [publicKeys=[]] + * @param {Number} [min=2] + * @param {Boolean} [log=true] + * @return {Object[]} + */ + public generateTransactions(wallets, approvalWallets = [], publicKeys = [], min = 2, log = true) { + const transactions = []; + wallets.forEach((wallet, i) => { + const builder = client.getBuilder().multiSignature(); + + builder + .fee(parseFee(this.options.multisigFee)) + .multiSignatureAsset({ + lifetime: this.options.lifetime, + keysgroup: publicKeys, + min, + }) + .network(this.config.network.version) + .sign(wallet.passphrase); + + if (wallet.secondPassphrase || this.config.secondPassphrase) { + builder.secondSign(wallet.secondPassphrase || this.config.secondPassphrase); + } + + if (approvalWallets) { + for (let j = approvalWallets.length - 1; j >= 0; j--) { + builder.multiSignatureSign(approvalWallets[j].passphrase); + } + } + + const transaction = builder.build(); + transactions.push(transaction); + + if (log) { + logger.info(`${i} ==> ${transaction.id}, ${wallet.address} (fee: ${arktoshiToArk(transaction.fee)})`); + } + }); + + return transactions; + } + + /** + * Send transactions with approver signatures. + * @param {Object[]} wallets + * @param {Object[]} [approvalWallets=[]] + * @return {void} + */ + public async testSendWithSignatures(wallets, approvalWallets = []) { + logger.info("Sending transactions with signatures"); + + const transactions = generateTransactions(arkToArktoshi(2), wallets, approvalWallets, { + config: this.config, + ...this.options, + }); + + try { + await this.sendTransactions(transactions); + for (const transaction of transactions) { + const tx = await this.getTransaction(transaction.id); + if (!tx) { + logger.error(`Transaction '${transaction.id}' should be on the blockchain`); + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } + + /** + * Send transactions with min approver signatures. + * @param {Object[]} wallets + * @param {Object[]} [approvalWallets=[]] + * @param {Number} [min=2] + * @return {void} + */ + public async testSendWithMinSignatures(wallets, approvalWallets = [], min = 2) { + logger.info( + `Sending transactions with ${min} (min) of ${pluralize("signature", approvalWallets.length, true)}`, + ); + + const transactions = generateTransactions(arkToArktoshi(2), wallets, take(approvalWallets, min), { + config: this.config, + ...this.options, + }); + + try { + await this.sendTransactions(transactions); + for (const transaction of transactions) { + const tx = await this.getTransaction(transaction.id); + if (!tx) { + logger.error(`Transaction '${transaction.id}' should be on the blockchain`); + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } + + /** + * Send transactions with below min approver signatures. + * @param {Object[]} wallets + * @param {Object[]} [approvalWallets=[]] + * @param {Number} [min=2] + * @return {void} + */ + public async testSendWithBelowMinSignatures(wallets, approvalWallets = [], min = 2) { + const max = min - 1; + logger.info( + `Sending transactions with ${max} (below min) of ${pluralize("signature", approvalWallets.length, true)}`, + ); + + const transactions = generateTransactions(arkToArktoshi(2), wallets, take(approvalWallets, max), { + config: this.config, + ...this.options, + }); + + try { + await this.sendTransactions(transactions); + for (const transaction of transactions) { + try { + const tx = await this.getTransaction(transaction.id); + if (tx) { + logger.error(`Transaction '${transaction.id}' should not be on the blockchain`); + } + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + if (message !== "Transaction not found") { + logger.error(`Failed to check transaction '${transaction.id}': ${message}`); + } + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } + + /** + * Send transactions without approver signatures. + * @param {Object[]} wallets + * @return {void} + */ + public async testSendWithoutSignatures(wallets) { + logger.info("Sending transactions without signatures"); + + const transactions = generateTransactions(arkToArktoshi(2), wallets, [], { + config: this.config, + ...this.options, + }); + + try { + await this.sendTransactions(transactions); + for (const transaction of transactions) { + try { + const tx = await this.getTransaction(transaction.id); + if (tx) { + logger.error(`Transaction '${transaction.id}' should not be on the blockchain`); + } + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + if (message !== "Transaction not found") { + logger.error(`Failed to check transaction '${transaction.id}': ${message}`); + } + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } + + /** + * Send transactions with empty approver signatures. + * @param {Object[]} wallets + * @return {void} + */ + public async testSendWithEmptySignatures(wallets) { + logger.info("Sending transactions with empty signatures"); + + const transactions = generateTransactions(arkToArktoshi(2), wallets, [], { + config: this.config, + ...this.options, + }); + for (const transaction of transactions) { + transaction.data.signatures = []; + } + + try { + await this.sendTransactions(transactions); + for (const transaction of transactions) { + try { + const tx = await this.getTransaction(transaction.id); + if (tx) { + logger.error(`Transaction '${transaction.id}' should not be on the blockchain`); + } + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + if (message !== "Transaction not found") { + logger.error(`Failed to check transaction '${transaction.id}': ${message}`); + } + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } + + /** + * Send transactions to re-register multi-signature wallets. + * @param {Object[]} wallets + * @param {Object[]} [approvalWallets=[]] + * @param {Object[]} [publicKeys=[]] + * @param {Number} [min=2] + * @return {void} + */ + public async testNewMultiSignatureRegistration(wallets, approvalWallets = [], publicKeys = [], min = 2) { + logger.info("Sending transactions to re-register multi-signature"); + + const transactions = this.generateTransactions(wallets, approvalWallets, publicKeys, min); + + try { + await this.sendTransactions(transactions); + for (const transaction of transactions) { + try { + const tx = await this.getTransaction(transaction.id); + if (tx) { + logger.error(`Transaction '${transaction.id}' should not be on the blockchain`); + } + } catch (error) { + const message = error.response ? error.response.data.message : error.message; + if (message !== "Transaction not found") { + logger.error(`Failed to check transaction '${transaction.id}': ${message}`); + } + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } +} diff --git a/packages/core-tester-cli/src/commands/second-signature.ts b/packages/core-tester-cli/src/commands/second-signature.ts new file mode 100644 index 0000000000..09bb8586dc --- /dev/null +++ b/packages/core-tester-cli/src/commands/second-signature.ts @@ -0,0 +1,88 @@ +import { client } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import pluralize from "pluralize"; +import { customFlags } from "../flags"; +import { arktoshiToArk, logger, parseFee } from "../utils"; +import { BaseCommand } from "./command"; +import { TransferCommand } from "./transfer"; + +export class SecondSignatureCommand extends BaseCommand { + public static description: string = "create wallets with second signature"; + + public static flags = { + ...BaseCommand.flags, + signatureFee: customFlags.number({ + description: "second signature fee", + default: 5, + }), + }; + + /** + * Run second-signature command. + * @return {void} + */ + public async run(): Promise { + await this.initialize(SecondSignatureCommand); + + const wallets = this.generateWallets(); + + for (const wallet of wallets) { + await TransferCommand.run([ + "--recipient", + wallet.address, + "--amount", + String(this.options.amount || 5), + "--skipTesting", + ]); + } + + logger.info(`Sending ${this.options.number} second signature ${pluralize("transaction", this.options.number)}`); + + const transactions = []; + wallets.forEach((wallet, i) => { + wallet.secondPassphrase = this.config.secondPassphrase || wallet.passphrase; + const transaction = client + .getBuilder() + .secondSignature() + .fee(parseFee(this.options.signatureFee)) + .signatureAsset(wallet.secondPassphrase) + .network(this.config.network.version) + .sign(wallet.passphrase) + .build(); + + wallet.publicKey = transaction.senderPublicKey; + wallet.secondPublicKey = transaction.asset.signature.publicKey; + transactions.push(transaction); + + logger.info(`${i} ==> ${transaction.id}, ${wallet.address} (fee: ${arktoshiToArk(transaction.fee)})`); + }); + + if (this.options.copy) { + this.copyToClipboard(transactions); + return; + } + + try { + await this.sendTransactions(transactions, "second-signature", !this.options.skipValidation); + + if (this.options.skipValidation) { + return; + } + + for (const walletObject of wallets) { + const wallet = await this.getWallet(walletObject.address); + + if ( + wallet.secondPublicKey !== walletObject.secondPublicKey || + wallet.publicKey !== walletObject.publicKey + ) { + logger.error(`Invalid second signature for ${walletObject.address}.`); + } + } + } catch (error) { + logger.error( + `There was a problem sending transactions: ${error.response ? error.response.data.message : error}`, + ); + } + } +} diff --git a/packages/core-tester-cli/src/commands/transfer.ts b/packages/core-tester-cli/src/commands/transfer.ts new file mode 100644 index 0000000000..e3cc9e9288 --- /dev/null +++ b/packages/core-tester-cli/src/commands/transfer.ts @@ -0,0 +1,315 @@ +import { Bignum, crypto } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import delay from "delay"; +import unique from "lodash/uniq"; +import pluralize from "pluralize"; +import { arkToArktoshi, arktoshiToArk, generateTransactions, logger } from "../utils"; +import { BaseCommand } from "./command"; + +export class TransferCommand extends BaseCommand { + public static description: string = "send multiple transactions"; + + public static flags = { + ...BaseCommand.flags, + recipient: flags.string({ + description: "recipient address", + }), + floodAttempts: flags.integer({ + description: "flood node with same transactions", + default: 0, + }), + skipSecondRun: flags.string({ + description: "skip second sending of transactions", + }), + smartBridge: flags.string({ + description: "smart-bridge value to use", + }), + }; + + /** + * Run transfer command. + * @param {Object} options + * @return {void} + */ + public async run(): Promise { + await this.initialize(TransferCommand); + + const primaryAddress = crypto.getAddress( + crypto.getKeys(this.config.passphrase).publicKey, + this.config.network.version, + ); + + let wallets = this.options.wallets; + if (wallets === undefined) { + wallets = this.generateWallets(); + } + + logger.info(`Sending ${wallets.length} transfer ${pluralize("transaction", wallets.length)}`); + + const walletBalance = await this.getWalletBalance(primaryAddress); + + if (!this.options.skipValidation) { + logger.info(`Sender starting balance: ${arktoshiToArk(walletBalance)}`); + } + + let totalDeductions = Bignum.ZERO; + const transactionAmount = arkToArktoshi(this.options.amount || 2); + + const transactions = this.generateTransactions(transactionAmount, wallets, null, true); + for (const transaction of transactions) { + totalDeductions = totalDeductions.plus(transactionAmount).plus(transaction.fee); + } + + if (this.options.copy) { + this.copyToClipboard(transactions); + return; + } + + const expectedSenderBalance = new Bignum(walletBalance).minus(totalDeductions); + if (!this.options.skipValidation) { + logger.info(`Sender expected ending balance: ${arktoshiToArk(expectedSenderBalance)}`); + } + + const runOptions = { + primaryAddress, + transactions, + wallets, + transactionAmount, + expectedSenderBalance, + skipValidation: this.options.skipValidation, + }; + + try { + if (!this.options.floodAttempts) { + const successfulTest = await this.performRun(runOptions, 1); + if ( + successfulTest && + !this.options.skipSecondRun && + !this.options.skipValidation && + !this.options.skipTesting + ) { + await this.performRun(runOptions, 2, false, true); + } + } else { + const attempts = this.options.floodAttempts; + for (let i = attempts; i > 0; i--) { + await this.performRun(runOptions, attempts - i + 1, i !== 1, i !== attempts); + } + } + } catch (error) { + const message = error.response ? error.response.data.message : error; + logger.error(`There was a problem sending transactions: ${message}`); + } + + if (this.options.skipValidation) { + return; + } + + await this.testVendorField(wallets); + await this.testEmptyVendorField(wallets); + + return; + } + + /** + * Generate batch of transactions based on wallets. + * @param {Bignum} transactionAmount + * @param {Object[]} wallets + * @param {Object[]} [approvalWallets=[]] + * @param {Boolean} [overridePassphrase=false] + * @param {String} [vendorField] + * @param {Boolean} [log=true] + * @return {Object[]} + */ + public generateTransactions( + transactionAmount, + wallets, + approvalWallets = [], + overridePassphrase = false, + vendorField = null, + log = true, + ) { + return generateTransactions(transactionAmount, wallets, approvalWallets, { + ...this.options, + config: this.config, + overridePassphrase, + vendorField: vendorField || this.options.smartBridge, + log, + }); + } + + /** + * Perform a run of transactions. + * @param {Object} runOptions + * @param {Number} [runNumber=1] + * @param {Boolean} [skipWait=false] + * @param {Boolean} [isSubsequentRun=false] + * @return {Boolean} + */ + public async performRun(runOptions, runNumber = 1, skipWait = false, isSubsequentRun = false) { + if (skipWait) { + runOptions.skipValidation = true; + this.sendTransactionsWithResults(runOptions, isSubsequentRun); + + return true; + } + + if (await this.sendTransactionsWithResults(runOptions, isSubsequentRun)) { + logger.info(`All transactions have been received and forged for run ${runNumber}!`); + + return true; + } + + logger.error(`Test failed on run ${runNumber}`); + + return false; + } + + /** + * Send transactions and validate results. + * @param {Object} runOptions + * @param {Boolean} isSubsequentRun + * @return {Boolean} + */ + public async sendTransactionsWithResults(runOptions, isSubsequentRun) { + let successfulTest = true; + + let postResponse; + try { + postResponse = await this.postTransactions(runOptions.transactions); + } catch (error) { + if (runOptions.skipValidation) { + return true; + } + + const message = error.response ? error.response.data.error : error.message; + logger.error(`Transaction request failed: ${message}`); + + return false; + } + + if (runOptions.skipValidation) { + return true; + } + + if (!isSubsequentRun && (!postResponse.accept || !postResponse.accept.length)) { + return false; + } + + if (!isSubsequentRun) { + for (const transaction of runOptions.transactions) { + if (!postResponse.accept.includes(transaction.id)) { + logger.error(`Transaction '${transaction.id}' didn't get approved on the network`); + + successfulTest = false; + } + } + } + + for (const key of Object.keys(postResponse)) { + if (key === "success") { + continue; + } + + const dataLength = postResponse[key].length; + const uniqueLength = unique(postResponse[key]).length; + if (dataLength !== uniqueLength) { + logger.error(`Response data for '${key}' has ${dataLength - uniqueLength} duplicate transaction ids`); + successfulTest = false; + } + } + + const delaySeconds = this.getTransactionDelaySeconds(runOptions.transactions); + logger.info(`Waiting ${delaySeconds} seconds to apply transfer transactions`); + await delay(delaySeconds * 1000); + + for (const transaction of runOptions.transactions) { + const transactionResponse = await this.getTransaction(transaction.id); + if (transactionResponse && transactionResponse.id !== transaction.id) { + logger.error(`Transaction '${transaction.id}' didn't get applied on the network`); + + successfulTest = false; + } + } + + if (runOptions.primaryAddress && runOptions.expectedSenderBalance) { + const walletBalance = await this.getWalletBalance(runOptions.primaryAddress); + if (!walletBalance.isEqualTo(runOptions.expectedSenderBalance)) { + successfulTest = false; + logger.error( + `Sender balance incorrect: '${arktoshiToArk(walletBalance)}' but should be '${arktoshiToArk( + runOptions.expectedSenderBalance, + )}'`, + ); + } + } + + for (const wallet of runOptions.wallets) { + const balance = await this.getWalletBalance(wallet.address); + if (!balance.isEqualTo(runOptions.transactionAmount)) { + successfulTest = false; + logger.error( + `Incorrect destination balance for ${wallet.address}. Should be '${arktoshiToArk( + runOptions.transactionAmount, + )}' but is '${arktoshiToArk(balance)}'`, + ); + } + } + + return successfulTest; + } + + /** + * Test vendor field is set correctly on blockchain. + * @param {Object[]} wallets + * @return {void} + */ + public async testVendorField(wallets) { + logger.info("Testing VendorField value is set correctly"); + + const transactions = this.generateTransactions(arkToArktoshi(2), wallets, null, null, "Testing VendorField"); + + try { + await this.sendTransactions(transactions); + + for (const transaction of transactions) { + const tx = await this.getTransaction(transaction.id); + if (!tx) { + logger.error(`Transaction '${transaction.id}' should be on the blockchain`); + } else if (tx.vendorField !== "Testing VendorField") { + logger.error(`Transaction '${transaction.id}' does not have correct vendorField value`); + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } + + /** + * Test empty vendor field is set correctly on blockchain. + * @param {Object[]} wallets + * @return {void} + */ + public async testEmptyVendorField(wallets) { + logger.info("Testing empty VendorField value"); + + const transactions = this.generateTransactions(arkToArktoshi(2), wallets, null, null, null); + + try { + await this.sendTransactions(transactions); + + for (const transaction of transactions) { + const tx = await this.getTransaction(transaction.id); + if (!tx) { + logger.error(`Transaction '${transaction.id}' should be on the blockchain`); + } else if (tx.vendorField) { + logger.error( + `Transaction '${transaction.id}' should not have vendorField value '${tx.vendorField}'`, + ); + } + } + } catch (error) { + this.problemSendingTransactions(error); + } + } +} diff --git a/packages/core-tester-cli/src/commands/vote.ts b/packages/core-tester-cli/src/commands/vote.ts new file mode 100644 index 0000000000..ae65bd09c7 --- /dev/null +++ b/packages/core-tester-cli/src/commands/vote.ts @@ -0,0 +1,97 @@ +import { client } from "@arkecosystem/crypto"; +import { flags } from "@oclif/command"; +import sample from "lodash/sample"; +import pluralize from "pluralize"; +import { customFlags } from "../flags"; +import { arktoshiToArk, logger, parseFee } from "../utils"; +import { BaseCommand } from "./command"; +import { TransferCommand } from "./transfer"; + +export class VoteCommand extends BaseCommand { + public static description: string = "create multiple votes for a delegate"; + + public static flags = { + ...BaseCommand.flags, + delegate: flags.string({ + description: "delegate public key", + }), + voteFee: customFlags.number({ + description: "vote fee", + default: 1, + }), + }; + + /** + * Run vote command. + * @return {void} + */ + public async run(): Promise { + await this.initialize(VoteCommand); + + const wallets = this.generateWallets(); + + for (const wallet of wallets) { + await TransferCommand.run(["--recipient", wallet.address, "--amount", String(2), "--skipTesting"]); + } + + let delegate = this.options.delegate; + if (!delegate) { + try { + delegate = sample(await this.getDelegates()).publicKey; + } catch (error) { + logger.error(error); + return; + } + } + + const voters = await this.getVoters(delegate); + logger.info(`Sending ${this.options.number} vote ${pluralize("transaction", this.options.number)}`); + + const transactions = []; + wallets.forEach((wallet, i) => { + const transaction = client + .getBuilder() + .vote() + .fee(parseFee(this.options.voteFee)) + .votesAsset([`+${delegate}`]) + .network(this.config.network.version) + .sign(wallet.passphrase) + .secondSign(this.config.secondPassphrase) + .build(); + + transactions.push(transaction); + + logger.info(`${i} ==> ${transaction.id}, ${wallet.address} (fee: ${arktoshiToArk(transaction.fee)})`); + }); + + if (this.options.copy) { + this.copyToClipboard(transactions); + return; + } + + const expectedVoterCount = voters.length + wallets.length; + if (!this.options.skipValidation) { + logger.info(`Expected end voters: ${expectedVoterCount}`); + } + + try { + await this.sendTransactions(transactions, "vote", !this.options.skipValidation); + + if (this.options.skipValidation) { + return; + } + + const voterCount = (await this.getVoters(delegate)).length; + + logger.info(`All transactions have been sent! Total voters: ${voterCount}`); + + if (voterCount !== expectedVoterCount) { + logger.error(`Delegate voter count incorrect. '${voterCount}' but should be '${expectedVoterCount}'`); + } + } catch (error) { + logger.error( + `There was a problem sending transactions: ${error.response ? error.response.data.message : error}`, + ); + } + } +} diff --git a/packages/core-tester-cli/src/config.ts b/packages/core-tester-cli/src/config.ts new file mode 100644 index 0000000000..2088db247e --- /dev/null +++ b/packages/core-tester-cli/src/config.ts @@ -0,0 +1,7 @@ +export const config = Object.freeze({ + apiPort: 4003, + p2pPort: 4000, + baseUrl: "http://localhost", + passphrase: "prison tobacco acquire stone dignity palace note decade they current lesson robot", + secondPassphrase: "", +}); diff --git a/packages/core-tester-cli/src/flags.ts b/packages/core-tester-cli/src/flags.ts new file mode 100644 index 0000000000..a75effe727 --- /dev/null +++ b/packages/core-tester-cli/src/flags.ts @@ -0,0 +1,14 @@ +import { flags as oclifFlags } from "@oclif/command"; + +export const customFlags = { + number: oclifFlags.build({ + parse: input => { + const value = Number(input); + if (value < 1 / 1e8) { + throw new Error(`Expected number greater than 1 satoshi.`); + } + + return value; + }, + }), +}; diff --git a/packages/core-tester-cli/src/index.ts b/packages/core-tester-cli/src/index.ts new file mode 100644 index 0000000000..8bdb76f9a0 --- /dev/null +++ b/packages/core-tester-cli/src/index.ts @@ -0,0 +1 @@ +export { run } from "@oclif/command"; diff --git a/packages/core-tester-cli/src/utils.ts b/packages/core-tester-cli/src/utils.ts new file mode 100644 index 0000000000..0d84a55c1c --- /dev/null +++ b/packages/core-tester-cli/src/utils.ts @@ -0,0 +1,142 @@ +import { bignumify } from "@arkecosystem/core-utils"; +import { Bignum, client, formatArktoshi } from "@arkecosystem/crypto"; +import axios from "axios"; +import pino from "pino"; + +export const logger = pino({ + name: "core-tester-cli", + safe: true, + prettyPrint: true, +}); + +export function request(config) { + const headers: any = {}; + if (config && config.network) { + headers.nethash = config.network.nethash; + headers.version = "2.0.0"; + headers.port = config.p2pPort; + headers["Content-Type"] = "application/json"; + } + + return { + get: async (endpoint, isP2P = false) => { + const baseUrl = `${config.baseUrl}:${isP2P ? config.p2pPort : config.apiPort}`; + + return (await axios.get(baseUrl + endpoint, { headers })).data; + }, + post: async (endpoint, data, isP2P = false) => { + const baseUrl = `${config.baseUrl}:${isP2P ? config.p2pPort : config.apiPort}`; + + return (await axios.post(baseUrl + endpoint, data, { headers })).data; + }, + }; +} + +export async function paginate(config, endpoint) { + const data = []; + let page = 1; + let maxPages = null; + while (maxPages === null || page <= maxPages) { + const response = await request(config).get(`${endpoint}?page=${page}`); + if (response) { + page++; + maxPages = response.meta.pageCount; + data.push(...response.data); + } else { + break; + } + } + + return data; +} + +/** + * Generate batch of transactions based on wallets. + */ +export function generateTransactions( + amountPerTransaction: any, + wallets: any[], + approvalWallets: any[], + options: { + config: any; + overridePassphrase?: false; + vendorField?: string; + log?: boolean; + [key: string]: any; + }, +) { + const transactions = []; + wallets.forEach((wallet, i) => { + const builder = client.getBuilder().transfer(); + // noinspection JSCheckFunctionSignatures + builder + .fee(this.parseFee(options.transferFee)) + .recipientId(options.recipient || wallet.address) + .network(options.config.network.version) + .amount(amountPerTransaction) + .vendorField(options.vendorField === undefined ? `Transaction ${i + 1}` : options.vendorField) + .sign(options.overridePassphrase ? options.config.passphrase : wallet.passphrase); + + if (wallet.secondPassphrase || options.config.secondPassphrase) { + builder.secondSign(wallet.secondPassphrase || options.config.secondPassphrase); + } + + if (approvalWallets) { + for (let j = approvalWallets.length - 1; j >= 0; j--) { + builder.multiSignatureSign(approvalWallets[j].passphrase); + } + } + + const transaction = builder.build(); + transactions.push(transaction); + + if (options.log) { + logger.info( + `${i} ==> ${transaction.id}, ${transaction.recipientId} (fee: ${this.arktoshiToArk(transaction.fee)})`, + ); + } + }); + + return transactions; +} + +/** + * Parse fee based on input. + * @param {(String|Number)} fee + * @return {Bignum} + */ +export function parseFee(fee): Bignum { + if (typeof fee === "string" && fee.indexOf("-") !== -1) { + const feeRange = fee.split("-").map( + f => + +bignumify(f) + .times(1e8) + .toFixed(), + ); + if (feeRange[1] < feeRange[0]) { + return bignumify(feeRange[0]); + } + + return bignumify(Math.floor(Math.random() * (feeRange[1] - feeRange[0] + 1) + feeRange[0])); + } + + return bignumify(fee).times(1e8); +} + +/** + * Convert ARK to Arktoshi. + * @param {Number} ark + * @return {Bignum} + */ +export function arkToArktoshi(ark) { + return bignumify(ark * 1e8); +} + +/** + * Convert Arktoshi to ARK. + * @param {Bignum} arktoshi + * @return {String} + */ +export function arktoshiToArk(arktoshi) { + return formatArktoshi(arktoshi); +} diff --git a/packages/core-tester-cli/tsconfig.json b/packages/core-tester-cli/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-tester-cli/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-transaction-pool-mem/.gitattributes b/packages/core-transaction-pool-mem/.gitattributes deleted file mode 100644 index 60cc52db63..0000000000 --- a/packages/core-transaction-pool-mem/.gitattributes +++ /dev/null @@ -1,11 +0,0 @@ -# Path-based git attributes -# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html - -# Ignore all test and documentation with "export-ignore". -/.editorconfig export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore -/.travis.yml export-ignore -/__tests__ export-ignore -/docs export-ignore -/README.md export-ignore diff --git a/packages/core-transaction-pool-mem/CHANGELOG.md b/packages/core-transaction-pool-mem/CHANGELOG.md deleted file mode 100644 index 365ba5960e..0000000000 --- a/packages/core-transaction-pool-mem/CHANGELOG.md +++ /dev/null @@ -1,33 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Get transactions from mem pool ordered by fee - -### Changed - -- Test flushing wallets -- Reduce nondeterminism in the transactions expiration tests -- Dropped node.js 9 as minimum requirement in favour of node.js 10 -- Lazy sort transactions to improve performance -- Don't always purge expired transactions when checking for existence to improve performance - -### Fixed - -- Ensure the SQL database exists -- Sorting of transactions by fee - -## 0.1.0 - 2018-10-12 - -### Added - -- initial release diff --git a/packages/core-transaction-pool-mem/LICENSE b/packages/core-transaction-pool-mem/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-transaction-pool-mem/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-transaction-pool-mem/README.md b/packages/core-transaction-pool-mem/README.md deleted file mode 100644 index 4b6a9771e7..0000000000 --- a/packages/core-transaction-pool-mem/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Ark Core - Transaction Pool - Memory - -

- -

- -## Documentation - -You can find installation instructions and detailed instructions on how to use this package at the [dedicated documentation site](https://docs.ark.io/guidebook/core/plugins/core-transaction-pool-mem.html). - -## Security - -If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. - -## Credits - -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [Vasil Dimov](https://github.com/vasild) -- [All Contributors](../../../../contributors) - -## License - -[MIT](LICENSE) © [ArkEcosystem](https://ark.io) diff --git a/packages/core-transaction-pool-mem/__tests__/__fixtures__/transactions.js b/packages/core-transaction-pool-mem/__tests__/__fixtures__/transactions.js deleted file mode 100644 index c647d1a3f5..0000000000 --- a/packages/core-transaction-pool-mem/__tests__/__fixtures__/transactions.js +++ /dev/null @@ -1,198 +0,0 @@ -const ark = require('@arkecosystem/crypto') - -const { slots } = ark -const { Transaction } = ark.models - -exports.dummy1 = new Transaction({ - version: 1, - network: 23, - type: 0, - timestamp: 35672738, - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - fee: 10000000, - vendorFieldHex: '5449443a2030', - amount: 200000000, - expiration: 0, - recipientId: 'AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5', - signature: - '304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619', - vendorField: 'TID: 0', - id: 'a5e9e6039675563959a783fa672c0ffe65369168a1ecffa3c89bf82961d8dbad', -}) - -exports.dummy2 = new Transaction({ - version: 1, - network: 30, - type: 0, - timestamp: 35632190, - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - fee: 10000000, - amount: 10000000, - expiration: 0, - recipientId: 'DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8', - signature: - '3045022100ead721ae139c0a18a7be2077453337f8305e02a474a3e4e35eb22bcf59ce474c02207ea591ac68b5cfee068ac605efb000c7e1e7479abc7f6ee7ece21f3a5c629800', - id: 'e665f6634fdbbbc562f79b92c8f0acd621081680c247cb4a6fc987bf456ea554', -}) - -exports.dummy3 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'ANqvJEMZcmUpcKBC8xiP1TntVkJeuZ3Lw3', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 0', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '304402203f4d2b11b6f05538b16e2ab314c3c158885d8ceb95f3c0237d00fb350ea1b8e7022052eb7a2cd35c0d91ac14a8cba32b14a744ef26fc7d4c63b66d55f3ade0d6c305', - id: 'b163572af7598e35b4ea51e92cd1b59c8d653a50fc21358a7690777cc793cc50', -}) - -exports.dummy4 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'AJ5eV59hu4xrbRCpoP3of7fEYWUteSVa8k', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 1', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '30450221008e04e622578bb6ac55097c9af3b7ffb553b659900f58056dae6ff2d57b0630000220071f416401431ba375f3f1a345b5f98deddd2198f072af4746a78417f8ece47d', - id: '03ebe9fd182e2ac19244a80717428b5ded0c2e7692f7f503f1acea0ea285ded9', -}) - -exports.dummy5 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'ASvC1E9hMLfANTi63S2gUMvr7rVZYJBj3u', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 2', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '304502210095e699ae51090076180ead5623059ad0e607f08cf2a56b6a214817ec08610fd6022041ab05fe8acffdf0e4ed265d062411b2d3e47cf0f76b22793aee6ba12b17042c', - id: 'b1b89654cabf06fd2db8aa0b3659efcbf7430d1223bae0d8a23f6fad0983b032', -}) - -exports.dummy6 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'Ac8utEr7XRebWRvArSBnbVoxbq6bXftAmL', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 3', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '304402203388ae5ba8f6248545593e7b4401900ca47dc5d694f5c36c8e1dafa67f1e214a02204a5e0cb620f0229cd0059675c8e2e3d835621eb682dc77f993acf5345a2f2bc7', - id: '937cb5431352100d60b5a6e9d5bb487c1276c1dee7ab75a238ca98daca35d236', -}) - -exports.dummy7 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'ANWEaVfvAh3VTyZNYcuFESUum1XBmAvAdj', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 4', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '304502210093b9cf39802eff75d1f16c5f1de5a4326c77c73153e9cb87cfeb81f00b59a06402200b5375046043f0839bcdc2c3f972728241fb04fdacf3a669b12f2ec47c962d23', - id: 'd14ebba264bc6056acc5593c5c6d5566ae7bbd688556386e9e70ab33eb6e3e9c', -}) - -exports.dummy8 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'ALsZS24Dn4HYXwed5kAC5fKyB9BFzdmcSx', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 5', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '30450221008425a7283e921d956a86db10bb34666deea9c13fa204420c4a85e2482399cce50220476bfdddc0743a0e05730e1b056a5a1d1030a963241ceced24da41ade6e6d2c9', - id: '7cf2325af89cdd7ac0b75e45a98ef1a30e8ee83842afeec27f22e695bf01f0ce', -}) - -exports.dummy9 = new Transaction({ - version: 1, - type: 0, - amount: 200000000, - fee: 10000000, - recipientId: 'ANuaLhRuBJhTcHao7kTfDcfsewLQGr7x5G', - timestamp: 37346710, - asset: {}, - vendorField: 'TID: 6', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '3045022100f6571a7da13e81053e3cf39262b0dba7c476e589ae0c30ea7fb46bdff22dbd05022015c528cf9e8aacd986bb20b81420bf8eb7fd235a51f37193a8488f060a884267', - id: '6cc8e7d4ea99198dee4bed393e77828da8302619b27064933c0487c9dbb48e78', -}) - -exports.dummy10 = new Transaction({ - version: 1, - network: 30, - type: 0, - timestamp: slots.getTime(), - senderPublicKey: - '0310c283aac7b35b4ae6fab201d36e8322c3408331149982e16013a5bcb917081c', - fee: 10000000, - amount: 20000000, - recipientId: 'DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8', - signature: - '3045022100ead721ae139c0a18a7be2077453337f8305e02a474a3e4e35eb22bcf59ce474c02207ea591ac68b5cfee068ac605efb000c7e1e7479abc7f6ee7ece21f3a5c629800', - vendorField: 'Expiring transaction 2', -}) - -exports.dummyExp1 = new Transaction({ - version: 1, - network: 23, - type: 0, - timestamp: slots.getTime(), - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - fee: 20000000, - vendorFieldHex: '5449443a2030', - amount: 200000000, - expiration: slots.getTime() + 5, - recipientId: 'AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5', - signature: - '304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619', - vendorField: 'Expiring transaction 1', -}) - -exports.dummyExp2 = new Transaction({ - version: 1, - network: 30, - type: 0, - timestamp: slots.getTime(), - senderPublicKey: - '0310c283aac7b35b4ae6fab201d36e8322c3408331149982e16013a5bcb917081c', - fee: 10000000, - amount: 20000000, - expiration: slots.getTime() + 5, - recipientId: 'DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8', - signature: - '3045022100ead721ae139c0a18a7be2077453337f8305e02a474a3e4e35eb22bcf59ce474c02207ea591ac68b5cfee068ac605efb000c7e1e7479abc7f6ee7ece21f3a5c629800', - vendorField: 'Expiring transaction 2', -}) diff --git a/packages/core-transaction-pool-mem/__tests__/__support__/setup.js b/packages/core-transaction-pool-mem/__tests__/__support__/setup.js deleted file mode 100644 index 3d93a41015..0000000000 --- a/packages/core-transaction-pool-mem/__tests__/__support__/setup.js +++ /dev/null @@ -1,15 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(30000) - -exports.setUp = async () => { - await appHelper.setUp({ - exit: '@arkecosystem/core-blockchain', - exclude: ['@arkecosystem/core-transaction-pool-mem'], - }) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-transaction-pool-mem/__tests__/connection.test.js b/packages/core-transaction-pool-mem/__tests__/connection.test.js deleted file mode 100644 index c5149e72fe..0000000000 --- a/packages/core-transaction-pool-mem/__tests__/connection.test.js +++ /dev/null @@ -1,722 +0,0 @@ -/* eslint max-len: "off" */ - -const { bignumify } = require('@arkecosystem/core-utils') -const container = require('@arkecosystem/core-container') -const crypto = require('@arkecosystem/crypto') -const delay = require('delay') -const delegatesSecrets = require('@arkecosystem/core-test-utils/fixtures/testnet/passphrases') -const generateTransfer = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const randomSeed = require('random-seed') -const mockData = require('./__fixtures__/transactions') -const app = require('./__support__/setup') - -const ARKTOSHI = crypto.constants.ARKTOSHI -const TRANSACTION_TYPES = crypto.constants.TRANSACTION_TYPES -const Transaction = crypto.models.Transaction -const slots = crypto.slots - -let config -let defaultConfig -let database -let connection - -beforeAll(async () => { - await app.setUp() - - config = container.resolvePlugin('config') - defaultConfig = require('../lib/defaults') - database = container.resolvePlugin('database') - - const Connection = require('../lib/connection.js') - connection = new Connection(defaultConfig) - await connection.make() - // 100+ years in the future to avoid our hardcoded transactions used in these - // tests to expire - connection.options.maxTransactionAge = 4036608000 -}) - -afterAll(async () => { - connection.disconnect() - await app.tearDown() -}) - -afterEach(() => { - connection.flush() -}) - -describe('Connection', () => { - it('should be an object', () => { - expect(connection).toBeObject() - }) - - describe('getPoolSize', () => { - it('should be a function', () => { - expect(connection.getPoolSize).toBeFunction() - }) - - it('should return 0 if no transactions were added', () => { - expect(connection.getPoolSize()).toBe(0) - }) - - it('should return 2 if transactions were added', () => { - expect(connection.getPoolSize()).toBe(0) - - connection.addTransaction(mockData.dummy1) - - expect(connection.getPoolSize()).toBe(1) - - connection.addTransaction(mockData.dummy2) - - expect(connection.getPoolSize()).toBe(2) - }) - }) - - describe('getSenderSize', () => { - it('should be a function', () => { - expect(connection.getSenderSize).toBeFunction() - }) - - it('should return 0 if no transactions were added', () => { - expect(connection.getSenderSize('undefined')).toBe(0) - }) - - it('should return 2 if transactions were added', () => { - const senderPublicKey = mockData.dummy1.senderPublicKey - - expect(connection.getSenderSize(senderPublicKey)).toBe(0) - - connection.addTransaction(mockData.dummy1) - - expect(connection.getSenderSize(senderPublicKey)).toBe(1) - - connection.addTransaction(mockData.dummy2) - - expect(connection.getSenderSize(senderPublicKey)).toBe(2) - }) - }) - - describe('addTransaction', () => { - it('should be a function', () => { - expect(connection.addTransaction).toBeFunction() - }) - - it('should add the transaction to the pool', () => { - expect(connection.getPoolSize()).toBe(0) - - connection.addTransaction(mockData.dummy1) - - // Test adding already existent transaction - connection.addTransaction(mockData.dummy1) - - expect(connection.getPoolSize()).toBe(1) - }) - }) - - describe('addTransactions', () => { - it('should be a function', () => { - expect(connection.addTransactions).toBeFunction() - }) - - it('should add the transactions to the pool', () => { - expect(connection.getPoolSize()).toBe(0) - - connection.addTransactions([mockData.dummy1, mockData.dummy2]) - - expect(connection.getPoolSize()).toBe(2) - }) - - it('should not add not-appliable transactions', () => { - // This should be skipped due to insufficient funds - const highFeeTransaction = new Transaction(mockData.dummy3) - highFeeTransaction.fee = bignumify(1e9 * ARKTOSHI) - // changing public key as fixture transactions have the same one - highFeeTransaction.senderPublicKey = - '000000000000000000000000000000000000000420000000000000000000000000' - - const transactions = [ - mockData.dummy1, - mockData.dummy2, - highFeeTransaction, - mockData.dummy4, - mockData.dummy5, - mockData.dummy6, - ] - - // Ensure no cold wallet - database.walletManager.findByPublicKey( - '000000000000000000000000000000000000000420000000000000000000000000', - ) - - const { added, notAdded } = connection.addTransactions(transactions) - expect(notAdded[0].message).toEqual( - `["[PoolWalletManager] Can't apply transaction id:b163572af7598e35b4ea51e92cd1b59c8d653a50fc21358a7690777cc793cc50 from sender:AHkZLLjUdjjjJzNe1zCXqHh27bUhzg8GZw","Insufficient balance in the wallet"]`, - ) - expect(connection.getPoolSize()).toBe(5) - }) - }) - - describe('addTransactions with expiration', () => { - it('should add the transactions to the pool and they should expire', async () => { - expect(connection.getPoolSize()).toBe(0) - - const expireAfterSeconds = 3 - const expiration = slots.getTime() + expireAfterSeconds - - const transactions = [] - - transactions.push(new Transaction(mockData.dummyExp1)) - transactions[transactions.length - 1].expiration = expiration - - transactions.push(new Transaction(mockData.dummy1)) - // transactions[transactions.length - 1].type = - // TRANSACTION_TYPES.TIMELOCK_TRANSFER - - // Workaround: Increase balance of sender wallet to succeed - const insufficientBalanceTx = new Transaction(mockData.dummyExp2) - transactions.push(insufficientBalanceTx) - insufficientBalanceTx.expiration = expiration - - const wallet = connection.walletManager.findByPublicKey( - insufficientBalanceTx.senderPublicKey, - ) - - wallet.balance = wallet.balance.plus(insufficientBalanceTx.amount * 2) - - transactions.push(mockData.dummy2) - - // Ensure no cold wallets - transactions.forEach(tx => - database.walletManager.findByPublicKey(tx.senderPublicKey), - ) - - const { added, notAdded } = connection.addTransactions(transactions) - expect(added).toHaveLength(4) - expect(notAdded).toBeEmpty() - - expect(connection.getPoolSize()).toBe(4) - await delay((expireAfterSeconds + 1) * 1000) - expect(connection.getPoolSize()).toBe(2) - - transactions.forEach(t => connection.removeTransactionById(t.id)) - }) - }) - - describe('removeTransaction', () => { - it('should be a function', () => { - expect(connection.removeTransaction).toBeFunction() - }) - - it('should remove the specified transaction from the pool', () => { - connection.addTransaction(mockData.dummy1) - - expect(connection.getPoolSize()).toBe(1) - - connection.removeTransaction(mockData.dummy1) - - expect(connection.getPoolSize()).toBe(0) - }) - }) - - describe('removeTransactionById', () => { - it('should be a function', () => { - expect(connection.removeTransactionById).toBeFunction() - }) - - it('should remove the specified transaction from the pool (by id)', () => { - connection.addTransaction(mockData.dummy1) - - expect(connection.getPoolSize()).toBe(1) - - connection.removeTransactionById(mockData.dummy1.id) - - expect(connection.getPoolSize()).toBe(0) - }) - - it('should do nothing when asked to delete a non-existent transaction', () => { - connection.addTransaction(mockData.dummy1) - - connection.removeTransactionById('nonexistenttransactionid') - - expect(connection.getPoolSize()).toBe(1) - }) - }) - - describe('removeTransactionsForSender', () => { - it('should be a function', () => { - expect(connection.removeTransactionsForSender).toBeFunction() - }) - - it('should remove the senders transactions from the pool', () => { - connection.addTransaction(mockData.dummy1) - connection.addTransaction(mockData.dummy2) - connection.addTransaction(mockData.dummy3) - connection.addTransaction(mockData.dummy4) - connection.addTransaction(mockData.dummy5) - connection.addTransaction(mockData.dummy6) - - // dummy10 is the only cold wallet - database.walletManager.findByPublicKey( - mockData.dummy10.data.senderPublicKey, - ) - - connection.addTransaction(mockData.dummy10) - - expect(connection.getPoolSize()).toBe(7) - - connection.removeTransactionsForSender(mockData.dummy1.senderPublicKey) - - expect(connection.getPoolSize()).toBe(1) - }) - }) - - describe('transactionExists', () => { - it('should be a function', () => { - expect(connection.transactionExists).toBeFunction() - }) - - it('should return true if transaction is IN pool', () => { - connection.addTransactions([mockData.dummy1, mockData.dummy2]) - - expect(connection.transactionExists(mockData.dummy1.id)).toBeTrue() - expect(connection.transactionExists(mockData.dummy2.id)).toBeTrue() - }) - - it('should return false if transaction is NOT pool', () => { - expect(connection.transactionExists(mockData.dummy1.id)).toBeFalse() - expect(connection.transactionExists(mockData.dummy2.id)).toBeFalse() - }) - }) - - describe('hasExceededMaxTransactions', () => { - it('should be a function', () => { - expect(connection.hasExceededMaxTransactions).toBeFunction() - }) - - it('should be true if exceeded', () => { - connection.options.maxTransactionsPerSender = 5 - connection.options.allowedSenders = [] - connection.addTransaction(mockData.dummy3) - connection.addTransaction(mockData.dummy4) - connection.addTransaction(mockData.dummy5) - connection.addTransaction(mockData.dummy6) - connection.addTransaction(mockData.dummy7) - connection.addTransaction(mockData.dummy8) - connection.addTransaction(mockData.dummy9) - - expect(connection.getPoolSize()).toBe(7) - const exceeded = connection.hasExceededMaxTransactions(mockData.dummy3) - expect(exceeded).toBeTrue() - }) - - it('should be falsy if not exceeded', () => { - connection.options.maxTransactionsPerSender = 7 - connection.options.allowedSenders = [] - - connection.addTransaction(mockData.dummy4) - connection.addTransaction(mockData.dummy5) - connection.addTransaction(mockData.dummy6) - - expect(connection.getPoolSize()).toBe(3) - const exceeded = connection.hasExceededMaxTransactions(mockData.dummy3) - expect(exceeded).toBeFalse() - }) - - it('should be allowed to exceed if whitelisted', () => { - connection.flush() - connection.options.maxTransactionsPerSender = 5 - connection.options.allowedSenders = [ - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - 'ghjk', - ] - connection.addTransaction(mockData.dummy3) - connection.addTransaction(mockData.dummy4) - connection.addTransaction(mockData.dummy5) - connection.addTransaction(mockData.dummy6) - connection.addTransaction(mockData.dummy7) - connection.addTransaction(mockData.dummy8) - connection.addTransaction(mockData.dummy9) - - expect(connection.getPoolSize()).toBe(7) - const exceeded = connection.hasExceededMaxTransactions(mockData.dummy3) - expect(exceeded).toBeFalse() - }) - }) - - describe('getTransaction', () => { - it('should be a function', () => { - expect(connection.getTransaction).toBeFunction() - }) - - it('should return the specified transaction', () => { - connection.addTransaction(mockData.dummy1) - - const poolTransaction = connection.getTransaction(mockData.dummy1.id) - expect(poolTransaction).toBeObject() - expect(poolTransaction.id).toBe(mockData.dummy1.id) - }) - - it('should return undefined for nonexisting transaction', () => { - const poolTransaction = connection.getTransaction('non existing id') - expect(poolTransaction).toBeFalsy() - }) - }) - - describe('getTransactions', () => { - it('should be a function', () => { - expect(connection.getTransactions).toBeFunction() - }) - - it('should return transactions within the specified range', () => { - const transactions = [mockData.dummy1, mockData.dummy2] - - connection.addTransactions(transactions) - - if (transactions[1].fee > transactions[0].fee) { - transactions.reverse() - } - - for (const i of [0, 1]) { - const retrieved = connection - .getTransactions(i, 1) - .map(serializedTx => Transaction.fromBytes(serializedTx)) - - expect(retrieved.length).toBe(1) - expect(retrieved[0]).toBeObject() - expect(retrieved[0].id).toBe(transactions[i].id) - } - }) - }) - - describe('getTransactionIdsForForging', () => { - it('should be a function', () => { - expect(connection.getTransactionIdsForForging).toBeFunction() - }) - - it('should return an array of transactions ids', () => { - connection.addTransaction(mockData.dummy1) - connection.addTransaction(mockData.dummy2) - connection.addTransaction(mockData.dummy3) - connection.addTransaction(mockData.dummy4) - connection.addTransaction(mockData.dummy5) - connection.addTransaction(mockData.dummy6) - - const transactionIds = connection.getTransactionIdsForForging(0, 6) - - expect(transactionIds).toBeArray() - expect(transactionIds[0]).toBe(mockData.dummy1.id) - expect(transactionIds[1]).toBe(mockData.dummy2.id) - expect(transactionIds[2]).toBe(mockData.dummy3.id) - expect(transactionIds[3]).toBe(mockData.dummy4.id) - expect(transactionIds[4]).toBe(mockData.dummy5.id) - expect(transactionIds[5]).toBe(mockData.dummy6.id) - }) - }) - - describe('getTransactionsForForging', () => { - it('should be a function', () => { - expect(connection.getTransactionsForForging).toBeFunction() - }) - }) - - describe('flush', () => { - it('should be a function', () => { - expect(connection.flush).toBeFunction() - }) - - it('should flush the pool', () => { - connection.addTransaction(mockData.dummy1) - - expect(connection.getPoolSize()).toBe(1) - - connection.flush() - - expect(connection.getPoolSize()).toBe(0) - }) - }) - - describe('senderHasTransactionsOfType', () => { - it('should be a function', () => { - expect(connection.senderHasTransactionsOfType).toBeFunction() - }) - - it('should be false for non-existent sender', () => { - connection.addTransaction(mockData.dummy1) - - expect( - connection.senderHasTransactionsOfType( - 'nonexistent', - TRANSACTION_TYPES.VOTE, - ), - ).toBeFalse() - }) - - it('should be false for existent sender with no votes', () => { - const tx = mockData.dummy1 - - connection.addTransaction(tx) - - expect( - connection.senderHasTransactionsOfType( - tx.senderPublicKey, - TRANSACTION_TYPES.VOTE, - ), - ).toBeFalse() - }) - - it('should be true for existent sender with votes', () => { - const tx = mockData.dummy1 - - // Prevent 'wallet has already voted' error - connection.walletManager.findByPublicKey(tx.senderPublicKey).vote = '' - - const voteTx = new Transaction(tx) - voteTx.id = - '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef' - voteTx.type = TRANSACTION_TYPES.VOTE - voteTx.amount = 0 - voteTx.asset = { votes: [`+${tx.senderPublicKey}`] } - - const transactions = [tx, voteTx, mockData.dummy2] - - connection.addTransactions(transactions) - - expect( - connection.senderHasTransactionsOfType( - tx.senderPublicKey, - TRANSACTION_TYPES.VOTE, - ), - ).toBeTrue() - }) - }) - - describe('shutdown and start', () => { - it('save and restore transactions', () => { - expect(connection.getPoolSize()).toBe(0) - - const transactions = [mockData.dummy1, mockData.dummy4] - - connection.addTransactions(transactions) - - expect(connection.getPoolSize()).toBe(2) - - connection.disconnect() - - connection.make() - - expect(connection.getPoolSize()).toBe(2) - - transactions.forEach(t => - expect(connection.getTransaction(t.id).serialized.toLowerCase()).toBe( - t.serialized.toLowerCase(), - ), - ) - - connection.flush() - }) - - it('remove forged when starting', async () => { - expect(connection.getPoolSize()).toBe(0) - - const block = await database.getLastBlock() - - // XXX This accesses directly block.transactions which is not even - // documented in packages/crypto/lib/models/block.js - const forgedTransaction = block.transactions[0] - - // Workaround: Add tx to exceptions so it gets applied, because the fee is 0. - config.network.exceptions.transactions = [forgedTransaction.id] - - // For some reason all genesis transactions fail signature verification, so - // they are not loaded from the local storage and this fails otherwise. - const original = database.getForgedTransactionsId - database.getForgedTransactionsIds = jest.fn(() => [forgedTransaction.id]) - - expect(forgedTransaction instanceof Transaction).toBeTrue() - - const transactions = [mockData.dummy1, forgedTransaction, mockData.dummy4] - - connection.addTransactions(transactions) - - expect(connection.getPoolSize()).toBe(3) - - connection.disconnect() - - await connection.make() - - expect(connection.getPoolSize()).toBe(2) - - transactions.splice(1, 1) - - transactions.forEach(t => - expect(connection.getTransaction(t.id).serialized.toLowerCase()).toBe( - t.serialized.toLowerCase(), - ), - ) - - connection.flush() - - database.getForgedTransactionsIds = original - }) - }) - - describe('stress', () => { - const fakeTransactionId = i => - `id${String(i)}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa` - - it('multiple additions and retrievals', () => { - // Abstract number which decides how many iterations are run by the test. - // Increase it to run more iterations. - const testSize = connection.options.syncInterval * 2 - - for (let i = 0; i < testSize; i++) { - const transaction = new Transaction(mockData.dummy1) - transaction.id = fakeTransactionId(i) - connection.addTransaction(transaction) - - if (i % 27 === 0) { - connection.removeTransaction(transaction) - } - } - - for (let i = 0; i < testSize * 2; i++) { - connection.getPoolSize() - for (const sender of ['nonexistent', mockData.dummy1.senderPublicKey]) { - connection.getSenderSize(sender) - connection.hasExceededMaxTransactions(sender) - } - connection.getTransaction(fakeTransactionId(i)) - connection.getTransactions(0, i) - } - - for (let i = 0; i < testSize; i++) { - const transaction = new Transaction(mockData.dummy1) - transaction.id = fakeTransactionId(i) - connection.removeTransaction(transaction) - } - }) - - it('delete + add after sync', () => { - for (let i = 0; i < connection.options.syncInterval; i++) { - const transaction = new Transaction(mockData.dummy1) - transaction.id = fakeTransactionId(i) - connection.addTransaction(transaction) - } - - const transaction = new Transaction(mockData.dummy1) - transaction.id = fakeTransactionId(0) - connection.removeTransaction(transaction) - connection.addTransaction(transaction) - }) - - it('add many then get first few', () => { - const nAdd = 2000 - - // We use a predictable random number calculator in order to get - // a deterministic test. - const rand = randomSeed.create(0) - - const allTransactions = [] - for (let i = 0; i < nAdd; i++) { - const transaction = new Transaction(mockData.dummy1) - transaction.id = fakeTransactionId(i) - transaction.fee = bignumify( - rand.intBetween(0.002 * ARKTOSHI, 2 * ARKTOSHI), - ) - transaction.serialized = Transaction.serialize(transaction).toString( - 'hex', - ) - allTransactions.push(transaction) - } - - // console.time(`time to add ${nAdd}`) - connection.addTransactions(allTransactions) - // console.timeEnd(`time to add ${nAdd}`) - - const nGet = 150 - - const topFeesExpected = allTransactions - .map(t => t.fee) - .sort((a, b) => b - a) - .slice(0, nGet) - .map(f => f.toString()) - - // console.time(`time to get first ${nGet}`) - const topTransactionsSerialized = connection.getTransactions(0, nGet) - // console.timeEnd(`time to get first ${nGet}`) - - const topFeesReceived = topTransactionsSerialized.map(e => - new Transaction(e).fee.toString(), - ) - - expect(topFeesReceived).toEqual(topFeesExpected) - }) - }) - - describe('purgeSendersWithInvalidTransactions', () => { - it('should be a function', () => { - expect(connection.purgeSendersWithInvalidTransactions).toBeFunction() - }) - - it('should purge transactions from sender when invalid', async () => { - const transfersA = generateTransfer( - 'testnet', - delegatesSecrets[0], - mockData.dummy1.recipientId, - 1, - 5, - ) - - const transfersB = generateTransfer( - 'testnet', - delegatesSecrets[1], - mockData.dummy1.recipientId, - 1, - 1, - ) - - const block = { - transactions: [...transfersA, ...transfersB], - } - - block.transactions.forEach(tx => connection.addTransaction(tx)) - - expect(connection.getPoolSize()).toBe(6) - - // Last tx has a unique sender - block.transactions[5].verified = false - - connection.purgeSendersWithInvalidTransactions(block) - expect(connection.getPoolSize()).toBe(5) - - // The remaining tx all have the same sender - block.transactions[0].verified = false - - connection.purgeSendersWithInvalidTransactions(block) - expect(connection.getPoolSize()).toBe(0) - }) - }) - - describe('purgeBlock', () => { - it('should be a function', () => { - expect(connection.purgeBlock).toBeFunction() - }) - - it('should purge transactions from block', async () => { - const transactions = generateTransfer( - 'testnet', - delegatesSecrets[0], - mockData.dummy1.recipientId, - 1, - 5, - ) - const block = { transactions } - - block.transactions.forEach(tx => connection.addTransaction(tx)) - - expect(connection.getPoolSize()).toBe(5) - - connection.purgeBlock(block) - expect(connection.getPoolSize()).toBe(0) - }) - }) -}) diff --git a/packages/core-transaction-pool-mem/jest.config.js b/packages/core-transaction-pool-mem/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-transaction-pool-mem/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-transaction-pool-mem/lib/connection.js b/packages/core-transaction-pool-mem/lib/connection.js deleted file mode 100644 index 26025a7b97..0000000000 --- a/packages/core-transaction-pool-mem/lib/connection.js +++ /dev/null @@ -1,417 +0,0 @@ -/* eslint no-constant-condition: "off" */ -/* eslint no-await-in-loop: "off" */ - -const { - TransactionPoolInterface, -} = require('@arkecosystem/core-transaction-pool') - -const assert = require('assert') -const app = require('@arkecosystem/core-container') -const Mem = require('./mem') -const MemPoolTransaction = require('./mem-pool-transaction') -const Storage = require('./storage') - -const database = app.resolvePlugin('database') -const emitter = app.resolvePlugin('event-emitter') -const logger = app.resolvePlugin('logger') - -/** - * Transaction pool. It uses a hybrid storage - caching the data - * in memory and occasionally saving it to a persistent, on-disk storage (SQLite), - * every N modifications, and also during shutdown. The operations that only read - * data (everything other than add or remove transaction) are served from the - * in-memory storage. - */ -class TransactionPool extends TransactionPoolInterface { - /** - * Make the transaction pool instance. Load all transactions in the pool from - * the on-disk database, saved there from a previous run. - * @return {TransactionPool} - */ - async make() { - this.mem = new Mem() - this.storage = new Storage(this.options.storage) - this.loggedAllowedSenders = [] - - const all = this.storage.loadAll() - all.forEach(t => this.mem.add(t, this.options.maxTransactionAge, true)) - - this.__purgeExpired() - - // Remove transactions that were forged while we were offline. - const allIds = all.map( - memPoolTransaction => memPoolTransaction.transaction.id, - ) - - const forgedIds = await database.getForgedTransactionsIds(allIds) - - forgedIds.forEach(id => this.removeTransactionById(id)) - - return this - } - - /** - * Disconnect from transaction pool. - * @return {void} - */ - disconnect() { - this.__syncToPersistentStorage() - this.storage.close() - } - - /** - * Get the number of transactions in the pool. - * @return {Number} - */ - getPoolSize() { - this.__purgeExpired() - - return this.mem.getSize() - } - - /** - * Get the number of transactions in the pool from a specific sender - * @param {String} senderPublicKey - * @returns {Number} - */ - getSenderSize(senderPublicKey) { - this.__purgeExpired() - - return this.mem.getBySender(senderPublicKey).size - } - - /** - * Add a transaction to the pool. - * @param {Transaction} transaction - * @return {Object} The success property indicates wether the transaction was successfully added - * and applied to the pool or not. In case it was not successful, the type and message - * property yield information about the error. - */ - addTransaction(transaction) { - if (this.transactionExists(transaction.id)) { - logger.debug( - 'Transaction pool: ignoring attempt to add a transaction that is already ' + - `in the pool, id: ${transaction.id}`, - ) - - return this.__createError( - transaction, - 'ERR_ALREADY_IN_POOL', - 'Already in pool', - ) - } - - const poolSize = this.mem.getSize() - - if (this.options.maxTransactionsInPool <= poolSize) { - // The pool can't accommodate more transactions. Either decline the newcomer or remove - // an existing transaction from the pool in order to free up space. - const all = this.mem.getTransactionsOrderedByFee() - const lowest = all[all.length - 1].transaction - - if (lowest.fee.isLessThan(transaction.fee)) { - this.walletManager.revertTransactionForSender(lowest) - this.mem.remove(lowest.id, lowest.senderPublicKey) - } else { - return this.__createError( - transaction, - 'ERR_POOL_FULL', - `Pool is full (has ${poolSize} transactions) and this transaction's fee ` + - `${transaction.fee.toFixed()} is not higher than the lowest fee already in pool ` + - `${lowest.fee.toFixed()}`, - ) - } - } - - this.mem.add( - new MemPoolTransaction(transaction), - this.options.maxTransactionAge, - ) - - // Apply transaction to pool wallet manager. - const senderWallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - - const errors = [] - if (this.walletManager.canApply(transaction, errors)) { - senderWallet.applyTransactionToSender(transaction) - } else { - // Remove tx again from the pool - this.mem.remove(transaction.id) - - return this.__createError( - transaction, - 'ERR_APPLY', - JSON.stringify(errors), - ) - } - - this.__syncToPersistentStorageIfNecessary() - return { success: true } - } - - /** - * Add many transactions to the pool. - * @param {Array} transactions, already transformed and verified - * by transaction guard - must have serialized field - * @return {Object} like - * { - * added: [ ... successfully added transactions ... ], - * notAdded: [ { transaction: Transaction, type: String, message: String }, ... ] - * } - */ - addTransactions(transactions) { - const added = [] - const notAdded = [] - - for (const t of transactions) { - const result = this.addTransaction(t) - - if (result.success) { - added.push(t) - } else { - notAdded.push(result) - } - } - - return { added, notAdded } - } - - /** - * Remove a transaction from the pool by transaction object. - * @param {Transaction} transaction - * @return {void} - */ - removeTransaction(transaction) { - this.removeTransactionById(transaction.id, transaction.senderPublicKey) - } - - /** - * Remove a transaction from the pool by id. - * @param {String} id - * @param {String} senderPublicKey - * @return {void} - */ - removeTransactionById(id, senderPublicKey = undefined) { - this.mem.remove(id, senderPublicKey) - - this.__syncToPersistentStorageIfNecessary() - } - - /** - * Check whether sender of transaction has exceeded max transactions in queue. - * @param {Transaction} transaction - * @return {Boolean} true if exceeded - */ - hasExceededMaxTransactions(transaction) { - this.__purgeExpired() - - if (this.options.allowedSenders.includes(transaction.senderPublicKey)) { - if (!this.loggedAllowedSenders.includes(transaction.senderPublicKey)) { - logger.debug( - `Transaction pool: allowing sender public key: ${ - transaction.senderPublicKey - } (listed in options.allowedSenders), thus skipping throttling.`, - ) - this.loggedAllowedSenders.push(transaction.senderPublicKey) - } - - return false - } - - const count = this.mem.getBySender(transaction.senderPublicKey).size - - return !(count <= this.options.maxTransactionsPerSender) - } - - /** - * Get a transaction by transaction id. - * @param {String} id - * @return {(Transaction|undefined)} - */ - getTransaction(id) { - this.__purgeExpired() - - return this.mem.getTransactionById(id) - } - - /** - * Get all transactions that are ready to be forged. - * @param {Number} blockSize - * @return {(Array|void)} - */ - getTransactionsForForging(blockSize) { - return this.getTransactions(0, blockSize) - } - - /** - * Get all transactions within the specified range [start, start + size), ordered by fee. - * @param {Number} start - * @param {Number} size - * @return {(Array|void)} array of serialized transaction hex strings - */ - getTransactions(start, size) { - return this.getTransactionsData(start, size, 'serialized') - } - - /** - * Get all transactions within the specified range [start, start + size). - * @param {Number} start - * @param {Number} size - * @return {Array} array of transactions IDs in the specified range - */ - getTransactionIdsForForging(start, size) { - return this.getTransactionsData(start, size, 'id') - } - - /** - * Get data from all transactions within the specified range [start, start + size). - * Transactions are ordered by fee (highest fee first) or by - * insertion time, if fees equal (earliest transaction first). - * @param {Number} start - * @param {Number} size - * @param {String} property - * @return {Array} array of transaction[property] - */ - getTransactionsData(start, size, property) { - this.__purgeExpired() - - const data = [] - - let i = 0 - for (const memPoolTransaction of this.mem.getTransactionsOrderedByFee()) { - if (i >= start + size) { - break - } - - if (i >= start) { - assert.notStrictEqual( - memPoolTransaction.transaction[property], - undefined, - ) - data.push(memPoolTransaction.transaction[property]) - } - - i++ - } - - return data - } - - /** - * Flush the pool (delete all transactions from it). - * @return {void} - */ - flush() { - this.mem.flush() - - this.storage.deleteAll() - } - - /** - * Remove all transactions from the transaction pool belonging to specific sender. - * @param {String} senderPublicKey - * @return {void} - */ - removeTransactionsForSender(senderPublicKey) { - this.mem - .getBySender(senderPublicKey) - .forEach(e => this.removeTransactionById(e.transaction.id)) - } - - /** - * Checks if a transaction exists in the pool. - * @param {String} transactionId - * @return {Boolean} - */ - transactionExists(transactionId) { - if (!this.mem.transactionExists(transactionId)) { - // If it does not exist then no need to purge expired transactions because - // we know it will not exist after purge too. - return false - } - - this.__purgeExpired() - - return this.mem.transactionExists(transactionId) - } - - /** - * Check whether a given sender has any transactions of the specified type - * in the pool. - * @param {String} senderPublicKey public key of the sender - * @param {Number} transactionType transaction type, must be one of - * TRANSACTION_TYPES.* and is compared against transaction.type. - * @return {Boolean} true if exist - */ - senderHasTransactionsOfType(senderPublicKey, transactionType) { - this.__purgeExpired() - - for (const memPoolTransaction of this.mem.getBySender(senderPublicKey)) { - if (memPoolTransaction.transaction.type === transactionType) { - return true - } - } - - return false - } - - /** - * Remove all transactions from the pool that have expired. - * @return {void} - */ - __purgeExpired() { - for (const transaction of this.mem.getExpired( - this.options.maxTransactionAge, - )) { - emitter.emit('transaction.expired', transaction.data) - - this.walletManager.revertTransactionForSender(transaction) - - this.mem.remove(transaction.id, transaction.senderPublicKey) - - this.__syncToPersistentStorageIfNecessary() - } - } - - /** - * Sync the in-memory storage to the persistent (on-disk) storage if too - * many changes have been accumulated in-memory. - * @return {void} - */ - __syncToPersistentStorageIfNecessary() { - if (this.options.syncInterval <= this.mem.getNumberOfDirty()) { - this.__syncToPersistentStorage() - } - } - - /** - * Sync the in-memory storage to the persistent (on-disk) storage. - */ - __syncToPersistentStorage() { - const added = this.mem.getDirtyAddedAndForget() - this.storage.bulkAdd(added) - - const removed = this.mem.getDirtyRemovedAndForget() - this.storage.bulkRemoveById(removed) - } - - /** - * Create an error object which the TransactionGuard understands. - * @param {Transaction} transaction - * @param {String} type - * @param {String} message - * @return {Object} - */ - __createError(transaction, type, message) { - return { - transaction, - type, - message, - success: false, - } - } -} - -module.exports = TransactionPool diff --git a/packages/core-transaction-pool-mem/lib/defaults.js b/packages/core-transaction-pool-mem/lib/defaults.js deleted file mode 100644 index 15aa270672..0000000000 --- a/packages/core-transaction-pool-mem/lib/defaults.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - enabled: !process.env.ARK_TRANSACTION_POOL_DISABLED, - syncInterval: 512, - storage: `${process.env.ARK_PATH_DATA}/database/transaction-pool-${ - process.env.ARK_NETWORK_NAME - }.sqlite`, - // When the pool contains that many transactions, then a new transaction is - // only accepted if its fee is higher than the transaction with the lowest - // fee in the pool. In this case the transaction with the lowest fee is removed - // from the pool in order to accommodate the new one. - maxTransactionsInPool: process.env.ARK_MAX_TRANSACTIONS_IN_POOL || 100000, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - maxTransactionsPerRequest: - process.env.ARK_TRANSACTION_POOL_MAX_PER_REQUEST || 40, - maxTransactionAge: 2700, -} diff --git a/packages/core-transaction-pool-mem/lib/index.js b/packages/core-transaction-pool-mem/lib/index.js deleted file mode 100644 index bb7f10efdd..0000000000 --- a/packages/core-transaction-pool-mem/lib/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const Connection = require('./connection') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'transactionPool', - extends: '@arkecosystem/core-transaction-pool', - async register(container, options) { - container.resolvePlugin('logger').info('Connecting to transaction pool') - - const transactionPoolManager = container.resolvePlugin( - 'transactionPoolManager', - ) - await transactionPoolManager.makeConnection(new Connection(options)) - - return transactionPoolManager.connection() - }, - async deregister(container, options) { - container - .resolvePlugin('logger') - .info('Disconnecting from transaction pool') - - return container.resolvePlugin('transactionPool').disconnect() - }, -} diff --git a/packages/core-transaction-pool-mem/lib/mem-pool-transaction.js b/packages/core-transaction-pool-mem/lib/mem-pool-transaction.js deleted file mode 100644 index c52b2afff1..0000000000 --- a/packages/core-transaction-pool-mem/lib/mem-pool-transaction.js +++ /dev/null @@ -1,64 +0,0 @@ -const assert = require('assert') -const crypto = require('@arkecosystem/crypto') - -const TRANSACTION_TYPES = crypto.constants.TRANSACTION_TYPES -const Transaction = crypto.models.Transaction - -/** - * A mem pool transaction. - * A normal transaction - * + a sequence number used to order by insertion time - * + a get-expiration-time method used to remove old transactions from the pool - */ -module.exports = class MemPoolTransaction { - /** - * Construct a MemPoolTransaction object. - * @param {Transaction} transaction base transaction object - * @param {Number} sequence insertion order sequence or undefined; - * if this is undefined at creation time, - * then it is assigned later using the - * setter method below - */ - constructor(transaction, sequence) { - assert(transaction instanceof Transaction) - this._transaction = transaction - - if (sequence !== undefined) { - assert(Number.isInteger(sequence)) - this._sequence = sequence - } - } - - get transaction() { - return this._transaction - } - - get sequence() { - return this._sequence - } - - set sequence(seq) { - assert.strictEqual(this._sequence, undefined) - this._sequence = seq - } - - /** - * Derive the transaction expiration time in number of seconds since - * the genesis block. - * @param {Number} maxTransactionAge maximum age (in seconds) of a transaction - * @return {Number} expiration time or null if the transaction does not expire - */ - expireAt(maxTransactionAge) { - const t = this._transaction - - if (t.expiration > 0) { - return t.expiration - } - - if (t.type !== TRANSACTION_TYPES.TIMELOCK_TRANSFER) { - return t.timestamp + maxTransactionAge - } - - return null - } -} diff --git a/packages/core-transaction-pool-mem/lib/mem.js b/packages/core-transaction-pool-mem/lib/mem.js deleted file mode 100644 index ba8935dbfa..0000000000 --- a/packages/core-transaction-pool-mem/lib/mem.js +++ /dev/null @@ -1,316 +0,0 @@ -const assert = require('assert') -const { slots } = require('@arkecosystem/crypto') - -class Mem { - /** - * Create the in-memory transaction pool structures. - */ - constructor() { - /** - * A monotonically increasing number, assigned to each new transaction and - * then incremented. - * Used to: - * - keep insertion order. - */ - this.sequence = 0 - - /** - * An array of MemPoolTransaction sorted by fee (the transaction with the - * highest fee is first). If the fee is equal, they are sorted by insertion - * order. - * Used to: - * - get the transactions with the highest fee - * - get the number of all transactions in the pool - */ - this.all = [] - - /** - * A boolean flag indicating whether `this.all` is indeed sorted or - * temporarily left unsorted. We use lazy sorting of `this.all`: - * - insertion just appends at the end (O(1)) + flag it as unsorted - * - deletion removes by using splice() (O(n)) + flag it as unsorted - * - lookup sorts if it is not sorted (O(n*log(n)) + flag it as sorted - */ - this.allIsSorted = true - - /** - * A map of (key=transaction id, value=MemPoolTransaction). - * Used to: - * - get a transaction, given its ID - */ - this.byId = {} - - /** - * A map of (key=sender public key, value=Set of MemPoolTransaction). - * Used to: - * - get all transactions from a given sender - * - get the number of all transactions from a given sender. - */ - this.bySender = {} - - /** - * An array of MemPoolTransaction, sorted by expiration (earliest date - * comes first). This array may not contain all transactions that are - * in the pool, transactions that are without expiration are not included. - * Used to: - * - find all transactions that have expired (have an expiration date - * earlier than a given date) - they are at the beginning of the array. - */ - this.byExpiration = [] - this.byExpirationIsSorted = true - - /** - * List of dirty transactions ids (that are not saved in the on-disk - * database yet). Used to delay and group operations to the on-disk database. - */ - this.dirty = { - added: new Set(), - removed: new Set(), - } - } - - /** - * Add a transaction. - * @param {MemPoolTransaction} memPoolTransaction transaction to add - * @param {Number} maxTransactionAge maximum age of a transaction in seconds - * @param {Boolean} thisIsDBLoad if true, then this is the initial - * loading from the database and we do - * not need to schedule the transaction - * that is being added for saving to disk - */ - add(memPoolTransaction, maxTransactionAge, thisIsDBLoad = false) { - const transaction = memPoolTransaction.transaction - - assert.strictEqual(this.byId[transaction.id], undefined) - - if (thisIsDBLoad) { - // Sequence is provided from outside, make sure we avoid duplicates - // later when we start using our this.sequence. - assert.strictEqual(typeof memPoolTransaction.sequence, 'number') - this.sequence = Math.max(this.sequence, memPoolTransaction.sequence) + 1 - } else { - // Sequence should only be set during DB load (when sequences come - // from the database). In other scenarios sequence is not set and we - // set it here. - memPoolTransaction.sequence = this.sequence++ - } - - this.all.push(memPoolTransaction) - this.allIsSorted = false - - this.byId[transaction.id] = memPoolTransaction - - const sender = transaction.senderPublicKey - if (this.bySender[sender] === undefined) { - // First transaction from this sender, create a new Set. - this.bySender[sender] = new Set([memPoolTransaction]) - } else { - // Append to existing transaction ids for this sender. - this.bySender[sender].add(memPoolTransaction) - } - - if (memPoolTransaction.expireAt(maxTransactionAge) !== null) { - this.byExpiration.push(memPoolTransaction) - this.byExpirationIsSorted = false - } - - if (!thisIsDBLoad) { - if (this.dirty.removed.has(transaction.id)) { - // If the transaction has been already in the pool and has been removed - // and the removal has not propagated to disk yet, just wipe it from the - // list of removed transactions, so that the old copy stays on disk. - this.dirty.removed.delete(transaction.id) - } else { - this.dirty.added.add(transaction.id) - } - } - } - - /** - * Remove a transaction. - * @param {String} id id of the transaction to remove - * @param {String} senderPublicKey public key of the sender, could be undefined - */ - remove(id, senderPublicKey) { - if (this.byId[id] === undefined) { - // Not found, not in pool - return - } - - if (senderPublicKey === undefined) { - senderPublicKey = this.byId[id].transaction.senderPublicKey - } - - const memPoolTransaction = this.byId[id] - - // XXX worst case: O(n) - let i = this.byExpiration.findIndex(e => e.transaction.id === id) - if (i !== -1) { - this.byExpiration.splice(i, 1) - } - - this.bySender[senderPublicKey].delete(memPoolTransaction) - if (this.bySender[senderPublicKey].size === 0) { - delete this.bySender[senderPublicKey] - } - - delete this.byId[id] - - i = this.all.findIndex(e => e.transaction.id === id) - assert.notStrictEqual(i, -1) - this.all.splice(i, 1) - this.allIsSorted = false - - if (this.dirty.added.has(id)) { - // This transaction has been added and deleted without data being synced - // to disk in between, so it will never touch the disk, just remove it - // from the added list. - this.dirty.added.delete(id) - } else { - this.dirty.removed.add(id) - } - } - - /** - * Get the number of transactions. - * @return Number - */ - getSize() { - return this.all.length - } - - /** - * Get all transactions from a given sender. - * @param {String} senderPublicKey public key of the sender - * @return {Set of MemPoolTransaction} all transactions for the given sender, could be empty Set - */ - getBySender(senderPublicKey) { - const memPoolTransactions = this.bySender[senderPublicKey] - if (memPoolTransactions !== undefined) { - return memPoolTransactions - } - return new Set() - } - - /** - * Get a transaction, given its id. - * @param {String} id transaction id - * @return {Transaction|undefined} - */ - getTransactionById(id) { - if (this.byId[id] === undefined) { - return undefined - } - return this.byId[id].transaction - } - - /** - * Get an array of all transactions ordered by fee. - * Transactions are ordered by fee (highest fee first) or by - * insertion time, if fees equal (earliest transaction first). - * @return {Array of MemPoolTransaction} transactions - */ - getTransactionsOrderedByFee() { - if (!this.allIsSorted) { - this.all.sort((a, b) => { - if (a.transaction.fee.isGreaterThan(b.transaction.fee)) { - return -1 - } - if (a.transaction.fee.isLessThan(b.transaction.fee)) { - return 1 - } - return a.sequence - b.sequence - }) - this.allIsSorted = true - } - - return this.all - } - - /** - * Check if a transaction with a given id exists. - * @param {String} id transaction id - * @return {Boolean} true if exists - */ - transactionExists(id) { - return this.byId[id] !== undefined - } - - /** - * Get the expired transactions. - * @param {Number} maxTransactionAge maximum age of a transaction in seconds - * @return {Array of Transaction} expired transactions - */ - getExpired(maxTransactionAge) { - if (!this.byExpirationIsSorted) { - this.byExpiration.sort( - (a, b) => a.expireAt(maxTransactionAge) - b.expireAt(maxTransactionAge), - ) - this.byExpirationIsSorted = true - } - - const now = slots.getTime() - - const transactions = [] - - for (const memPoolTransaction of this.byExpiration) { - if (memPoolTransaction.expireAt(maxTransactionAge) <= now) { - transactions.push(memPoolTransaction.transaction) - } else { - break - } - } - - return transactions - } - - /** - * Remove all transactions. - */ - flush() { - this.all = [] - this.allIsSorted = true - this.byId = {} - this.bySender = {} - this.byExpiration = [] - this.byExpirationIsSorted = true - this.dirty.added.clear() - this.dirty.removed.clear() - } - - /** - * Get the number of dirty transactions (added or removed, but those additions or - * removals have not been applied to the persistent storage). - * @return {Number} number of dirty transactions - */ - getNumberOfDirty() { - return this.dirty.added.size + this.dirty.removed.size - } - - /** - * Get the dirty transactions that were added and forget they are dirty. - * In other words, get the transactions that were added since the last - * call to this method (or to the flush() method). - * @return {Array of MemPoolTransaction} - */ - getDirtyAddedAndForget() { - const added = [] - this.dirty.added.forEach(id => added.push(this.byId[id])) - this.dirty.added.clear() - return added - } - - /** - * Get the ids of dirty transactions that were removed and forget them completely. - * In other words, get the transactions that were removed since the last - * call to this method (or to the flush() method). - * @return {Array of String} transaction ids - */ - getDirtyRemovedAndForget() { - const removed = Array.from(this.dirty.removed) - this.dirty.removed.clear() - return removed - } -} - -module.exports = Mem diff --git a/packages/core-transaction-pool-mem/lib/storage.js b/packages/core-transaction-pool-mem/lib/storage.js deleted file mode 100644 index 40ce9595ed..0000000000 --- a/packages/core-transaction-pool-mem/lib/storage.js +++ /dev/null @@ -1,121 +0,0 @@ -const BetterSqlite3 = require('better-sqlite3') -const fs = require('fs-extra') -const { Transaction } = require('@arkecosystem/crypto').models -const MemPoolTransaction = require('./mem-pool-transaction') - -/** - * A permanent storage (on-disk), supporting some basic functionalities required - * by the transaction pool. - */ -class Storage { - /** - * Construct the storage. - * @param {String} file - */ - constructor(file) { - this.table = 'pool' - - fs.ensureFileSync(file) - - this.db = new BetterSqlite3(file) - - this.db.exec(` - PRAGMA journal_mode=WAL; - CREATE TABLE IF NOT EXISTS ${this.table} ( - "sequence" INTEGER PRIMARY KEY, - "id" VARCHAR(64) UNIQUE, - "serialized" BLOB NOT NULL - ); - `) - } - - /** - * Close the storage. - */ - close() { - this.db.close() - this.db = null - } - - /** - * Add a bunch of new entries to the storage. - * @param {Array of MemPoolTransaction} data new entries to be added - */ - bulkAdd(data) { - if (data.length === 0) { - return - } - - const insertStatement = this.db.prepare( - `INSERT INTO ${this.table} ` + - '(sequence, id, serialized) VALUES ' + - '(:sequence, :id, :serialized);', - ) - - try { - this.db.prepare('BEGIN;').run() - - data.forEach(d => - insertStatement.run({ - sequence: d.sequence, - id: d.transaction.id, - serialized: Buffer.from(d.transaction.serialized, 'hex'), - }), - ) - - this.db.prepare('COMMIT;').run() - } finally { - if (this.db.inTransaction) { - this.db.prepare('ROLLBACK;').run() - } - } - } - - /** - * Remove a bunch of entries, given their ids. - * @param {Array of String} ids ids of the elements to be removed - */ - bulkRemoveById(ids) { - if (ids.length === 0) { - return - } - - const deleteStatement = this.db.prepare( - `DELETE FROM ${this.table} WHERE id = :id;`, - ) - - this.db.prepare('BEGIN;').run() - - ids.forEach(id => deleteStatement.run({ id })) - - this.db.prepare('COMMIT;').run() - } - - /** - * Load all entries. - * @return {Array of MemPoolTransaction} - */ - loadAll() { - const rows = this.db - .prepare( - `SELECT sequence, lower(HEX(serialized)) AS serialized FROM ${ - this.table - };`, - ) - .all() - - return rows - .map(r => ({ tx: new Transaction(r.serialized), ...r })) - .filter(r => r.tx.verified) - .map(r => new MemPoolTransaction(r.tx, r.sequence)) - } - - /** - * Delete all entries. - */ - deleteAll() { - this.db.exec(`DELETE FROM ${this.table};`) - } -} - -module.exports = Storage diff --git a/packages/core-transaction-pool-mem/package.json b/packages/core-transaction-pool-mem/package.json deleted file mode 100644 index d763d579a5..0000000000 --- a/packages/core-transaction-pool-mem/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@arkecosystem/core-transaction-pool-mem", - "description": "Transaction Pool - Memory implementation for Ark Core", - "version": "0.2.0", - "contributors": [ - "Kristjan Košič ", - "Brian Faust ", - "Alex Barnsley ", - "Vasil Dimov " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --forceExit --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --runInBand --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --forceExit", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-transaction-pool": "~0.2", - "@arkecosystem/crypto": "~0.2", - "better-sqlite3": "^5.0.1", - "delay": "^4.1.0", - "fs-extra": "^7.0.1" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2", - "@arkecosystem/core-utils": "~0.2", - "random-seed": "^0.3.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } -} diff --git a/packages/core-transaction-pool/CHANGELOG.md b/packages/core-transaction-pool/CHANGELOG.md deleted file mode 100644 index ffb6260b01..0000000000 --- a/packages/core-transaction-pool/CHANGELOG.md +++ /dev/null @@ -1,63 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.1 - 2018-12-04 - -### Fixed - -- Use the raw transaction data in `acceptChainedBlock` to avoid timestamp mismatches and second signature double spend errors - -## 0.2.0 - 2018-12-03 - -### Added - -- Delete pool wallet if no ballance or no transactions in pool -- Additional tests implemented -- Pool wallet manager implementation to guard the pool -- Blocking of sender if not in conditions or whitelisted -- Limit votes to 1 per wallet in pool via guard -- Return error feedback from the guard -- Handle dynamic fees -- Better transaction ping -- Broadcasting on launch of relay -- Cap the number of transactions the pool can hold - -### Changed - -- Splitting guard methods into more smaller units -- GetForgingTransactions moved to Transaction Pool -- Broadcasting only valid transactions further (verified, and wallet manager applied) -- Guard updated with wallet manager -- Handle numbers as `BigNumber` instances -- Dropped node.js 9 as minimum requirement in favour of node.js 10 -- Adjusted timeouts & lifetimes -- No longer add transactions that do not meet the fee requirements to the pool (BTC/ETH behaviour) -- No longer decline transactions that exceed the static fees -- Simplified the broadcasting logic -- Broadcast transactions when the pool is full - -### Fixed - -- Handling duplicates also on incomming payload level (before entering and checking with pool) -- Fix on applychained block - to use correct wallet and update from blockchain wallet only if necesary -- Call getTransactionIdsForForging() properly -- Properly log the transaction audit -- Properly determine valid transactions based on their type -- Handle unexpected errors in the guard -- Revert transactions with non-matching dynamic fees -- Revert excess transactions -- Reject transactions that have a timestamp from the future -- Remove invalid transactions from pool when receiving a bad block -- Handling of cold wallets while applying transactions - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-transaction-pool/LICENSE b/packages/core-transaction-pool/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-transaction-pool/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-transaction-pool/README.md b/packages/core-transaction-pool/README.md index 876e25757b..5bf2c68208 100644 --- a/packages/core-transaction-pool/README.md +++ b/packages/core-transaction-pool/README.md @@ -1,4 +1,4 @@ -# Ark Core - Transaction Pool - Interface +# Ark Core - Transaction Pool

@@ -14,10 +14,12 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [All Contributors](../../../../contributors) +- [Kristjan Košič](https://github.com/kristjank) +- [Brian Faust](https://github.com/faustbrian) +- [Alex Barnsley](https://github.com/alexbarnsley) +- [Vasil Dimov](https://github.com/vasild) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-transaction-pool/__tests__/__fixtures__/transactions.js b/packages/core-transaction-pool/__tests__/__fixtures__/transactions.js deleted file mode 100644 index 33c59c7cbc..0000000000 --- a/packages/core-transaction-pool/__tests__/__fixtures__/transactions.js +++ /dev/null @@ -1,84 +0,0 @@ -const { Transaction } = require('@arkecosystem/crypto').models - -exports.dummy1 = new Transaction({ - version: 1, - network: 23, - type: 0, - timestamp: 35672738, - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - fee: 10000000, - vendorFieldHex: '5449443a2030', - amount: 200000000, - expiration: 0, - recipientId: 'AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5', - signature: - '304502210096ec6e27176fa694638d6fff35d7a551b2ed8c479a7e03264026eea41a05edd702206c071c97d1c6cc3bfec64dfff808cb0d5dfe857803428efb80bf7717b85cb619', - vendorField: 'TID: 0', - id: 'a5e9e6039675563959a783fa672c0ffe65369168a1ecffa3c89bf82961d8dbad', -}) - -exports.dummy2 = new Transaction({ - version: 1, - network: 30, - type: 0, - timestamp: 35632190, - senderPublicKey: - '0310c283aac7b35b4ae6fab201d36e8322c3408331149982e16013a5bcb917081c', - fee: 10000000, - amount: 10000000, - expiration: 0, - recipientId: 'DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8', - signature: - '3045022100ead721ae139c0a18a7be2077453337f8305e02a474a3e4e35eb22bcf59ce474c02207ea591ac68b5cfee068ac605efb000c7e1e7479abc7f6ee7ece21f3a5c629800', - secondSignature: - '3044022006bd359a6820353e5e2f28adc0569f79ee7ed2918ee169bb149ca582f613fa760220502f39db1f9568edeb05df08d570a21a8204cb66f993f7cea6554a3298c548be', - signSignature: - '3044022006bd359a6820353e5e2f28adc0569f79ee7ed2918ee169bb149ca582f613fa760220502f39db1f9568edeb05df08d570a21a8204cb66f993f7cea6554a3298c548be', - id: 'e665f6634fdbbbc562f79b92c8f0acd621081680c247cb4a6fc987bf456ea554', -}) - -exports.dynamicFeeNormalDummy1 = new Transaction({ - type: 0, - amount: 200000000, - fee: 270000, - recipientId: 'AcjGpvDJEQdBVwspYsAs16B8Rv66zo7gyd', - timestamp: 45947670, - asset: {}, - vendorField: 'TID: 0', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '304402201ecbac2760492934873a13fdc7287958f464f4ee95fc13d4370a6a7c4351b2e902200ff75120a1663ab65eeb7a1795ad7c855363a0b61028751fcc2e7848b262df44', - id: 'b6d993f3294b2aee7c077cd15c2c54912427412fb4be291a559c93f51cf7e4cd', -}) - -exports.dynamicFeeLowDummy2 = new Transaction({ - type: 0, - amount: 200000000, - fee: 100, - recipientId: 'AabMvWPVKbdTHRcGBpATq9TEMiMD5xeJh9', - timestamp: 45947828, - asset: {}, - vendorField: 'TID: 0', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '3045022100a8754cee4492f30efa61825f39cda1a0de44b3d8e909b6c7e9055d7bc923b6d402200fab8abb348b4f5c7aaf10a9bb5451021e0e0e1fbb2f995555740b6d4ef8ccfe', - id: 'f7c7f073735d6900b4d12c70f75d7d1ad5ba41715d2254f50bf057580e05f7ec', -}) - -exports.dynamicFeeZero = new Transaction({ - type: 0, - amount: 200000000, - fee: 0, - recipientId: 'AVnRZSvrAeeSJZN3oSBxEF6mvvVpuKUXL5', - timestamp: 45948315, - asset: {}, - vendorField: 'TID: 0', - senderPublicKey: - '03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357', - signature: - '304402206119b9bfd045b0faa89436e4e487ff3e33aac310cea93f6e2870067ef42cc7e402204ccfc4756432901723fb70d98863adcf26f6e9ea963ba6f4063a886f44b82cb7', - id: '9966cc7fa7c646ab5771335809acb4a98c0c13c9045fa7976a1065f3a77c1721', -}) diff --git a/packages/core-transaction-pool/__tests__/__fixtures__/transactions.ts b/packages/core-transaction-pool/__tests__/__fixtures__/transactions.ts new file mode 100644 index 0000000000..7e87039269 --- /dev/null +++ b/packages/core-transaction-pool/__tests__/__fixtures__/transactions.ts @@ -0,0 +1,151 @@ +import { generators } from "@arkecosystem/core-test-utils"; +import { delegates } from "@arkecosystem/core-test-utils/src/fixtures/unitnet/delegates"; +const { generateTransfers } = generators; + +export const transactions = { + dummy1: generateTransfers( + "unitnet", + delegates[0].passphrase, + "AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy2: generateTransfers( + "unitnet", + delegates[0].passphrase, + "DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8", + 10000000, + 1, + false, + 10000000, + )[0], + + dummy3: generateTransfers( + "unitnet", + delegates[0].passphrase, + "ANqvJEMZcmUpcKBC8xiP1TntVkJeuZ3Lw3", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy4: generateTransfers( + "unitnet", + delegates[0].passphrase, + "AJ5eV59hu4xrbRCpoP3of7fEYWUteSVa8k", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy5: generateTransfers( + "unitnet", + delegates[0].passphrase, + "ASvC1E9hMLfANTi63S2gUMvr7rVZYJBj3u", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy6: generateTransfers( + "unitnet", + delegates[0].passphrase, + "Ac8utEr7XRebWRvArSBnbVoxbq6bXftAmL", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy7: generateTransfers( + "unitnet", + delegates[0].passphrase, + "ANWEaVfvAh3VTyZNYcuFESUum1XBmAvAdj", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy8: generateTransfers( + "unitnet", + delegates[0].passphrase, + "ALsZS24Dn4HYXwed5kAC5fKyB9BFzdmcSx", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy9: generateTransfers( + "unitnet", + delegates[0].passphrase, + "ANuaLhRuBJhTcHao7kTfDcfsewLQGr7x5G", + 200000000, + 1, + false, + 10000000, + )[0], + + dummy10: generateTransfers( + "unitnet", + delegates[1].passphrase, + "DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8", + 200000000, + 1, + false, + 10000000, + )[0], + + dynamicFeeNormalDummy1: generateTransfers( + "unitnet", + delegates[0].passphrase, + "AcjGpvDJEQdBVwspYsAs16B8Rv66zo7gyd", + 200000000, + 1, + false, + 280000, + )[0], + + dynamicFeeLowDummy2: generateTransfers( + "unitnet", + delegates[0].passphrase, + "AabMvWPVKbdTHRcGBpATq9TEMiMD5xeJh9", + 200000000, + 1, + false, + 100, + )[0], + + dynamicFeeZero: generateTransfers( + "unitnet", + delegates[0].passphrase, + "AVnRZSvrAeeSJZN3oSBxEF6mvvVpuKUXL5", + 200000000, + 1, + false, + 0, + )[0], + + dummyExp1: generateTransfers( + "unitnet", + delegates[1].passphrase, + "AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5", + 200000000, + 1, + )[0], + + dummyExp2: generateTransfers( + "unitnet", + delegates[1].passphrase, + "DFyDKsyvR4x9D9zrfEaPmeJxSniT5N5qY8", + 200000000, + 1, + )[0], +}; diff --git a/packages/core-transaction-pool/__tests__/__support__/setup.js b/packages/core-transaction-pool/__tests__/__support__/setup.js deleted file mode 100644 index b59568908d..0000000000 --- a/packages/core-transaction-pool/__tests__/__support__/setup.js +++ /dev/null @@ -1,16 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(60000) - -exports.setUp = async () => { - await appHelper.setUp({ - exit: '@arkecosystem/core-blockchain', - }) - - return app -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-transaction-pool/__tests__/__support__/setup.ts b/packages/core-transaction-pool/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..330882c231 --- /dev/null +++ b/packages/core-transaction-pool/__tests__/__support__/setup.ts @@ -0,0 +1,67 @@ +import { app } from "@arkecosystem/core-container"; +import { registerWithContainer, setUpContainer } from "../../../core-test-utils/src/helpers/container"; + +jest.setTimeout(60000); + +const options = { + enabled: true, + maxTransactionsPerSender: 300, + allowedSenders: [], + dynamicFees: { + enabled: true, + minFeePool: 1000, + minFeeBroadcast: 1000, + addonBytes: { + transfer: 100, + secondSignature: 250, + delegateRegistration: 400000, + vote: 100, + multiSignature: 500, + ipfs: 250, + timelockTransfer: 500, + multiPayment: 500, + delegateResignation: 400000, + }, + }, +}; + +export const setUp = async () => { + return await setUpContainer({ + exit: "@arkecosystem/core-blockchain", + exclude: ["@arkecosystem/core-transaction-pool"], + network: "unitnet", + }); +}; + +export const setUpFull = async () => { + await setUpContainer({ + exit: "@arkecosystem/core-transaction-pool", + exclude: ["@arkecosystem/core-transaction-pool"], + network: "unitnet", + }); + + await registerWithContainer(require("../../src/plugin").plugin, options); + + // now registering the plugins that need to be registered after transaction pool + // register p2p + await registerWithContainer(require("@arkecosystem/core-p2p").plugin, { + host: "0.0.0.0", + port: 4000, + minimumNetworkReach: 5, + coldStart: 5, + }); + await registerWithContainer(require("@arkecosystem/core-blockchain").plugin, {}); + return app; +}; + +export const tearDown = async () => { + await app.tearDown(); +}; + +export const tearDownFull = async () => { + await require("../../src/plugin").plugin.deregister(app, options); + await require("@arkecosystem/core-p2p").plugin.deregister(app, {}); + await require("@arkecosystem/core-blockchain").plugin.deregister(app, {}); + + await app.tearDown(); +}; diff --git a/packages/core-transaction-pool/__tests__/connection.test.ts b/packages/core-transaction-pool/__tests__/connection.test.ts new file mode 100644 index 0000000000..77ebebcbd4 --- /dev/null +++ b/packages/core-transaction-pool/__tests__/connection.test.ts @@ -0,0 +1,792 @@ +/* tslint:disable:max-line-length */ +import { app } from "@arkecosystem/core-container"; +import { Database } from "@arkecosystem/core-interfaces"; +import { bignumify } from "@arkecosystem/core-utils"; +import { Bignum, constants, models, slots } from "@arkecosystem/crypto"; +import dayjs from "dayjs-ext"; +import delay from "delay"; +import randomSeed from "random-seed"; +import { generators } from "../../core-test-utils"; +import { block2, delegates } from "../../core-test-utils/src/fixtures/unitnet"; +import { TransactionPool } from "../dist"; +import { transactions as mockData } from "./__fixtures__/transactions"; +import { setUpFull, tearDownFull } from "./__support__/setup"; + +const { ARKTOSHI, TransactionTypes } = constants; +const { Block, Transaction } = models; +const { generateTransfers } = generators; +const delegatesSecrets = delegates.map(d => d.secret); + +let config; +let databaseService: Database.IDatabaseService; +let connection: TransactionPool; + +beforeAll(async () => { + await setUpFull(); + + config = app.getConfig(); + databaseService = app.resolvePlugin("database"); + connection = app.resolvePlugin("transactionPool"); + + // Ensure no cold wallet and enough funds + databaseService.walletManager.findByPublicKey("000000000000000000000000000000000000000420000000000000000000000000"); + databaseService.walletManager.findByPublicKey( + "0310c283aac7b35b4ae6fab201d36e8322c3408331149982e16013a5bcb917081c", + ).balance = bignumify(200 * 1e8); + + // 100+ years in the future to avoid our hardcoded transactions used in these + // tests to expire + connection.options.maxTransactionAge = 4036608000; +}); + +afterAll(async () => { + await tearDownFull(); +}); + +beforeEach(() => { + connection.flush(); +}); + +describe("Connection", () => { + describe("getPoolSize", () => { + it("should return 0 if no transactions were added", () => { + expect(connection.getPoolSize()).toBe(0); + }); + + it("should return 2 if transactions were added", () => { + expect(connection.getPoolSize()).toBe(0); + + expect(connection.addTransaction(mockData.dummy1)).toEqual({ success: true }); + + expect(connection.getPoolSize()).toBe(1); + + expect(connection.addTransaction(mockData.dummy2)).toEqual({ success: true }); + + expect(connection.getPoolSize()).toBe(2); + }); + }); + + describe("getSenderSize", () => { + it("should return 0 if no transactions were added", () => { + expect(connection.getSenderSize("undefined")).toBe(0); + }); + + it("should return 2 if transactions were added", () => { + const senderPublicKey = mockData.dummy1.senderPublicKey; + + expect(connection.getSenderSize(senderPublicKey)).toBe(0); + + expect(connection.addTransaction(mockData.dummy1)).toEqual({ success: true }); + + expect(connection.getSenderSize(senderPublicKey)).toBe(1); + + expect(connection.addTransaction(mockData.dummy3)).toEqual({ success: true }); + + expect(connection.getSenderSize(senderPublicKey)).toBe(2); + }); + }); + + describe("addTransaction", () => { + it("should add the transaction to the pool", () => { + expect(connection.getPoolSize()).toBe(0); + + connection.addTransaction(mockData.dummy1); + + // Test adding already existent transaction + connection.addTransaction(mockData.dummy1); + + expect(connection.getPoolSize()).toBe(1); + }); + + it("should return error when adding 1 more transaction than maxTransactionsInPool", () => { + expect(connection.getPoolSize()).toBe(0); + + connection.addTransactions([mockData.dummy1, mockData.dummy2, mockData.dummy3, mockData.dummy4]); + + expect(connection.getPoolSize()).toBe(4); + + const maxTransactionsInPoolOrig = connection.options.maxTransactionsInPool; + connection.options.maxTransactionsInPool = 4; + + expect(connection.addTransaction(mockData.dummy5)).toEqual({ + transaction: mockData.dummy5, + type: "ERR_POOL_FULL", + message: + `Pool is full (has 4 transactions) and this transaction's fee ` + + `${mockData.dummy5.fee.toFixed()} is not higher than the lowest fee already in pool 10000000`, + success: false, + }); + + connection.options.maxTransactionsInPool = maxTransactionsInPoolOrig; + }); + + it("should replace lowest fee transaction when adding 1 more transaction than maxTransactionsInPool", () => { + expect(connection.getPoolSize()).toBe(0); + + connection.addTransactions([ + mockData.dummy1, + mockData.dummy2, + mockData.dummy3, + mockData.dynamicFeeNormalDummy1, + ]); + + expect(connection.getPoolSize()).toBe(4); + + const maxTransactionsInPoolOrig = connection.options.maxTransactionsInPool; + connection.options.maxTransactionsInPool = 4; + + expect(connection.addTransaction(mockData.dummy5)).toEqual({ + success: true, + }); + expect(connection.getTransactionIdsForForging(0, 10)).toEqual([ + mockData.dummy1.id, + mockData.dummy2.id, + mockData.dummy3.id, + mockData.dummy5.id, + ]); + + connection.options.maxTransactionsInPool = maxTransactionsInPoolOrig; + }); + }); + + describe("addTransactions", () => { + it("should add the transactions to the pool", () => { + expect(connection.getPoolSize()).toBe(0); + + connection.addTransactions([mockData.dummy1, mockData.dummy2]); + + expect(connection.getPoolSize()).toBe(2); + }); + + it("should not add not-appliable transactions", () => { + // This should be skipped due to insufficient funds + const highFeeTransaction = new Transaction(mockData.dummy3); + highFeeTransaction.fee = bignumify(1e9 * ARKTOSHI); + // changing public key as fixture transactions have the same one + highFeeTransaction.senderPublicKey = "000000000000000000000000000000000000000420000000000000000000000000"; + + const transactions = [ + mockData.dummy1, + mockData.dummy2, + highFeeTransaction, + mockData.dummy4, + mockData.dummy5, + mockData.dummy6, + ]; + + const { added, notAdded } = connection.addTransactions(transactions); + expect(notAdded[0].message).toEqual( + `["[PoolWalletManager] Can't apply transaction id:${ + mockData.dummy3.id + } from sender:AHkZLLjUdjjjJzNe1zCXqHh27bUhzg8GZw","Insufficient balance in the wallet"]`, + ); + expect(connection.getPoolSize()).toBe(5); + }); + }); + + describe("addTransactions with expiration", () => { + it("should add the transactions to the pool and they should expire", async () => { + expect(connection.getPoolSize()).toBe(0); + + const expireAfterSeconds = 3; + const expiration = slots.getTime() + expireAfterSeconds; + + const transactions = []; + + transactions.push(new Transaction(mockData.dummyExp1)); + transactions[transactions.length - 1].expiration = expiration; + + transactions.push(new Transaction(mockData.dummy1)); + + // Workaround: Increase balance of sender wallet to succeed + const insufficientBalanceTx: any = new Transaction(mockData.dummyExp2); + transactions.push(insufficientBalanceTx); + insufficientBalanceTx.expiration = expiration; + + transactions.push(mockData.dummy2); + + // Ensure no cold wallets + transactions.forEach(tx => databaseService.walletManager.findByPublicKey(tx.senderPublicKey)); + + const { added, notAdded } = connection.addTransactions(transactions); + expect(added).toHaveLength(4); + expect(notAdded).toBeEmpty(); + + expect(connection.getPoolSize()).toBe(4); + await delay((expireAfterSeconds + 1) * 1000); + expect(connection.getPoolSize()).toBe(2); + + transactions.forEach(t => connection.removeTransactionById(t.id)); + }); + }); + + describe("removeTransaction", () => { + it("should remove the specified transaction from the pool", () => { + connection.addTransaction(mockData.dummy1); + + expect(connection.getPoolSize()).toBe(1); + + connection.removeTransaction(mockData.dummy1); + + expect(connection.getPoolSize()).toBe(0); + }); + }); + + describe("removeTransactionById", () => { + it("should remove the specified transaction from the pool (by id)", () => { + connection.addTransaction(mockData.dummy1); + + expect(connection.getPoolSize()).toBe(1); + + connection.removeTransactionById(mockData.dummy1.id); + + expect(connection.getPoolSize()).toBe(0); + }); + + it("should do nothing when asked to delete a non-existent transaction", () => { + connection.addTransaction(mockData.dummy1); + + connection.removeTransactionById("nonexistenttransactionid"); + + expect(connection.getPoolSize()).toBe(1); + }); + }); + + describe("removeTransactionsForSender", () => { + it("should remove the senders transactions from the pool", () => { + connection.addTransaction(mockData.dummy1); + connection.addTransaction(mockData.dummy3); + connection.addTransaction(mockData.dummy4); + connection.addTransaction(mockData.dummy5); + connection.addTransaction(mockData.dummy6); + connection.addTransaction(mockData.dummy10); + + expect(connection.getPoolSize()).toBe(6); + + connection.removeTransactionsForSender(mockData.dummy1.senderPublicKey); + + expect(connection.getPoolSize()).toBe(1); + }); + }); + + describe("transactionExists", () => { + it("should return true if transaction is IN pool", () => { + connection.addTransactions([mockData.dummy1, mockData.dummy2]); + + expect(connection.transactionExists(mockData.dummy1.id)).toBeTrue(); + expect(connection.transactionExists(mockData.dummy2.id)).toBeTrue(); + }); + + it("should return false if transaction is NOT pool", () => { + expect(connection.transactionExists(mockData.dummy1.id)).toBeFalse(); + expect(connection.transactionExists(mockData.dummy2.id)).toBeFalse(); + }); + }); + + describe("hasExceededMaxTransactions", () => { + it("should be true if exceeded", () => { + connection.options.maxTransactionsPerSender = 5; + connection.options.allowedSenders = []; + connection.addTransaction(mockData.dummy3); + connection.addTransaction(mockData.dummy4); + connection.addTransaction(mockData.dummy5); + connection.addTransaction(mockData.dummy6); + connection.addTransaction(mockData.dummy7); + connection.addTransaction(mockData.dummy8); + connection.addTransaction(mockData.dummy9); + + expect(connection.getPoolSize()).toBe(7); + const exceeded = connection.hasExceededMaxTransactions(mockData.dummy3); + expect(exceeded).toBeTrue(); + }); + + it("should be falsy if not exceeded", () => { + connection.options.maxTransactionsPerSender = 7; + connection.options.allowedSenders = []; + + connection.addTransaction(mockData.dummy4); + connection.addTransaction(mockData.dummy5); + connection.addTransaction(mockData.dummy6); + + expect(connection.getPoolSize()).toBe(3); + const exceeded = connection.hasExceededMaxTransactions(mockData.dummy3); + expect(exceeded).toBeFalse(); + }); + + it("should be allowed to exceed if whitelisted", () => { + connection.flush(); + connection.options.maxTransactionsPerSender = 5; + connection.options.allowedSenders = [delegates[0].publicKey, delegates[1].publicKey]; + connection.addTransaction(mockData.dummy3); + connection.addTransaction(mockData.dummy4); + connection.addTransaction(mockData.dummy5); + connection.addTransaction(mockData.dummy6); + connection.addTransaction(mockData.dummy7); + connection.addTransaction(mockData.dummy8); + connection.addTransaction(mockData.dummy9); + + expect(connection.getPoolSize()).toBe(7); + const exceeded = connection.hasExceededMaxTransactions(mockData.dummy3); + expect(exceeded).toBeFalse(); + }); + }); + + describe("getTransaction", () => { + it("should return the specified transaction", () => { + connection.addTransaction(mockData.dummy1); + + const poolTransaction = connection.getTransaction(mockData.dummy1.id); + expect(poolTransaction).toBeObject(); + expect(poolTransaction.id).toBe(mockData.dummy1.id); + }); + + it("should return undefined for nonexisting transaction", () => { + const poolTransaction = connection.getTransaction("non existing id"); + expect(poolTransaction).toBeFalsy(); + }); + }); + + describe("getTransactions", () => { + it("should return transactions within the specified range", () => { + const transactions = [mockData.dummy1, mockData.dummy2]; + + connection.addTransactions(transactions); + + if (transactions[1].fee > transactions[0].fee) { + transactions.reverse(); + } + + for (const i of [0, 1]) { + const retrieved = connection.getTransactions(i, 1).map(serializedTx => new Transaction(serializedTx)); + + expect(retrieved.length).toBe(1); + expect(retrieved[0]).toBeObject(); + expect(retrieved[0].id).toBe(transactions[i].id); + } + }); + }); + + describe("getTransactionIdsForForging", () => { + it("should return an array of transactions ids", () => { + connection.addTransaction(mockData.dummy1); + connection.addTransaction(mockData.dummy2); + connection.addTransaction(mockData.dummy3); + connection.addTransaction(mockData.dummy4); + connection.addTransaction(mockData.dummy5); + connection.addTransaction(mockData.dummy6); + + const transactionIds = connection.getTransactionIdsForForging(0, 6); + + expect(transactionIds).toBeArray(); + expect(transactionIds[0]).toBe(mockData.dummy1.id); + expect(transactionIds[1]).toBe(mockData.dummy2.id); + expect(transactionIds[2]).toBe(mockData.dummy3.id); + expect(transactionIds[3]).toBe(mockData.dummy4.id); + expect(transactionIds[4]).toBe(mockData.dummy5.id); + expect(transactionIds[5]).toBe(mockData.dummy6.id); + }); + }); + + describe("getTransactionsForForging", () => { + it("should return an array of transactions serialized", () => { + const transactions = [mockData.dummy1, mockData.dummy2, mockData.dummy3, mockData.dummy4]; + connection.addTransactions(transactions); + + const transactionsForForging = connection.getTransactionsForForging(4); + + expect(transactionsForForging).toEqual(transactions.map(tx => tx.serialized)); + }); + }); + + describe("flush", () => { + it("should flush the pool", () => { + connection.addTransaction(mockData.dummy1); + + expect(connection.getPoolSize()).toBe(1); + + connection.flush(); + + expect(connection.getPoolSize()).toBe(0); + }); + }); + + describe("isSenderBlocked", () => { + it("should return false if sender is not blocked", () => { + const publicKey = "thisPublicKeyIsNotBlocked"; + expect(connection.isSenderBlocked(publicKey)).toBeFalse(); + }); + + it("should return true if sender is blocked", () => { + const publicKey = "thisPublicKeyIsBlocked"; + connection.blockedByPublicKey[publicKey] = dayjs().add(1, "hour"); + expect(connection.isSenderBlocked(publicKey)).toBeTrue(); + }); + + it("should return false and remove blockedByPublicKey[senderPublicKey] when sender is not blocked anymore", async () => { + const publicKey = "thisPublicKeyIsNotBlockedAnymore"; + connection.blockedByPublicKey[publicKey] = dayjs().add(1, "second"); + await delay(1100); + expect(connection.isSenderBlocked(publicKey)).toBeFalse(); + expect(connection.blockedByPublicKey[publicKey]).toBeUndefined(); + }); + }); + + describe("blockSender", () => { + it("should block sender for 1 hour", () => { + const publicKey = "publicKeyToBlock"; + const plus1HourBefore = dayjs().add(1, "hour"); + + const blockReleaseTime = connection.blockSender(publicKey); + + const plus1HourAfter = dayjs().add(1, "hour"); + expect(connection.blockedByPublicKey[publicKey]).toBe(blockReleaseTime); + expect(blockReleaseTime >= plus1HourBefore).toBeTrue(); + expect(blockReleaseTime <= plus1HourAfter).toBeTrue(); + }); + }); + + describe("acceptChainedBlock", () => { + beforeEach(() => connection.walletManager.reset()); + afterEach(() => connection.walletManager.reset()); + + it("should update wallet when accepting a chained block", () => { + const senderRecipientWallet = connection.walletManager.findByAddress(block2.transactions[0].recipientId); + const balanceBefore = senderRecipientWallet.balance; + + connection.acceptChainedBlock(new Block(block2)); + + expect(+senderRecipientWallet.balance).toBe(balanceBefore - block2.totalFee); + }); + + it("should remove transaction from pool if it's in the chained block", () => { + const transaction0 = new Transaction(block2.transactions[0]); + connection.addTransaction(transaction0); + + expect(connection.getTransactions(0, 10)).toEqual([transaction0.serialized]); + + connection.acceptChainedBlock(new Block(block2)); + + expect(connection.getTransactions(0, 10)).toEqual([]); + }); + + it("should purge and block sender if canApply() failed for a transaction in the chained block", () => { + const senderRecipientWallet = connection.walletManager.findByAddress(block2.transactions[0].recipientId); + senderRecipientWallet.balance = new Bignum(10); // not enough funds for transactions in block + + expect(connection.walletManager.allByAddress()).toEqual([senderRecipientWallet]); + + // canApply should fail because wallet has not enough funds + connection.acceptChainedBlock(new Block(block2)); + + expect(connection.walletManager.allByAddress()).toEqual([]); + expect(connection.isSenderBlocked(block2.transactions[0].senderPublicKey)).toBeTrue(); + }); + + it("should delete wallet of transaction sender if its balance is down to zero", () => { + const senderRecipientWallet = connection.walletManager.findByAddress(block2.transactions[0].recipientId); + senderRecipientWallet.balance = new Bignum(block2.totalFee); // exactly enough funds for transactions in block + + expect(connection.walletManager.allByAddress()).toEqual([senderRecipientWallet]); + + connection.acceptChainedBlock(new Block(block2)); + + expect(connection.walletManager.allByAddress()).toEqual([]); + }); + }); + + describe("buildWallets", () => { + beforeEach(() => connection.walletManager.reset()); + afterEach(() => connection.walletManager.reset()); + + it("should build wallets from transactions in the pool", async () => { + const transaction0 = new Transaction(block2.transactions[0]); + connection.addTransaction(transaction0); + + expect(connection.getTransactions(0, 10)).toEqual([transaction0.serialized]); + + connection.walletManager.reset(); + + expect(connection.walletManager.allByAddress()).toEqual([]); + + await connection.buildWallets(); + + const allWallets = connection.walletManager.allByAddress(); + expect(allWallets).toHaveLength(1); + expect(allWallets[0].publicKey).toBe(transaction0.senderPublicKey); + }); + + it("should handle getTransaction() not finding transaction", async () => { + const transaction0 = new Transaction(block2.transactions[0]); + connection.addTransaction(transaction0); + + expect(connection.getTransactions(0, 10)).toEqual([transaction0.serialized]); + + connection.walletManager.reset(); + + expect(connection.walletManager.allByAddress()).toEqual([]); + + jest.spyOn(connection, "getTransaction").mockImplementationOnce(id => undefined); + + await connection.buildWallets(); + + expect(connection.walletManager.allByAddress()).toEqual([]); + }); + + it("should not apply transaction to wallet if canApply() failed", async () => { + const transaction0 = new Transaction(block2.transactions[0]); + connection.addTransaction(transaction0); + expect(connection.getTransactions(0, 10)).toEqual([transaction0.serialized]); + + connection.walletManager.reset(); + expect(connection.walletManager.allByAddress()).toEqual([]); + + const senderRecipientWallet = connection.walletManager.findByAddress(block2.transactions[0].recipientId); + senderRecipientWallet.balance = new Bignum(10); // not enough funds for transactions in block + + jest.spyOn(connection.walletManager, "findByPublicKey").mockImplementationOnce( + publicKey => senderRecipientWallet, + ); + + await connection.buildWallets(); + + expect(connection.walletManager.allByAddress()).toEqual([]); // canApply() failed, wallet was purged + }); + }); + + describe("senderHasTransactionsOfType", () => { + it("should be false for non-existent sender", () => { + connection.addTransaction(mockData.dummy1); + + expect(connection.senderHasTransactionsOfType("nonexistent", TransactionTypes.Vote)).toBeFalse(); + }); + + it("should be false for existent sender with no votes", () => { + const tx = mockData.dummy1; + + connection.addTransaction(tx); + + expect(connection.senderHasTransactionsOfType(tx.senderPublicKey, TransactionTypes.Vote)).toBeFalse(); + }); + + it("should be true for existent sender with votes", () => { + const tx = mockData.dummy1; + + // Prevent 'wallet has already voted' error + connection.walletManager.findByPublicKey(tx.senderPublicKey).vote = ""; + + const voteTx = new Transaction(tx); + voteTx.id = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + voteTx.type = TransactionTypes.Vote; + voteTx.amount = bignumify(0); + voteTx.asset = { votes: [`+${tx.senderPublicKey}`] }; + + const transactions = [tx, voteTx, mockData.dummy2]; + + connection.addTransactions(transactions); + + expect(connection.senderHasTransactionsOfType(tx.senderPublicKey, TransactionTypes.Vote)).toBeTrue(); + }); + }); + + describe("shutdown and start", () => { + it("save and restore transactions", () => { + expect(connection.getPoolSize()).toBe(0); + + const transactions = [mockData.dummy1, mockData.dummy4]; + + connection.addTransactions(transactions); + + expect(connection.getPoolSize()).toBe(2); + + connection.disconnect(); + + connection.make(); + + expect(connection.getPoolSize()).toBe(2); + + transactions.forEach(t => + expect(connection.getTransaction(t.id).serialized.toLowerCase()).toBe(t.serialized.toLowerCase()), + ); + + connection.flush(); + }); + + it("remove forged when starting", async () => { + expect(connection.getPoolSize()).toBe(0); + + const block = await databaseService.getLastBlock(); + + // XXX This accesses directly block.transactions which is not even + // documented in packages/crypto/src/models/block.js + const forgedTransaction = block.transactions[0]; + + // Workaround: Add tx to exceptions so it gets applied, because the fee is 0. + config.set("exceptions.transactions", [forgedTransaction.id]); + + // For some reason all genesis transactions fail signature verification, so + // they are not loaded from the local storage and this fails otherwise. + // TODO: Use jest.spyOn() to change behavior instead. jest.restoreAllMocks() will reset afterwards + const original = databaseService.getForgedTransactionsIds; + databaseService.getForgedTransactionsIds = jest.fn(() => [forgedTransaction.id]); + + expect(forgedTransaction instanceof Transaction).toBeTrue(); + + const transactions = [mockData.dummy1, forgedTransaction, mockData.dummy4]; + + connection.addTransactions(transactions); + + expect(connection.getPoolSize()).toBe(3); + + connection.disconnect(); + + await connection.make(); + + expect(connection.getPoolSize()).toBe(2); + + transactions.splice(1, 1); + + transactions.forEach(t => + expect(connection.getTransaction(t.id).serialized.toLowerCase()).toBe(t.serialized.toLowerCase()), + ); + + connection.flush(); + + databaseService.getForgedTransactionsIds = original; + }); + }); + + describe("stress", () => { + const fakeTransactionId = i => `id${String(i)}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`; + + it("multiple additions and retrievals", () => { + // Abstract number which decides how many iterations are run by the test. + // Increase it to run more iterations. + const testSize = connection.options.syncInterval * 2; + + for (let i = 0; i < testSize; i++) { + const transaction = new Transaction(mockData.dummy1); + transaction.id = fakeTransactionId(i); + connection.addTransaction(transaction); + + if (i % 27 === 0) { + connection.removeTransaction(transaction); + } + } + + for (let i = 0; i < testSize * 2; i++) { + connection.getPoolSize(); + for (const sender of ["nonexistent", mockData.dummy1.senderPublicKey]) { + connection.getSenderSize(sender); + connection.hasExceededMaxTransactions(sender); + } + connection.getTransaction(fakeTransactionId(i)); + connection.getTransactions(0, i); + } + + for (let i = 0; i < testSize; i++) { + const transaction = new Transaction(mockData.dummy1); + transaction.id = fakeTransactionId(i); + connection.removeTransaction(transaction); + } + }); + + it("delete + add after sync", () => { + for (let i = 0; i < connection.options.syncInterval; i++) { + // tslint:disable-next-line:no-shadowed-variable + const transaction = new Transaction(mockData.dummy1); + transaction.id = fakeTransactionId(i); + connection.addTransaction(transaction); + } + + const transaction = new Transaction(mockData.dummy1); + transaction.id = fakeTransactionId(0); + connection.removeTransaction(transaction); + connection.addTransaction(transaction); + }); + + it("add many then get first few", () => { + const nAdd = 2000; + + // We use a predictable random number calculator in order to get + // a deterministic test. + const rand = randomSeed.create("0"); + + const allTransactions = []; + for (let i = 0; i < nAdd; i++) { + const transaction = new Transaction(mockData.dummy1); + transaction.id = fakeTransactionId(i); + transaction.fee = bignumify(rand.intBetween(0.002 * ARKTOSHI, 2 * ARKTOSHI)); + transaction.serialized = Transaction.serialize(transaction).toString("hex"); + allTransactions.push(transaction); + } + + // console.time(`time to add ${nAdd}`) + connection.addTransactions(allTransactions); + // console.timeEnd(`time to add ${nAdd}`) + + const nGet = 150; + + const topFeesExpected = allTransactions + .map(t => t.fee) + .sort((a, b) => b - a) + .slice(0, nGet) + .map(f => f.toString()); + + // console.time(`time to get first ${nGet}`) + const topTransactionsSerialized = connection.getTransactions(0, nGet); + // console.timeEnd(`time to get first ${nGet}`) + + const topFeesReceived = topTransactionsSerialized.map(e => new Transaction(e).fee.toString()); + + expect(topFeesReceived).toEqual(topFeesExpected); + }); + }); + + describe("purgeSendersWithInvalidTransactions", () => { + it("should purge transactions from sender when invalid", async () => { + const transfersA = generateTransfers("unitnet", delegatesSecrets[0], mockData.dummy1.recipientId, 1, 5); + + const transfersB = generateTransfers("unitnet", delegatesSecrets[1], mockData.dummy1.recipientId, 1, 1); + + const block = { + transactions: [...transfersA, ...transfersB], + }; + + block.transactions.forEach(tx => connection.addTransaction(tx)); + + expect(connection.getPoolSize()).toBe(6); + + // Last tx has a unique sender + block.transactions[5].verified = false; + + connection.purgeSendersWithInvalidTransactions(block); + expect(connection.getPoolSize()).toBe(5); + + // The remaining tx all have the same sender + block.transactions[0].verified = false; + + connection.purgeSendersWithInvalidTransactions(block); + expect(connection.getPoolSize()).toBe(0); + }); + }); + + describe("purgeBlock", () => { + it("should purge transactions from block", async () => { + const transactions = generateTransfers("unitnet", delegatesSecrets[0], mockData.dummy1.recipientId, 1, 5); + const block = { transactions }; + + block.transactions.forEach(tx => connection.addTransaction(tx)); + + expect(connection.getPoolSize()).toBe(5); + + connection.purgeBlock(block); + expect(connection.getPoolSize()).toBe(0); + }); + }); + + describe("driver", () => { + it("should get the driver instance", async () => { + expect(connection.driver()).toBe(connection.driver); + }); + }); +}); diff --git a/packages/core-transaction-pool/__tests__/dynamic-fee.test.js b/packages/core-transaction-pool/__tests__/dynamic-fee.test.js deleted file mode 100644 index 64a23d2602..0000000000 --- a/packages/core-transaction-pool/__tests__/dynamic-fee.test.js +++ /dev/null @@ -1,97 +0,0 @@ -const app = require('./__support__/setup') -const mockData = require('./__fixtures__/transactions') - -let dynamicFeeMatch -let blockchain -let container - -beforeAll(async () => { - container = await app.setUp() - await container.resolvePlugin('blockchain').start() - - dynamicFeeMatch = require('../lib/utils/dynamicfee-matcher') -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('static fees', () => { - beforeAll(() => { - blockchain = container.resolvePlugin('blockchain') - blockchain.getLastBlock = jest.fn(plugin => ({ - data: { - height: 20, - }, - })) - const h = blockchain.getLastBlock().data.height - container.resolvePlugin('config').getConstants(h).fees.dynamic = false - }) - - it('should be a function', () => { - expect(dynamicFeeMatch).toBeFunction() - }) - - it('should accept transactions matching the static fee for broadcast', () => { - expect(dynamicFeeMatch(mockData.dummy1).broadcast).toBeTrue() - expect(dynamicFeeMatch(mockData.dummy2).broadcast).toBeTrue() - }) - - it('should accept transactions matching the static fee to enter pool', () => { - expect(dynamicFeeMatch(mockData.dummy1).enterPool).toBeTrue() - expect(dynamicFeeMatch(mockData.dummy2).enterPool).toBeTrue() - }) - - it('should not broadcast transactions with a fee other than the static fee', () => { - expect( - dynamicFeeMatch(mockData.dynamicFeeNormalDummy1).broadcast, - ).toBeFalse() - expect(dynamicFeeMatch(mockData.dynamicFeeZero).broadcast).toBeFalse() - }) - - it('should not allow transactions with a fee other than the static fee to enter the pool', () => { - expect( - dynamicFeeMatch(mockData.dynamicFeeNormalDummy1).enterPool, - ).toBeFalse() - expect(dynamicFeeMatch(mockData.dynamicFeeZero).enterPool).toBeFalse() - }) -}) - -describe('dynamic fees', () => { - beforeAll(() => { - blockchain = container.resolvePlugin('blockchain') - blockchain.getLastBlock = jest.fn(plugin => ({ - data: { - height: 20, - }, - })) - const h = blockchain.getLastBlock().data.height - container.resolvePlugin('config').getConstants(h).fees.dynamic = true - }) - - it('should broadcast transactions with high enough fee', () => { - expect(dynamicFeeMatch(mockData.dummy1).broadcast).toBeTrue() - expect(dynamicFeeMatch(mockData.dummy2).broadcast).toBeTrue() - expect( - dynamicFeeMatch(mockData.dynamicFeeNormalDummy1).broadcast, - ).toBeTrue() - }) - - it('should accept transactions with high enough fee to enter the pool', () => { - expect(dynamicFeeMatch(mockData.dummy1).enterPool).toBeTrue() - expect(dynamicFeeMatch(mockData.dummy2).enterPool).toBeTrue() - expect( - dynamicFeeMatch(mockData.dynamicFeeNormalDummy1).enterPool, - ).toBeTrue() - }) - - it('should not broadcast transactions with too low fee', () => { - expect(dynamicFeeMatch(mockData.dynamicFeeLowDummy2).broadcast).toBeFalse() - expect(dynamicFeeMatch(mockData.dynamicFeeZero).broadcast).toBeFalse() - }) - - it('should not allow transactions with too low fee to enter the pool', () => { - expect(dynamicFeeMatch(mockData.dynamicFeeLowDummy2).enterPool).toBeFalse() - expect(dynamicFeeMatch(mockData.dynamicFeeZero).enterPool).toBeFalse() - }) -}) diff --git a/packages/core-transaction-pool/__tests__/dynamic-fee.test.ts b/packages/core-transaction-pool/__tests__/dynamic-fee.test.ts new file mode 100644 index 0000000000..569e0abdf7 --- /dev/null +++ b/packages/core-transaction-pool/__tests__/dynamic-fee.test.ts @@ -0,0 +1,121 @@ +import { Blockchain, Container } from "@arkecosystem/core-interfaces"; +import { calculateFee, dynamicFeeMatcher } from "../src/dynamic-fee"; +import { transactions } from "./__fixtures__/transactions"; +import { setUpFull, tearDownFull } from "./__support__/setup"; + +let config; +let blockchain: Blockchain.IBlockchain; +let container: Container.IContainer; + +beforeAll(async () => { + container = await setUpFull(); + + config = require("../src").config; + config.init(container.resolveOptions("transactionPool")); + blockchain = container.resolvePlugin("blockchain"); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +describe("static fees", () => { + beforeAll(() => { + blockchain.getLastBlock = jest.fn(plugin => ({ + data: { + height: 20, + }, + })); + + config.set("dynamicFees.enabled", false); + }); + + it("should accept transactions matching the static fee for broadcast", () => { + expect(dynamicFeeMatcher(transactions.dummy1).broadcast).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dummy2).broadcast).toBeTrue(); + }); + + it("should accept transactions matching the static fee to enter pool", () => { + expect(dynamicFeeMatcher(transactions.dummy1).enterPool).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dummy2).enterPool).toBeTrue(); + }); + + it("should not broadcast transactions with a fee other than the static fee", () => { + expect(dynamicFeeMatcher(transactions.dynamicFeeNormalDummy1).broadcast).toBeFalse(); + expect(dynamicFeeMatcher(transactions.dynamicFeeZero).broadcast).toBeFalse(); + }); + + it("should not allow transactions with a fee other than the static fee to enter the pool", () => { + expect(dynamicFeeMatcher(transactions.dynamicFeeNormalDummy1).enterPool).toBeFalse(); + expect(dynamicFeeMatcher(transactions.dynamicFeeZero).enterPool).toBeFalse(); + }); +}); + +describe("dynamic fees", () => { + let dynFeeConfig; + beforeAll(() => { + blockchain.getLastBlock = jest.fn(plugin => ({ + data: { + height: 20, + }, + })); + + config.set("dynamicFees.enabled", true); + + dynFeeConfig = config.get("dynamicFees"); + }); + + it("should broadcast transactions with high enough fee", () => { + expect(dynamicFeeMatcher(transactions.dummy1).broadcast).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dummy2).broadcast).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dynamicFeeNormalDummy1).broadcast).toBeTrue(); + + // testing with transaction fee === min fee for transaction broadcast + transactions.dummy3.fee = calculateFee(dynFeeConfig.minFeeBroadcast, transactions.dummy3); + transactions.dummy4.fee = calculateFee(dynFeeConfig.minFeeBroadcast, transactions.dummy4); + expect(dynamicFeeMatcher(transactions.dummy3).broadcast).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dummy4).broadcast).toBeTrue(); + }); + + it("should accept transactions with high enough fee to enter the pool", () => { + expect(dynamicFeeMatcher(transactions.dummy1).enterPool).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dummy2).enterPool).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dynamicFeeNormalDummy1).enterPool).toBeTrue(); + + // testing with transaction fee === min fee for transaction enter pool + transactions.dummy3.fee = calculateFee(dynFeeConfig.minFeePool, transactions.dummy3); + transactions.dummy4.fee = calculateFee(dynFeeConfig.minFeePool, transactions.dummy4); + expect(dynamicFeeMatcher(transactions.dummy3).enterPool).toBeTrue(); + expect(dynamicFeeMatcher(transactions.dummy4).enterPool).toBeTrue(); + }); + + it("should not broadcast transactions with too low fee", () => { + expect(dynamicFeeMatcher(transactions.dynamicFeeLowDummy2).broadcast).toBeFalse(); + expect(dynamicFeeMatcher(transactions.dynamicFeeZero).broadcast).toBeFalse(); + }); + + it("should not allow transactions with too low fee to enter the pool", () => { + expect(dynamicFeeMatcher(transactions.dynamicFeeLowDummy2).enterPool).toBeFalse(); + expect(dynamicFeeMatcher(transactions.dynamicFeeZero).enterPool).toBeFalse(); + }); +}); + +describe("calculateFee", () => { + it("should correctly calculate the transaction fee based on transaction size and addonBytes", () => { + config.set("dynamicFees.addonBytes", { + transfer: 137, + }); + expect(calculateFee(3, transactions.dummy1)).toBe((137 + transactions.dummy1.serialized.length / 2) * 3); + expect(calculateFee(6, transactions.dummy1)).toBe((137 + transactions.dummy1.serialized.length / 2) * 6); + + config.set("dynamicFees.addonBytes", { + transfer: 0, + }); + expect(calculateFee(9, transactions.dummy1)).toBe((transactions.dummy1.serialized.length / 2) * 9); + }); + + it("should default arktoshiPerByte to 1 if value provided is <= 0", () => { + expect(calculateFee(-50, transactions.dummy1)).toBe(calculateFee(1, transactions.dummy1)); + expect(calculateFee(0, transactions.dummy1)).toBe(calculateFee(1, transactions.dummy1)); + }); +}); diff --git a/packages/core-transaction-pool/__tests__/guard.test.js b/packages/core-transaction-pool/__tests__/guard.test.js deleted file mode 100644 index d9c0fea430..0000000000 --- a/packages/core-transaction-pool/__tests__/guard.test.js +++ /dev/null @@ -1,719 +0,0 @@ -/* eslint import/no-extraneous-dependencies: "off" */ -/* eslint no-await-in-loop: "off" */ -const Guard = require('@arkecosystem/core-transaction-pool/lib/guard') -const { slots, crypto } = require('@arkecosystem/crypto') -const bip39 = require('bip39') -const delegates = require('@arkecosystem/core-test-utils/fixtures/testnet/delegates') -const generateVote = require('@arkecosystem/core-test-utils/lib/generators/transactions/vote') -const generateSignature = require('@arkecosystem/core-test-utils/lib/generators/transactions/signature') -const generateDelegateReg = require('@arkecosystem/core-test-utils/lib/generators/transactions/delegate') -const generateTransfers = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const generateWallets = require('@arkecosystem/core-test-utils/lib/generators/wallets') -const app = require('./__support__/setup') - -let container -let guard -let transactionPool -let poolInterface - -beforeAll(async () => { - container = await app.setUp() - - transactionPool = container.resolvePlugin('transactionPool') - transactionPool.make() -}) - -afterAll(async () => { - await app.tearDown() -}) - -beforeEach(() => { - transactionPool.flush() - guard = new Guard(transactionPool) -}) - -describe('Transaction Guard', () => { - it('should be an object', () => { - expect(guard).toBeObject() - }) - - describe('validate', () => { - it('should be a function', () => { - expect(guard.validate).toBeFunction() - }) - - it.each([false, true])( - 'should not apply transactions for chained transfers involving cold wallets', - async inverseOrder => { - /* The logic here is we can't have a chained transfer A => B => C if B is a cold wallet. - A => B needs to be first confirmed (forged), then B can transfer to C - */ - - const arktoshi = 10 ** 8 - const delegate = inverseOrder ? delegates[8] : delegates[9] // don't re-use the same delegate (need clean balance) - const delegateWallet = transactionPool.walletManager.findByAddress( - delegate.address, - ) - - const wallets = generateWallets('testnet', 2) - const poolWallets = wallets.map(w => - transactionPool.walletManager.findByAddress(w.address), - ) - - expect(+delegateWallet.balance).toBe(+delegate.balance) - poolWallets.forEach(w => { - expect(+w.balance).toBe(0) - }) - - const transfer0 = { - // transfer from delegate to wallet 0 - from: delegate, - to: wallets[0], - amount: 100 * arktoshi, - } - const transfer1 = { - // transfer from wallet 0 to wallet 1 - from: wallets[0], - to: wallets[1], - amount: 55 * arktoshi, - } - const transfers = [transfer0, transfer1] - if (inverseOrder) { - transfers.reverse() - } - - for (const t of transfers) { - const transfer = generateTransfers( - 'testnet', - t.from.passphrase, - t.to.address, - t.amount, - 1, - )[0] - - await guard.validate([transfer]) - } - - // apply again transfer from 0 to 1 - const transfer = generateTransfers( - 'testnet', - transfer1.from.passphrase, - transfer1.to.address, - transfer1.amount, - 1, - )[0] - - await guard.validate([transfer]) - - const expectedError = { - message: - '["Cold wallet is not allowed to send until receiving transaction is confirmed."]', - type: 'ERR_APPLY', - } - expect(guard.errors[transfer.id]).toContainEqual(expectedError) - - // check final balances - expect(+delegateWallet.balance).toBe( - delegate.balance - (100 + 0.1) * arktoshi, - ) - expect(+poolWallets[0].balance).toBe(0) - expect(+poolWallets[1].balance).toBe(0) - }, - ) - - it('should not apply the tx to the balance of the sender & recipient with dyn fee < min fee', async () => { - const delegate0 = delegates[14] - const { publicKey } = crypto.getKeys(bip39.generateMnemonic()) - const newAddress = crypto.getAddress(publicKey) - - const delegateWallet = transactionPool.walletManager.findByPublicKey( - delegate0.publicKey, - ) - const newWallet = transactionPool.walletManager.findByPublicKey(publicKey) - - expect(+delegateWallet.balance).toBe(+delegate0.balance) - expect(+newWallet.balance).toBe(0) - - const amount1 = 123 * 10 ** 8 - const fee = 10 - const transfers = generateTransfers( - 'testnet', - delegate0.secret, - newAddress, - amount1, - 1, - false, - fee, - ) - - await guard.validate(transfers) - - expect(+delegateWallet.balance).toBe(+delegate0.balance) - expect(+newWallet.balance).toBe(0) - }) - - it('should update the balance of the sender & recipient with dyn fee > min fee', async () => { - const delegate1 = delegates[1] - const { publicKey } = crypto.getKeys(bip39.generateMnemonic()) - const newAddress = crypto.getAddress(publicKey) - - const delegateWallet = transactionPool.walletManager.findByPublicKey( - delegate1.publicKey, - ) - const newWallet = transactionPool.walletManager.findByPublicKey(publicKey) - - expect(+delegateWallet.balance).toBe(+delegate1.balance) - expect(+newWallet.balance).toBe(0) - - const amount1 = +delegateWallet.balance / 2 - const fee = 0.1 * 10 ** 8 - const transfers = generateTransfers( - 'testnet', - delegate1.secret, - newAddress, - amount1, - 1, - false, - fee, - ) - - await guard.validate(transfers) - expect(guard.errors).toEqual({}) - - // simulate forged transaction - newWallet.applyTransactionToRecipient(transfers[0]) - - expect(+delegateWallet.balance).toBe(+delegate1.balance - amount1 - fee) - expect(+newWallet.balance).toBe(amount1) - }) - - it('should update the balance of the sender & recipient with multiple transactions type', async () => { - const delegate2 = delegates[2] - const newWalletPassphrase = bip39.generateMnemonic() - const { publicKey } = crypto.getKeys(newWalletPassphrase) - const newAddress = crypto.getAddress(publicKey) - - const delegateWallet = transactionPool.walletManager.findByPublicKey( - delegate2.publicKey, - ) - const newWallet = transactionPool.walletManager.findByPublicKey(publicKey) - - expect(+delegateWallet.balance).toBe(+delegate2.balance) - expect(+newWallet.balance).toBe(0) - expect(guard.errors).toEqual({}) - - const amount1 = +delegateWallet.balance / 2 - const fee = 0.1 * 10 ** 8 - const voteFee = 10 ** 8 - const delegateRegFee = 25 * 10 ** 8 - const signatureFee = 5 * 10 ** 8 - const transfers = generateTransfers( - 'testnet', - delegate2.secret, - newAddress, - amount1, - 1, - false, - fee, - ) - const votes = generateVote( - 'testnet', - newWalletPassphrase, - delegate2.publicKey, - 1, - ) - const delegateRegs = generateDelegateReg( - 'testnet', - newWalletPassphrase, - 1, - ) - const signatures = generateSignature('testnet', newWalletPassphrase, 1) - - // Index wallets to not encounter cold wallet error - const allTransactions = [ - ...transfers, - ...votes, - ...delegateRegs, - ...signatures, - ] - - allTransactions.forEach(transaction => { - container - .resolvePlugin('database') - .walletManager.findByPublicKey(transaction.senderPublicKey) - }) - - // first validate the 1st transfer so that new wallet is updated with the amount - await guard.validate(transfers) - - // simulate forged transaction - newWallet.applyTransactionToRecipient(transfers[0]) - - expect(guard.errors).toEqual({}) - expect(+newWallet.balance).toBe(amount1) - - // reset guard, if not the 1st transaction will still be in this.accept and mess up - guard = new Guard(transactionPool) - - await guard.validate([votes[0], delegateRegs[0], signatures[0]]) - - expect(guard.errors).toEqual({}) - expect(+delegateWallet.balance).toBe(+delegate2.balance - amount1 - fee) - expect(+newWallet.balance).toBe( - amount1 - voteFee - delegateRegFee - signatureFee, - ) - }) - - it('should not accept transaction in excess', async () => { - const delegate3 = delegates[3] - const newWalletPassphrase = bip39.generateMnemonic() - const { publicKey } = crypto.getKeys(newWalletPassphrase) - const newAddress = crypto.getAddress(publicKey) - - const delegateWallet = transactionPool.walletManager.findByPublicKey( - delegate3.publicKey, - ) - const newWallet = transactionPool.walletManager.findByPublicKey(publicKey) - - // Make sure it is not considered a cold wallet - container.resolvePlugin('database').walletManager.reindex(newWallet) - - expect(+delegateWallet.balance).toBe(+delegate3.balance) - expect(+newWallet.balance).toBe(0) - - // first, transfer coins to new wallet so that we can test from it then - const amount1 = 1000 * 10 ** 8 - const fee = 0.1 * 10 ** 8 - const transfers1 = generateTransfers( - 'testnet', - delegate3.secret, - newAddress, - amount1, - 1, - ) - await guard.validate(transfers1) - - // simulate forged transaction - newWallet.applyTransactionToRecipient(transfers1[0]) - - expect(+delegateWallet.balance).toBe(+delegate3.balance - amount1 - fee) - expect(+newWallet.balance).toBe(amount1) - - // transfer almost everything from new wallet so that we don't have enough for any other transaction - const amount2 = 999 * 10 ** 8 - const transfers2 = generateTransfers( - 'testnet', - newWalletPassphrase, - delegate3.address, - amount2, - 1, - ) - await guard.validate(transfers2) - - // simulate forged transaction - delegateWallet.applyTransactionToRecipient(transfers2[0]) - - expect(+newWallet.balance).toBe(amount1 - amount2 - fee) - - // now try to validate any other transaction - should not be accepted because in excess - const transferAmount = 0.5 * 10 ** 8 - const transferDynFee = 0.5 * 10 ** 8 - const allTransactions = [ - generateTransfers( - 'testnet', - newWalletPassphrase, - delegate3.address, - transferAmount, - 1, - false, - transferDynFee, - ), - generateSignature('testnet', newWalletPassphrase, 1), - generateVote('testnet', newWalletPassphrase, delegate3.publicKey, 1), - generateDelegateReg('testnet', newWalletPassphrase, 1), - ] - - for (const transaction of allTransactions) { - await guard.validate(transaction) // eslint-disable-line no-await-in-loop - - const errorExpected = [ - { - message: `["[PoolWalletManager] Can't apply transaction id:${ - transaction[0].id - } from sender:${ - newWallet.address - }","Insufficient balance in the wallet"]`, - type: 'ERR_APPLY', - }, - ] - expect(guard.errors[transaction[0].id]).toEqual(errorExpected) - - expect(+delegateWallet.balance).toBe( - +delegate3.balance - amount1 - fee + amount2, - ) - expect(+newWallet.balance).toBe(amount1 - amount2 - fee) - } - }) - - it('should not validate 2 double spending transactions', async () => { - const amount = 245098000000000 - 5098000000000 // a bit less than the delegates' balance - const transactions = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[1].address, - amount, - 2, - true, - ) - - const result = await guard.validate(transactions) - - expect(result.errors[transactions[1].id]).toEqual([ - { - message: `["[PoolWalletManager] Can't apply transaction id:${ - transactions[1].id - } from sender:${ - delegates[0].address - }","Insufficient balance in the wallet"]`, - type: 'ERR_APPLY', - }, - ]) - }) - - it.each([3, 5, 8])( - 'should validate emptying wallet with %i transactions', - async txNumber => { - // use txNumber so that we use a different delegate for each test case - const sender = delegates[txNumber] - const senderWallet = transactionPool.walletManager.findByPublicKey( - sender.publicKey, - ) - const receivers = generateWallets('testnet', 2) - const amountPlusFee = Math.floor(senderWallet.balance / txNumber) - const lastAmountPlusFee = - senderWallet.balance - (txNumber - 1) * amountPlusFee - const transferFee = 10000000 - - const transactions = generateTransfers( - 'testnet', - sender.secret, - receivers[0].address, - amountPlusFee - transferFee, - txNumber - 1, - true, - ) - const lastTransaction = generateTransfers( - 'testnet', - sender.secret, - receivers[1].address, - lastAmountPlusFee - transferFee, - 1, - true, - ) - // we change the receiver in lastTransaction to prevent having 2 exact - // same transactions with same id (if not, could be same as transactions[0]) - - const result = await guard.validate( - transactions.concat(lastTransaction), - ) - - expect(result.errors).toEqual(null) - }, - ) - - it.each([3, 5, 8])( - 'should not validate emptying wallet with %i transactions when the last one is 1 arktoshi too much', - async txNumber => { - // use txNumber + 1 so that we don't use the same delegates as the above test - const sender = delegates[txNumber + 1] - const receivers = generateWallets('testnet', 2) - const amountPlusFee = Math.floor(sender.balance / txNumber) - const lastAmountPlusFee = - sender.balance - (txNumber - 1) * amountPlusFee + 1 - const transferFee = 10000000 - - const transactions = generateTransfers( - 'testnet', - sender.secret, - receivers[0].address, - amountPlusFee - transferFee, - txNumber - 1, - true, - ) - const lastTransaction = generateTransfers( - 'testnet', - sender.secret, - receivers[1].address, - lastAmountPlusFee - transferFee, - 1, - true, - ) - // we change the receiver in lastTransaction to prevent having 2 - // exact same transactions with same id (if not, could be same as transactions[0]) - - const allTransactions = transactions.concat(lastTransaction) - - const result = await guard.validate(allTransactions) - - expect(Object.keys(result.errors).length).toBe(1) - expect(result.errors[lastTransaction[0].id]).toEqual([ - { - message: `["[PoolWalletManager] Can't apply transaction id:${ - lastTransaction[0].id - } from sender:${ - sender.address - }","Insufficient balance in the wallet"]`, - type: 'ERR_APPLY', - }, - ]) - }, - ) - }) - - describe('__filterAndTransformTransactions', () => { - it('should be a function', () => { - expect(guard.__filterAndTransformTransactions).toBeFunction() - }) - - it('should reject duplicate transactions', () => { - const transactionExists = guard.pool.transactionExists - guard.pool.transactionExists = jest.fn(() => true) - - const tx = { id: '1' } - guard.__filterAndTransformTransactions([tx]) - - expect(guard.errors[tx.id]).toEqual([ - { - message: `Duplicate transaction ${tx.id}`, - type: 'ERR_DUPLICATE', - }, - ]) - - guard.pool.transactionExists = transactionExists - }) - - it('should reject blocked senders', () => { - const transactionExists = guard.pool.transactionExists - guard.pool.transactionExists = jest.fn(() => false) - const isSenderBlocked = guard.pool.isSenderBlocked - guard.pool.isSenderBlocked = jest.fn(() => true) - - const tx = { id: '1', senderPublicKey: 'affe' } - guard.__filterAndTransformTransactions([tx]) - - expect(guard.errors[tx.id]).toEqual([ - { - message: `Transaction ${tx.id} rejected. Sender ${ - tx.senderPublicKey - } is blocked.`, - type: 'ERR_SENDER_BLOCKED', - }, - ]) - - guard.pool.isSenderBlocked = isSenderBlocked - guard.pool.transactionExists = transactionExists - }) - - it('should reject transactions from the future', () => { - const now = 47157042 // seconds since genesis block - const transactionExists = guard.pool.transactionExists - guard.pool.transactionExists = jest.fn(() => false) - const getTime = slots.getTime - slots.getTime = jest.fn(() => now) - - const secondsInFuture = 3601 - const tx = { - id: '1', - senderPublicKey: 'affe', - timestamp: slots.getTime() + secondsInFuture, - } - guard.__filterAndTransformTransactions([tx]) - - expect(guard.errors[tx.id]).toEqual([ - { - message: `Transaction ${ - tx.id - } is ${secondsInFuture} seconds in the future`, - type: 'ERR_FROM_FUTURE', - }, - ]) - - slots.getTime = getTime - guard.pool.transactionExists = transactionExists - }) - }) - - describe('__validateTransaction', () => { - it('should be a function', () => { - expect(guard.__validateTransaction).toBeFunction() - }) - }) - - describe('__removeForgedTransactions', () => { - it('should be a function', () => { - expect(guard.__removeForgedTransactions).toBeFunction() - }) - - it('should remove forged transactions', async () => { - const database = container.resolvePlugin('database') - const getForgedTransactionsIds = database.getForgedTransactionsIds - - const transfers = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[0].senderPublicKey, - 1, - 4, - ) - - transfers.forEach(tx => { - guard.accept.set(tx.id, tx) - guard.broadcast.set(tx.id, tx) - }) - - const forgedTx = transfers[2] - database.getForgedTransactionsIds = jest.fn(() => [forgedTx.id]) - - await guard.__removeForgedTransactions() - - expect(guard.accept.size).toBe(3) - expect(guard.broadcast.size).toBe(3) - - expect(guard.errors[forgedTx.id]).toHaveLength(1) - expect(guard.errors[forgedTx.id][0].type).toEqual('ERR_FORGED') - - database.getForgedTransactionsIds = getForgedTransactionsIds - }) - }) - - describe('__addTransactionsToPool', () => { - it('should be a function', () => { - expect(guard.__addTransactionsToPool).toBeFunction() - }) - - it('should add transactions to the pool', () => { - const transfers = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[0].senderPublicKey, - 1, - 4, - ) - - transfers.forEach(tx => { - guard.accept.set(tx.id, tx) - guard.broadcast.set(tx.id, tx) - }) - - expect(guard.errors).toEqual({}) - - guard.__addTransactionsToPool() - - expect(guard.errors).toEqual({}) - expect(guard.accept.size).toBe(4) - expect(guard.broadcast.size).toBe(4) - }) - - it('should raise ERR_ALREADY_IN_POOL when adding existing transactions', () => { - const transfers = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[0].senderPublicKey, - 1, - 4, - ) - - transfers.forEach(tx => { - guard.accept.set(tx.id, tx) - guard.broadcast.set(tx.id, tx) - }) - - expect(guard.errors).toEqual({}) - - guard.__addTransactionsToPool() - - expect(guard.errors).toEqual({}) - expect(guard.accept.size).toBe(4) - expect(guard.broadcast.size).toBe(4) - - // Adding again invokes ERR_ALREADY_IN_POOL - guard.__addTransactionsToPool() - - expect(guard.accept.size).toBe(0) - expect(guard.broadcast.size).toBe(0) - - for (const transfer of transfers) { - expect(guard.errors[transfer.id]).toHaveLength(1) - expect(guard.errors[transfer.id][0].type).toEqual('ERR_ALREADY_IN_POOL') - } - }) - - it('should raise ERR_POOL_FULL when attempting to add transactions to a full pool', () => { - const poolSize = transactionPool.options.maxTransactionsInPool - transactionPool.options.maxTransactionsInPool = 3 - - const transfers = generateTransfers( - 'testnet', - delegates[0].secret, - delegates[0].senderPublicKey, - 1, - 4, - ) - - transfers.forEach(tx => { - guard.accept.set(tx.id, tx) - guard.broadcast.set(tx.id, tx) - }) - - guard.__addTransactionsToPool() - - expect(guard.accept.size).toBe(3) - expect(guard.broadcast.size).toBe(4) - - expect(guard.errors[transfers[3].id]).toHaveLength(1) - expect(guard.errors[transfers[3].id][0].type).toEqual('ERR_POOL_FULL') - - transactionPool.options.maxTransactionsInPool = poolSize - }) - }) - - describe('__pushError', () => { - it('should be a function', () => { - expect(guard.__pushError).toBeFunction() - }) - - it('should have error for transaction', () => { - expect(guard.errors).toBeEmpty() - - guard.__pushError({ id: 1 }, 'ERR_INVALID', 'Invalid.') - - expect(guard.errors).toBeObject() - expect(guard.errors['1']).toBeArray() - expect(guard.errors['1']).toHaveLength(1) - expect(guard.errors['1']).toEqual([ - { message: 'Invalid.', type: 'ERR_INVALID' }, - ]) - - expect(guard.invalid.size).toEqual(1) - expect(guard.invalid.entries().next().value[1]).toEqual({ id: 1 }) - }) - - it('should have multiple errors for transaction', () => { - expect(guard.errors).toBeEmpty() - - guard.__pushError({ id: 1 }, 'ERR_INVALID', 'Invalid 1.') - guard.__pushError({ id: 1 }, 'ERR_INVALID', 'Invalid 2.') - - expect(guard.errors).toBeObject() - expect(guard.errors['1']).toBeArray() - expect(guard.errors['1']).toHaveLength(2) - expect(guard.errors['1']).toEqual([ - { message: 'Invalid 1.', type: 'ERR_INVALID' }, - { message: 'Invalid 2.', type: 'ERR_INVALID' }, - ]) - - expect(guard.invalid.size).toEqual(1) - expect(guard.invalid.entries().next().value[1]).toEqual({ id: 1 }) - }) - }) -}) diff --git a/packages/core-transaction-pool/__tests__/guard.test.ts b/packages/core-transaction-pool/__tests__/guard.test.ts new file mode 100644 index 0000000000..308db00d7a --- /dev/null +++ b/packages/core-transaction-pool/__tests__/guard.test.ts @@ -0,0 +1,1134 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import { generators } from "@arkecosystem/core-test-utils"; +import { configManager, constants, crypto, models, slots } from "@arkecosystem/crypto"; +import bip39 from "bip39"; +import "jest-extended"; +import { delegates, genesisBlock, wallets, wallets2ndSig } from "../../core-test-utils/src/fixtures/unitnet"; +import { config as localConfig } from "../src/config"; +import { setUpFull, tearDownFull } from "./__support__/setup"; + +const { Block } = models; +const { + generateDelegateRegistration, + generateSecondSignature, + generateTransfers, + generateVote, + generateWallets, +} = generators; + +let TransactionGuard; + +let container: Container.IContainer; +let guard; +let transactionPool; +let blockchain; + +beforeAll(async () => { + container = await setUpFull(); + + TransactionGuard = require("../src").TransactionGuard; + + transactionPool = container.resolvePlugin("transactionPool"); + blockchain = container.resolvePlugin("blockchain"); + localConfig.init(transactionPool.options); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +beforeEach(() => { + transactionPool.flush(); + guard = new TransactionGuard(transactionPool); +}); + +describe("Transaction Guard", () => { + describe("validate", () => { + it.each([false, true])( + "should not apply transactions for chained transfers involving cold wallets", + async inverseOrder => { + /* The logic here is we can't have a chained transfer A => B => C if B is a cold wallet. + A => B needs to be first confirmed (forged), then B can transfer to C + */ + + const arktoshi = 10 ** 8; + // don't re-use the same delegate (need clean balance) + const delegate = inverseOrder ? delegates[8] : delegates[9]; + const delegateWallet = transactionPool.walletManager.findByAddress(delegate.address); + + const newWallets = generateWallets("unitnet", 2); + const poolWallets = newWallets.map(w => transactionPool.walletManager.findByAddress(w.address)); + + expect(+delegateWallet.balance).toBe(+delegate.balance); + poolWallets.forEach(w => { + expect(+w.balance).toBe(0); + }); + + const transfer0 = { + // transfer from delegate to wallet 0 + from: delegate, + to: newWallets[0], + amount: 100 * arktoshi, + }; + const transfer1 = { + // transfer from wallet 0 to wallet 1 + from: newWallets[0], + to: newWallets[1], + amount: 55 * arktoshi, + }; + const transfers = [transfer0, transfer1]; + if (inverseOrder) { + transfers.reverse(); + } + + for (const t of transfers) { + const transferTx = generateTransfers("unitnet", t.from.passphrase, t.to.address, t.amount, 1)[0]; + + await guard.validate([transferTx]); + } + + // apply again transfer from 0 to 1 + const transfer = generateTransfers( + "unitnet", + transfer1.from.passphrase, + transfer1.to.address, + transfer1.amount, + 1, + )[0]; + + await guard.validate([transfer]); + + const expectedError = { + message: '["Cold wallet is not allowed to send until receiving transaction is confirmed."]', + type: "ERR_APPLY", + }; + expect(guard.errors[transfer.id]).toContainEqual(expectedError); + + // check final balances + expect(+delegateWallet.balance).toBe(delegate.balance - (100 + 0.1) * arktoshi); + expect(+poolWallets[0].balance).toBe(0); + expect(+poolWallets[1].balance).toBe(0); + }, + ); + + it("should not apply the tx to the balance of the sender & recipient with dyn fee < min fee", async () => { + const delegate0 = delegates[14]; + const { publicKey } = crypto.getKeys(bip39.generateMnemonic()); + const newAddress = crypto.getAddress(publicKey); + + const delegateWallet = transactionPool.walletManager.findByPublicKey(delegate0.publicKey); + const newWallet = transactionPool.walletManager.findByPublicKey(publicKey); + + expect(+delegateWallet.balance).toBe(+delegate0.balance); + expect(+newWallet.balance).toBe(0); + + const amount1 = 123 * 10 ** 8; + const fee = 10; + const transfers = generateTransfers("unitnet", delegate0.secret, newAddress, amount1, 1, false, fee); + + await guard.validate(transfers); + + expect(+delegateWallet.balance).toBe(+delegate0.balance); + expect(+newWallet.balance).toBe(0); + }); + + it("should update the balance of the sender & recipient with dyn fee > min fee", async () => { + const delegate1 = delegates[1]; + const { publicKey } = crypto.getKeys(bip39.generateMnemonic()); + const newAddress = crypto.getAddress(publicKey); + + const delegateWallet = transactionPool.walletManager.findByPublicKey(delegate1.publicKey); + const newWallet = transactionPool.walletManager.findByPublicKey(publicKey); + + expect(+delegateWallet.balance).toBe(+delegate1.balance); + expect(+newWallet.balance).toBe(0); + + const amount1 = +delegateWallet.balance / 2; + const fee = 0.1 * 10 ** 8; + const transfers = generateTransfers("unitnet", delegate1.secret, newAddress, amount1, 1, false, fee); + + await guard.validate(transfers); + expect(guard.errors).toEqual({}); + + // simulate forged transaction + newWallet.applyTransactionToRecipient(transfers[0]); + + expect(+delegateWallet.balance).toBe(+delegate1.balance - amount1 - fee); + expect(+newWallet.balance).toBe(amount1); + }); + + it("should update the balance of the sender & recipient with multiple transactions type", async () => { + const delegate2 = delegates[2]; + const newWalletPassphrase = bip39.generateMnemonic(); + const { publicKey } = crypto.getKeys(newWalletPassphrase); + const newAddress = crypto.getAddress(publicKey); + + const delegateWallet = transactionPool.walletManager.findByPublicKey(delegate2.publicKey); + const newWallet = transactionPool.walletManager.findByPublicKey(publicKey); + + expect(+delegateWallet.balance).toBe(+delegate2.balance); + expect(+newWallet.balance).toBe(0); + expect(guard.errors).toEqual({}); + + const amount1 = +delegateWallet.balance / 2; + const fee = 0.1 * 10 ** 8; + const voteFee = 10 ** 8; + const delegateRegFee = 25 * 10 ** 8; + const signatureFee = 5 * 10 ** 8; + const transfers = generateTransfers("unitnet", delegate2.secret, newAddress, amount1, 1, false, fee); + const votes = generateVote("unitnet", newWalletPassphrase, delegate2.publicKey, 1); + const delegateRegs = generateDelegateRegistration("unitnet", newWalletPassphrase, 1); + const signatures = generateSecondSignature("unitnet", newWalletPassphrase, 1); + + // Index wallets to not encounter cold wallet error + const allTransactions = [...transfers, ...votes, ...delegateRegs, ...signatures]; + + allTransactions.forEach(transaction => { + container.resolvePlugin("database").walletManager.findByPublicKey(transaction.senderPublicKey); + }); + + // first validate the 1st transfer so that new wallet is updated with the amount + await guard.validate(transfers); + + // simulate forged transaction + newWallet.applyTransactionToRecipient(transfers[0]); + + expect(guard.errors).toEqual({}); + expect(+newWallet.balance).toBe(amount1); + + // reset guard, if not the 1st transaction will still be in this.accept and mess up + guard = new TransactionGuard(transactionPool); + + await guard.validate([votes[0], delegateRegs[0], signatures[0]]); + + expect(guard.errors).toEqual({}); + expect(+delegateWallet.balance).toBe(+delegate2.balance - amount1 - fee); + expect(+newWallet.balance).toBe(amount1 - voteFee - delegateRegFee - signatureFee); + }); + + it("should not accept transaction in excess", async () => { + const delegate3 = delegates[3]; + const newWalletPassphrase = bip39.generateMnemonic(); + const { publicKey } = crypto.getKeys(newWalletPassphrase); + const newAddress = crypto.getAddress(publicKey); + + const delegateWallet = transactionPool.walletManager.findByPublicKey(delegate3.publicKey); + const newWallet = transactionPool.walletManager.findByPublicKey(publicKey); + + // Make sure it is not considered a cold wallet + container.resolvePlugin("database").walletManager.reindex(newWallet); + + expect(+delegateWallet.balance).toBe(+delegate3.balance); + expect(+newWallet.balance).toBe(0); + + // first, transfer coins to new wallet so that we can test from it then + const amount1 = 1000 * 10 ** 8; + const fee = 0.1 * 10 ** 8; + const transfers1 = generateTransfers("unitnet", delegate3.secret, newAddress, amount1, 1); + await guard.validate(transfers1); + + // simulate forged transaction + newWallet.applyTransactionToRecipient(transfers1[0]); + + expect(+delegateWallet.balance).toBe(+delegate3.balance - amount1 - fee); + expect(+newWallet.balance).toBe(amount1); + + // transfer almost everything from new wallet so that we don't have enough for any other transaction + const amount2 = 999 * 10 ** 8; + const transfers2 = generateTransfers("unitnet", newWalletPassphrase, delegate3.address, amount2, 1); + await guard.validate(transfers2); + + // simulate forged transaction + delegateWallet.applyTransactionToRecipient(transfers2[0]); + + expect(+newWallet.balance).toBe(amount1 - amount2 - fee); + + // now try to validate any other transaction - should not be accepted because in excess + const transferAmount = 0.5 * 10 ** 8; + const transferDynFee = 0.5 * 10 ** 8; + const allTransactions = [ + generateTransfers( + "unitnet", + newWalletPassphrase, + delegate3.address, + transferAmount, + 1, + false, + transferDynFee, + ), + generateSecondSignature("unitnet", newWalletPassphrase, 1), + generateVote("unitnet", newWalletPassphrase, delegate3.publicKey, 1), + generateDelegateRegistration("unitnet", newWalletPassphrase, 1), + ]; + + for (const transaction of allTransactions) { + await guard.validate(transaction); + + const errorExpected = [ + { + message: `["[PoolWalletManager] Can't apply transaction id:${transaction[0].id} from sender:${ + newWallet.address + }","Insufficient balance in the wallet"]`, + type: "ERR_APPLY", + }, + ]; + expect(guard.errors[transaction[0].id]).toEqual(errorExpected); + + expect(+delegateWallet.balance).toBe(+delegate3.balance - amount1 - fee + amount2); + expect(+newWallet.balance).toBe(amount1 - amount2 - fee); + } + }); + + it("should not validate 2 double spending transactions", async () => { + const amount = 245098000000000 - 5098000000000; // a bit less than the delegates' balance + const transactions = generateTransfers( + "unitnet", + delegates[0].secret, + delegates[1].address, + amount, + 2, + true, + ); + + const result = await guard.validate(transactions); + + expect(result.errors[transactions[1].id]).toEqual([ + { + message: `["[PoolWalletManager] Can't apply transaction id:${transactions[1].id} from sender:${ + delegates[0].address + }","Insufficient balance in the wallet"]`, + type: "ERR_APPLY", + }, + ]); + }); + + it.each([3, 5, 8])("should validate emptying wallet with %i transactions", async txNumber => { + // use txNumber so that we use a different delegate for each test case + const sender = delegates[txNumber]; + const senderWallet = transactionPool.walletManager.findByPublicKey(sender.publicKey); + const receivers = generateWallets("unitnet", 2); + const amountPlusFee = Math.floor(senderWallet.balance / txNumber); + const lastAmountPlusFee = senderWallet.balance - (txNumber - 1) * amountPlusFee; + const transferFee = 10000000; + + const transactions = generateTransfers( + "unitnet", + sender.secret, + receivers[0].address, + amountPlusFee - transferFee, + txNumber - 1, + true, + ); + const lastTransaction = generateTransfers( + "unitnet", + sender.secret, + receivers[1].address, + lastAmountPlusFee - transferFee, + 1, + true, + ); + // we change the receiver in lastTransaction to prevent having 2 exact + // same transactions with same id (if not, could be same as transactions[0]) + + const result = await guard.validate(transactions.concat(lastTransaction)); + + expect(result.errors).toEqual(null); + }); + + it.each([3, 5, 8])( + "should not validate emptying wallet with %i transactions when the last one is 1 arktoshi too much", + async txNumber => { + // use txNumber + 1 so that we don't use the same delegates as the above test + const sender = delegates[txNumber + 1]; + const receivers = generateWallets("unitnet", 2); + const amountPlusFee = Math.floor(sender.balance / txNumber); + const lastAmountPlusFee = sender.balance - (txNumber - 1) * amountPlusFee + 1; + const transferFee = 10000000; + + const transactions = generateTransfers( + "unitnet", + sender.secret, + receivers[0].address, + amountPlusFee - transferFee, + txNumber - 1, + true, + ); + const lastTransaction = generateTransfers( + "unitnet", + sender.secret, + receivers[1].address, + lastAmountPlusFee - transferFee, + 1, + true, + ); + // we change the receiver in lastTransaction to prevent having 2 + // exact same transactions with same id (if not, could be same as transactions[0]) + + const allTransactions = transactions.concat(lastTransaction); + + const result = await guard.validate(allTransactions); + + expect(Object.keys(result.errors).length).toBe(1); + expect(result.errors[lastTransaction[0].id]).toEqual([ + { + message: `["[PoolWalletManager] Can't apply transaction id:${ + lastTransaction[0].id + } from sender:${sender.address}","Insufficient balance in the wallet"]`, + type: "ERR_APPLY", + }, + ]); + }, + ); + + it("should compute transaction id and therefore validate transactions with wrong id", async () => { + const sender = delegates[21]; + const receivers = generateWallets("unitnet", 1); + + const transactions = generateTransfers("unitnet", sender.secret, receivers[0].address, 50, 1, true); + const transactionId = transactions[0].id; + transactions[0].id = "11111"; + + const result = await guard.validate(transactions); + expect(result.accept).toEqual([transactionId]); + expect(result.broadcast).toEqual([transactionId]); + expect(result.errors).toBeNull(); + }); + + it("should not validate when multiple wallets register the same username in the same transaction payload", async () => { + const delegateRegistrations = [ + generateDelegateRegistration("unitnet", wallets[14].passphrase, 1, false, "test_delegate")[0], + generateDelegateRegistration("unitnet", wallets[15].passphrase, 1, false, "test_delegate")[0], + ]; + + const result = await guard.validate(delegateRegistrations); + expect(result.invalid).toEqual(delegateRegistrations.map(transaction => transaction.id)); + + delegateRegistrations.forEach(tx => { + expect(guard.errors[tx.id]).toEqual([ + { + type: "ERR_CONFLICT", + message: `Multiple delegate registrations for "${ + tx.asset.delegate.username + }" in transaction payload`, + }, + ]); + }); + + const wallet1 = transactionPool.walletManager.findByPublicKey(wallets[14].keys.publicKey); + const wallet2 = transactionPool.walletManager.findByPublicKey(wallets[15].keys.publicKey); + + expect(wallet1.username).toBe(null); + expect(wallet2.username).toBe(null); + }); + + describe("Sign a transaction then change some fields shouldn't pass validation", () => { + const secondSignatureError = (id, address) => [ + id, + "ERR_APPLY", + `["[PoolWalletManager] Can't apply transaction id:${id} from sender:${address}","Failed to verify second-signature"]`, + ]; + + it("should not validate when changing fields after signing - transfer", async () => { + const sender = delegates[21]; + const notSender = delegates[22]; + + // the fields we are going to modify after signing + const modifiedFields = [ + { timestamp: 111111 }, + { amount: 111 }, + { fee: 1111111 }, + { recipientId: "ANqvJEMZcmUpcKBC8xiP1TntVkJeuZ3Lw3" }, + // we are also going to modify senderPublicKey but separately + ]; + + // generate transfers, "simple" and 2nd signed + const transfers = generateTransfers( + "unitnet", + sender.secret, + "AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5", + 50, + modifiedFields.length + 1, // + 1 because we will use it to modify senderPublicKey separately + true, + ); + const transfers2ndSigned = generateTransfers( + "unitnet", + { passphrase: wallets2ndSig[0].passphrase, secondPassphrase: wallets2ndSig[0].secondPassphrase }, + "AFzQCx5YpGg5vKMBg4xbuYbqkhvMkKfKe5", + 50, + modifiedFields.length + 1, // + 1 because we will use it to modify senderPublicKey separately + true, + ); + + // modify transaction fields and try to validate + const modifiedTransactions = [ + ...modifiedFields.map((objField, index) => Object.assign({}, transfers[index], objField)), + Object.assign({}, transfers[transfers.length - 1], { senderPublicKey: notSender.publicKey }), + ...modifiedFields.map((objField, index) => Object.assign({}, transfers2ndSigned[index], objField)), + Object.assign({}, transfers2ndSigned[transfers2ndSigned.length - 1], { + senderPublicKey: wallets2ndSig[1].keys.publicKey, + }), + ]; + const result = await guard.validate(modifiedTransactions); + + const expectedErrors = [ + ...transfers.map(transfer => [ + transfer.id, + "ERR_BAD_DATA", + "Transaction didn't pass the verification process.", + ]), + ...transfers2ndSigned + .slice(0, -1) + .map(transfer => secondSignatureError(transfer.id, wallets2ndSig[0].address)), + secondSignatureError( + transfers2ndSigned[transfers2ndSigned.length - 1].id, + wallets2ndSig[1].address, + ), + ]; + + expect( + Object.keys(result.errors).map(id => [id, result.errors[id][0].type, result.errors[id][0].message]), + ).toEqual(expectedErrors); + expect(result.invalid).toEqual(modifiedTransactions.map(transaction => transaction.id)); + expect(result.accept).toEqual([]); + expect(result.broadcast).toEqual([]); + }); + + it("should not validate when changing fields after signing - delegate registration", async () => { + // the fields we are going to modify after signing + const modifiedFieldsDelReg = [ + { timestamp: 111111 }, + { fee: 1111111 }, + // we are also going to modify senderPublicKey but separately + ]; + + // generate delegate registrations, "simple" and 2nd signed + const delegateRegs = generateDelegateRegistration( + "unitnet", + wallets.slice(0, modifiedFieldsDelReg.length + 1).map(w => w.passphrase), + 1, + true, + ); + const delegateRegs2ndSigned = generateDelegateRegistration( + "unitnet", + wallets2ndSig + .slice(0, modifiedFieldsDelReg.length + 1) + .map(w => ({ passphrase: w.passphrase, secondPassphrase: w.secondPassphrase })), + 1, + true, + ); + + // modify transaction fields and try to validate + const modifiedTransactions = [ + ...modifiedFieldsDelReg.map((objField, index) => Object.assign({}, delegateRegs[index], objField)), + Object.assign({}, delegateRegs[delegateRegs.length - 1], { + senderPublicKey: wallets[50].keys.publicKey, + }), + ...modifiedFieldsDelReg.map((objField, index) => + Object.assign({}, delegateRegs2ndSigned[index], objField), + ), + Object.assign({}, delegateRegs2ndSigned[delegateRegs2ndSigned.length - 1], { + senderPublicKey: wallets2ndSig[50].keys.publicKey, + }), + ]; + const result = await guard.validate(modifiedTransactions); + + const expectedErrors = [ + ...delegateRegs.map(tx => [ + tx.id, + "ERR_BAD_DATA", + "Transaction didn't pass the verification process.", + ]), + ...delegateRegs2ndSigned + .slice(0, -1) + .map((tx, index) => secondSignatureError(tx.id, wallets2ndSig[index].address)), + secondSignatureError( + delegateRegs2ndSigned[delegateRegs2ndSigned.length - 1].id, + wallets2ndSig[50].address, + ), + ]; + + expect( + Object.keys(result.errors).map(id => [id, result.errors[id][0].type, result.errors[id][0].message]), + ).toEqual(expectedErrors); + expect(result.invalid).toEqual(modifiedTransactions.map(transaction => transaction.id)); + expect(result.accept).toEqual([]); + expect(result.broadcast).toEqual([]); + }); + + it("should not validate when changing fields after signing - vote", async () => { + // the fields we are going to modify after signing + const modifiedFieldsVote = [ + { timestamp: 111111 }, + { fee: 1111111 }, + // we are also going to modify senderPublicKey but separately + ]; + + // generate votes, "simple" and 2nd signed + const votes = generateVote( + "unitnet", + wallets.slice(0, modifiedFieldsVote.length + 1).map(w => w.passphrase), + delegates[21].publicKey, + 1, + true, + ); + const votes2ndSigned = generateVote( + "unitnet", + wallets2ndSig + .slice(0, modifiedFieldsVote.length + 1) + .map(w => ({ passphrase: w.passphrase, secondPassphrase: w.secondPassphrase })), + delegates[21].publicKey, + 1, + true, + ); + + // modify transaction fields and try to validate + const modifiedTransactions = [ + ...modifiedFieldsVote.map((objField, index) => Object.assign({}, votes[index], objField)), + Object.assign({}, votes[votes.length - 1], { senderPublicKey: wallets[50].keys.publicKey }), + ...modifiedFieldsVote.map((objField, index) => Object.assign({}, votes2ndSigned[index], objField)), + Object.assign({}, votes2ndSigned[votes2ndSigned.length - 1], { + senderPublicKey: wallets2ndSig[50].keys.publicKey, + }), + ]; + const result = await guard.validate(modifiedTransactions); + + const expectedErrors = [ + ...votes.map(tx => [tx.id, "ERR_BAD_DATA", "Transaction didn't pass the verification process."]), + ...votes2ndSigned + .slice(0, -1) + .map((tx, index) => secondSignatureError(tx.id, wallets2ndSig[index].address)), + secondSignatureError(votes2ndSigned[votes2ndSigned.length - 1].id, wallets2ndSig[50].address), + ]; + + expect( + Object.keys(result.errors).map(id => [id, result.errors[id][0].type, result.errors[id][0].message]), + ).toEqual(expectedErrors); + expect(result.invalid).toEqual(modifiedTransactions.map(transaction => transaction.id)); + expect(result.accept).toEqual([]); + expect(result.broadcast).toEqual([]); + }); + + it("should not validate when changing fields after signing - 2nd signature registration", async () => { + // the fields we are going to modify after signing + const modifiedFields2ndSig = [ + { timestamp: 111111 }, + { fee: 1111111 }, + { senderPublicKey: wallets[50].keys.publicKey }, + ]; + + const secondSigs = generateSecondSignature( + "unitnet", + wallets.slice(0, modifiedFields2ndSig.length).map(w => w.passphrase), + 1, + true, + ); + + const modifiedTransactions = modifiedFields2ndSig.map((objField, index) => + Object.assign({}, secondSigs[index], objField), + ); + const result = await guard.validate(modifiedTransactions); + + expect( + Object.keys(result.errors).map(id => [id, result.errors[id][0].type, result.errors[id][0].message]), + ).toEqual( + secondSigs.map(tx => [tx.id, "ERR_BAD_DATA", "Transaction didn't pass the verification process."]), + ); + expect(result.invalid).toEqual(modifiedTransactions.map(transaction => transaction.id)); + expect(result.accept).toEqual([]); + expect(result.broadcast).toEqual([]); + }); + }); + + describe("Transaction replay shouldn't pass validation", () => { + afterEach(async () => blockchain.removeBlocks(blockchain.getLastHeight() - 1)); // resets to height 1 + + const addBlock = async transactions => { + // makes blockchain accept a new block with the transactions specified + const block = { + id: "17882607875259085966", + version: 0, + timestamp: 46583330, + height: 2, + reward: 0, + previousBlock: genesisBlock.id, + numberOfTransactions: 1, + transactions, + totalAmount: transactions.reduce((acc, curr) => acc + curr.amount), + totalFee: transactions.reduce((acc, curr) => acc + curr.fee), + payloadLength: 0, + payloadHash: genesisBlock.payloadHash, + generatorPublicKey: delegates[0].publicKey, + blockSignature: + "3045022100e7385c6ea42bd950f7f6ab8c8619cf2f66a41d8f8f185b0bc99af032cb25f30d02200b6210176a6cedfdcbe483167fd91c21d740e0e4011d24d679c601fdd46b0de9", + createdAt: "2019-07-11T16:48:50.550Z", + }; + const blockVerified = new Block(block); + blockVerified.verification.verified = true; + + await blockchain.processBlock(blockVerified, () => null); + }; + const forgedErrorMessage = id => ({ + [id]: [ + { + message: "Already forged.", + type: "ERR_FORGED", + }, + ], + }); + + it("should not validate an already forged transaction", async () => { + const transfers = generateTransfers("unitnet", wallets[0].passphrase, wallets[1].address, 11, 1, true); + await addBlock(transfers); + + const result = await guard.validate(transfers); + + expect(result.errors).toEqual(forgedErrorMessage(transfers[0].id)); + }); + + it("should not validate an already forged transaction - trying to tweak tx id", async () => { + const transfers = generateTransfers("unitnet", wallets[0].passphrase, wallets[1].address, 11, 1, true); + await addBlock(transfers); + + const realTransferId = transfers[0].id; + transfers[0].id = "123456"; + + const result = await guard.validate(transfers); + + expect(result.errors).toEqual(forgedErrorMessage(realTransferId)); + }); + }); + }); + + describe("__cacheTransactions", () => { + it("should add transactions to cache", () => { + const transactions = generateTransfers("unitnet", wallets[10].passphrase, wallets[11].address, 35, 3); + expect(guard.__cacheTransactions(transactions)).toEqual(transactions); + }); + + it("should not add a transaction already in cache and add it as an error", () => { + const transactions = generateTransfers("unitnet", wallets[11].passphrase, wallets[12].address, 35, 3); + expect(guard.__cacheTransactions(transactions)).toEqual(transactions); + expect(guard.__cacheTransactions([transactions[0]])).toEqual([]); + expect(guard.errors).toEqual({ + [transactions[0].id]: [ + { + message: "Already in cache.", + type: "ERR_DUPLICATE", + }, + ], + }); + }); + }); + + describe("getBroadcastTransactions", () => { + it("should return broadcast transaction", async () => { + const transactions = generateTransfers("unitnet", wallets[10].passphrase, wallets[11].address, 25, 3); + + await guard.validate(transactions); + expect(guard.getBroadcastTransactions()).toEqual(transactions); + }); + }); + + describe("__filterAndTransformTransactions", () => { + it("should reject duplicate transactions", () => { + const transactionExists = guard.pool.transactionExists; + guard.pool.transactionExists = jest.fn(() => true); + + const tx = { id: "1" }; + guard.__filterAndTransformTransactions([tx]); + + expect(guard.errors[tx.id]).toEqual([ + { + message: `Duplicate transaction ${tx.id}`, + type: "ERR_DUPLICATE", + }, + ]); + + guard.pool.transactionExists = transactionExists; + }); + + it("should reject blocked senders", () => { + const transactionExists = guard.pool.transactionExists; + guard.pool.transactionExists = jest.fn(() => false); + const isSenderBlocked = guard.pool.isSenderBlocked; + guard.pool.isSenderBlocked = jest.fn(() => true); + + const tx = { id: "1", senderPublicKey: "affe" }; + guard.__filterAndTransformTransactions([tx]); + + expect(guard.errors[tx.id]).toEqual([ + { + message: `Transaction ${tx.id} rejected. Sender ${tx.senderPublicKey} is blocked.`, + type: "ERR_SENDER_BLOCKED", + }, + ]); + + guard.pool.isSenderBlocked = isSenderBlocked; + guard.pool.transactionExists = transactionExists; + }); + + it("should reject transactions from the future", () => { + const now = 47157042; // seconds since genesis block + const transactionExists = guard.pool.transactionExists; + guard.pool.transactionExists = jest.fn(() => false); + const getTime = slots.getTime; + slots.getTime = jest.fn(() => now); + + const secondsInFuture = 3601; + const tx = { + id: "1", + senderPublicKey: "affe", + timestamp: slots.getTime() + secondsInFuture, + }; + guard.__filterAndTransformTransactions([tx]); + + expect(guard.errors[tx.id]).toEqual([ + { + message: `Transaction ${tx.id} is ${secondsInFuture} seconds in the future`, + type: "ERR_FROM_FUTURE", + }, + ]); + + slots.getTime = getTime; + guard.pool.transactionExists = transactionExists; + }); + + it("should accept transaction with correct network byte", () => { + const transactionExists = guard.pool.transactionExists; + guard.pool.transactionExists = jest.fn(() => false); + + const canApply = guard.pool.walletManager.canApply; + guard.pool.walletManager.canApply = jest.fn(() => true); + + const tx = { + id: "1", + network: 23, + senderPublicKey: "023ee98f453661a1cb765fd60df95b4efb1e110660ffb88ae31c2368a70f1f7359", + }; + guard.__filterAndTransformTransactions([tx]); + + expect(guard.errors[tx.id]).not.toEqual([ + { + message: `Transaction network '${tx.network}' does not match '${configManager.get("pubKeyHash")}'`, + type: "ERR_WRONG_NETWORK", + }, + ]); + + guard.pool.transactionExists = transactionExists; + guard.pool.walletManager.canApply = canApply; + }); + + it("should accept transaction with missing network byte", () => { + const transactionExists = guard.pool.transactionExists; + guard.pool.transactionExists = jest.fn(() => false); + + const canApply = guard.pool.walletManager.canApply; + guard.pool.walletManager.canApply = jest.fn(() => true); + + const tx = { + id: "1", + senderPublicKey: "023ee98f453661a1cb765fd60df95b4efb1e110660ffb88ae31c2368a70f1f7359", + }; + guard.__filterAndTransformTransactions([tx]); + + expect(guard.errors[tx.id].type).not.toEqual("ERR_WRONG_NETWORK"); + + guard.pool.transactionExists = transactionExists; + guard.pool.walletManager.canApply = canApply; + }); + + it("should not accept transaction with wrong network byte", () => { + const transactionExists = guard.pool.transactionExists; + guard.pool.transactionExists = jest.fn(() => false); + + const canApply = guard.pool.walletManager.canApply; + guard.pool.walletManager.canApply = jest.fn(() => true); + + const tx = { + id: "1", + network: 2, + senderPublicKey: "023ee98f453661a1cb765fd60df95b4efb1e110660ffb88ae31c2368a70f1f7359", + }; + guard.__filterAndTransformTransactions([tx]); + + expect(guard.errors[tx.id]).toEqual([ + { + message: `Transaction network '${tx.network}' does not match '${configManager.get("pubKeyHash")}'`, + type: "ERR_WRONG_NETWORK", + }, + ]); + + guard.pool.transactionExists = transactionExists; + guard.pool.walletManager.canApply = canApply; + }); + + it("should not accept transaction if pool hasExceededMaxTransactions and add it to excess", () => { + const transactions = generateTransfers("unitnet", wallets[10].passphrase, wallets[11].address, 35, 1); + + jest.spyOn(guard.pool, "hasExceededMaxTransactions").mockImplementationOnce(tx => true); + + guard.__filterAndTransformTransactions(transactions); + + expect(guard.excess).toEqual([transactions[0].id]); + expect(guard.accept).toEqual(new Map()); + expect(guard.broadcast).toEqual(new Map()); + }); + + it("should push a ERR_UNKNOWN error if something threw in validated transaction block", () => { + const transactions = generateTransfers("unitnet", wallets[10].passphrase, wallets[11].address, 35, 1); + + // use guard.accept.set() call to introduce a throw + jest.spyOn(guard.accept, "set").mockImplementationOnce(() => { + throw new Error("hey"); + }); + + guard.__filterAndTransformTransactions(transactions); + + expect(guard.accept).toEqual(new Map()); + expect(guard.broadcast).toEqual(new Map()); + expect(guard.errors[transactions[0].id]).toEqual([ + { + message: `hey`, + type: "ERR_UNKNOWN", + }, + ]); + }); + }); + + describe("__validateTransaction", () => { + it("should not validate when recipient is not on the same network", async () => { + const transactions = generateTransfers( + "unitnet", + wallets[10].passphrase, + "DEJHR83JFmGpXYkJiaqn7wPGztwjheLAmY", + 35, + 1, + ); + + expect(guard.__validateTransaction(transactions[0])).toBeFalse(); + expect(guard.errors).toEqual({ + [transactions[0].id]: [ + { + type: "ERR_INVALID_RECIPIENT", + message: `Recipient ${ + transactions[0].recipientId + } is not on the same network: ${configManager.get("pubKeyHash")}`, + }, + ], + }); + }); + + it("should not validate a delegate registration if an existing registration for the same username from a different wallet exists in the pool", async () => { + const delegateRegistrations = [ + generateDelegateRegistration("unitnet", wallets[16].passphrase, 1, false, "test_delegate")[0], + generateDelegateRegistration("unitnet", wallets[17].passphrase, 1, false, "test_delegate")[0], + ]; + + expect(guard.__validateTransaction(delegateRegistrations[0])).toBeTrue(); + guard.accept.set(delegateRegistrations[0].id, delegateRegistrations[0]); + guard.__addTransactionsToPool(); + expect(guard.errors).toEqual({}); + expect(guard.__validateTransaction(delegateRegistrations[1])).toBeFalse(); + expect(guard.errors[delegateRegistrations[1].id]).toEqual([ + { + type: "ERR_PENDING", + message: `Delegate registration for "${ + delegateRegistrations[1].asset.delegate.username + }" already in the pool`, + }, + ]); + + const wallet1 = transactionPool.walletManager.findByPublicKey(wallets[16].keys.publicKey); + const wallet2 = transactionPool.walletManager.findByPublicKey(wallets[17].keys.publicKey); + + expect(wallet1.username).toBe("test_delegate"); + expect(wallet2.username).toBe(null); + }); + + it("should not validate when sender has same type transactions in the pool (only for 2nd sig, delegate registration, vote)", async () => { + jest.spyOn(guard.pool.walletManager, "canApply").mockImplementation(() => true); + const votes = [ + generateVote("unitnet", wallets[10].passphrase, delegates[0].publicKey, 1)[0], + generateVote("unitnet", wallets[10].passphrase, delegates[1].publicKey, 1)[0], + ]; + const delegateRegs = generateDelegateRegistration("unitnet", wallets[11].passphrase, 2); + const signatures = generateSecondSignature("unitnet", wallets[12].passphrase, 2); + + for (const transactions of [votes, delegateRegs, signatures]) { + await guard.validate([transactions[0]]); + expect(guard.__validateTransaction(transactions[1])).toBeFalse(); + expect(guard.errors[transactions[1].id]).toEqual([ + { + type: "ERR_PENDING", + message: + `Sender ${transactions[1].senderPublicKey} already has a transaction of type ` + + `'${constants.TransactionTypes[transactions[1].type]}' in the pool`, + }, + ]); + } + + jest.restoreAllMocks(); + }); + + it("should not validate unsupported transaction types", async () => { + jest.spyOn(guard.pool.walletManager, "canApply").mockImplementation(() => true); + + // use a random transaction as a base - then play with type + const baseTransaction = generateDelegateRegistration("unitnet", wallets[11].passphrase, 1)[0]; + + for (const transactionType of [ + constants.TransactionTypes.MultiSignature, + constants.TransactionTypes.Ipfs, + constants.TransactionTypes.TimelockTransfer, + constants.TransactionTypes.MultiPayment, + constants.TransactionTypes.DelegateResignation, + 99, + ]) { + baseTransaction.type = transactionType; + baseTransaction.id = transactionType; + + expect(guard.__validateTransaction(baseTransaction)).toBeFalse(); + expect(guard.errors[baseTransaction.id]).toEqual([ + { + type: "ERR_UNSUPPORTED", + message: `Invalidating transaction of unsupported type '${ + constants.TransactionTypes[transactionType] + }'`, + }, + ]); + } + + jest.restoreAllMocks(); + }); + }); + + describe("__removeForgedTransactions", () => { + it("should remove forged transactions", async () => { + const database = container.resolvePlugin("database"); + const getForgedTransactionsIds = database.getForgedTransactionsIds; + + const transfers = generateTransfers("unitnet", delegates[0].secret, delegates[0].senderPublicKey, 1, 4); + + transfers.forEach(tx => { + guard.accept.set(tx.id, tx); + guard.broadcast.set(tx.id, tx); + }); + + const forgedTx = transfers[2]; + database.getForgedTransactionsIds = jest.fn(() => [forgedTx.id]); + + await guard.__removeForgedTransactions(); + + expect(guard.accept.size).toBe(3); + expect(guard.broadcast.size).toBe(3); + + expect(guard.errors[forgedTx.id]).toHaveLength(1); + expect(guard.errors[forgedTx.id][0].type).toEqual("ERR_FORGED"); + + database.getForgedTransactionsIds = getForgedTransactionsIds; + }); + }); + + describe("__addTransactionsToPool", () => { + it("should add transactions to the pool", () => { + const transfers = generateTransfers("unitnet", delegates[0].secret, delegates[0].senderPublicKey, 1, 4); + + transfers.forEach(tx => { + guard.accept.set(tx.id, tx); + guard.broadcast.set(tx.id, tx); + }); + + expect(guard.errors).toEqual({}); + + guard.__addTransactionsToPool(); + + expect(guard.errors).toEqual({}); + expect(guard.accept.size).toBe(4); + expect(guard.broadcast.size).toBe(4); + }); + + it("should raise ERR_ALREADY_IN_POOL when adding existing transactions", () => { + const transfers = generateTransfers("unitnet", delegates[0].secret, delegates[0].senderPublicKey, 1, 4); + + transfers.forEach(tx => { + guard.accept.set(tx.id, tx); + guard.broadcast.set(tx.id, tx); + }); + + expect(guard.errors).toEqual({}); + + guard.__addTransactionsToPool(); + + expect(guard.errors).toEqual({}); + expect(guard.accept.size).toBe(4); + expect(guard.broadcast.size).toBe(4); + + // Adding again invokes ERR_ALREADY_IN_POOL + guard.__addTransactionsToPool(); + + expect(guard.accept.size).toBe(0); + expect(guard.broadcast.size).toBe(0); + + for (const transfer of transfers) { + expect(guard.errors[transfer.id]).toHaveLength(1); + expect(guard.errors[transfer.id][0].type).toEqual("ERR_ALREADY_IN_POOL"); + } + }); + + it("should raise ERR_POOL_FULL when attempting to add transactions to a full pool", () => { + const poolSize = transactionPool.options.maxTransactionsInPool; + transactionPool.options.maxTransactionsInPool = 3; + + const transfers = generateTransfers("unitnet", delegates[0].secret, delegates[0].senderPublicKey, 1, 4); + + transfers.forEach(tx => { + guard.accept.set(tx.id, tx); + guard.broadcast.set(tx.id, tx); + }); + + guard.__addTransactionsToPool(); + + expect(guard.accept.size).toBe(3); + expect(guard.broadcast.size).toBe(4); + + expect(guard.errors[transfers[3].id]).toHaveLength(1); + expect(guard.errors[transfers[3].id][0].type).toEqual("ERR_POOL_FULL"); + + transactionPool.options.maxTransactionsInPool = poolSize; + }); + }); + + describe("__pushError", () => { + it("should have error for transaction", () => { + expect(guard.errors).toBeEmpty(); + + guard.__pushError({ id: 1 }, "ERR_INVALID", "Invalid."); + + expect(guard.errors).toBeObject(); + expect(guard.errors["1"]).toBeArray(); + expect(guard.errors["1"]).toHaveLength(1); + expect(guard.errors["1"]).toEqual([{ message: "Invalid.", type: "ERR_INVALID" }]); + + expect(guard.invalid.size).toEqual(1); + expect(guard.invalid.entries().next().value[1]).toEqual({ id: 1 }); + }); + + it("should have multiple errors for transaction", () => { + expect(guard.errors).toBeEmpty(); + + guard.__pushError({ id: 1 }, "ERR_INVALID", "Invalid 1."); + guard.__pushError({ id: 1 }, "ERR_INVALID", "Invalid 2."); + + expect(guard.errors).toBeObject(); + expect(guard.errors["1"]).toBeArray(); + expect(guard.errors["1"]).toHaveLength(2); + expect(guard.errors["1"]).toEqual([ + { message: "Invalid 1.", type: "ERR_INVALID" }, + { message: "Invalid 2.", type: "ERR_INVALID" }, + ]); + + expect(guard.invalid.size).toEqual(1); + expect(guard.invalid.entries().next().value[1]).toEqual({ id: 1 }); + }); + }); +}); diff --git a/packages/core-transaction-pool/__tests__/interface.test.js b/packages/core-transaction-pool/__tests__/interface.test.js deleted file mode 100644 index d04fce3498..0000000000 --- a/packages/core-transaction-pool/__tests__/interface.test.js +++ /dev/null @@ -1,219 +0,0 @@ -const dayjs = require('dayjs-ext') -const app = require('./__support__/setup') - -let poolInterface - -beforeAll(async () => { - const container = await app.setUp() - await container.resolvePlugin('blockchain').start() - - poolInterface = new (require('../lib/interface'))({ enabled: false }) -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('Transaction Pool Interface', () => { - it('should be an object', () => { - expect(poolInterface).toBeObject() - }) - - describe('driver', () => { - it('should be a function', () => { - expect(poolInterface.driver).toBeFunction() - }) - }) - - describe('getPoolSize', () => { - it('should be a function', () => { - expect(poolInterface.getPoolSize).toBeFunction() - }) - - it('should throw an exception', () => { - expect(poolInterface.getPoolSize).toThrow( - 'Method [getPoolSize] not implemented!', - ) - }) - }) - - describe('getSenderSize', () => { - it('should be a function', () => { - expect(poolInterface.getSenderSize).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.getSenderSize).toThrow( - 'Method [getSenderSize] not implemented!', - ) - }) - }) - - describe('addTransaction', () => { - it('should be a function', () => { - expect(poolInterface.addTransaction).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.addTransaction).toThrow( - 'Method [addTransaction] not implemented!', - ) - }) - }) - - describe('removeTransaction', () => { - it('should be a function', () => { - expect(poolInterface.removeTransaction).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.removeTransaction).toThrow( - 'Method [removeTransaction] not implemented!', - ) - }) - }) - - describe('getTransaction', () => { - it('should be a function', () => { - expect(poolInterface.getTransaction).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.getTransaction).toThrow( - 'Method [getTransaction] not implemented!', - ) - }) - }) - - describe('getTransactions', () => { - it('should be a function', () => { - expect(poolInterface.getTransactions).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.getTransactions).toThrow( - 'Method [getTransactions] not implemented!', - ) - }) - }) - - describe('getTransactionsForForging', () => { - it('should be a function', () => { - expect(poolInterface.getTransactionsForForging).toBeFunction() - }) - - it('should throw an exception', () => { - expect(poolInterface.getTransactionsForForging).toThrow( - 'Method [getTransactionsForForging] not implemented!', - ) - }) - }) - - describe('getTransactionIdsForForging', () => { - it('should be a function', () => { - expect(poolInterface.getTransactionIdsForForging).toBeFunction() - }) - - it('should throw an exception', () => { - expect(poolInterface.getTransactionIdsForForging).toThrow( - 'Method [getTransactionIdsForForging] not implemented!', - ) - }) - }) - - describe('hasExceededMaxTransactions', () => { - it('should be a function', () => { - expect(poolInterface.hasExceededMaxTransactions).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.hasExceededMaxTransactions).toThrow( - 'Method [hasExceededMaxTransactions] not implemented!', - ) - }) - }) - - describe('senderHasTransactionsOfType', () => { - it('should be a function', () => { - expect(poolInterface.senderHasTransactionsOfType).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.senderHasTransactionsOfType).toThrow( - 'Method [senderHasTransactionsOfType] not implemented!', - ) - }) - }) - - describe('transactionExists', () => { - it('should be a function', () => { - expect(poolInterface.transactionExists).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.transactionExists).toThrow( - 'Method [transactionExists] not implemented!', - ) - }) - }) - - describe('removeTransactionsForSender', () => { - it('should be a function', () => { - expect(poolInterface.removeTransactionsForSender).toBeFunction() - }) - - it('should throw an exception', async () => { - expect(poolInterface.removeTransactionsForSender).toThrow( - 'Method [removeTransactionsForSender] not implemented!', - ) - }) - }) - - describe('isSenderBlocked', () => { - it('should be a function', () => { - expect(poolInterface.isSenderBlocked).toBeFunction() - }) - - it('should return true', async () => { - poolInterface.blockSender('keykeykey') - expect(poolInterface.isSenderBlocked('keykeykey')).toBeTrue() - }) - - it('should return false', async () => { - expect(poolInterface.isSenderBlocked('keykeykey2')).toBeFalse() - }) - }) - - describe('blockSender', () => { - it('should be a function', () => { - expect(poolInterface.blockSender).toBeFunction() - }) - - it('should block sender for specified time', async () => { - const time = dayjs() - const blockedTime = poolInterface.blockSender('keykeykey') - const duration = blockedTime.diff(time) / 1000 / 60 / 60 - - expect(poolInterface.isSenderBlocked('keykeykey')).toBeTrue() - expect(parseInt(duration)).toEqual(1) - }) - }) - - describe('acceptChainedBlock', () => { - it('should be a function', () => { - expect(poolInterface.acceptChainedBlock).toBeFunction() - }) - }) - - describe('buildWallets', () => { - it('should be a function', () => { - expect(poolInterface.buildWallets).toBeFunction() - }) - }) - - describe('purgeByPublicKey', () => { - it('should be a function', () => { - expect(poolInterface.purgeByPublicKey).toBeFunction() - }) - }) -}) diff --git a/packages/core-transaction-pool/__tests__/manager.test.js b/packages/core-transaction-pool/__tests__/manager.test.js deleted file mode 100644 index f5c08e64aa..0000000000 --- a/packages/core-transaction-pool/__tests__/manager.test.js +++ /dev/null @@ -1,39 +0,0 @@ -const transactionPoolManager = require('../lib/manager') - -class FakeDriver { - make() { - return this - } -} - -describe('Transaction Pool Manager', () => { - it('should be an object', () => { - expect(transactionPoolManager).toBeObject() - }) - - describe('connection', () => { - it('should be a function', () => { - expect(transactionPoolManager.connection).toBeFunction() - }) - - it('should return the drive-connection', async () => { - await transactionPoolManager.makeConnection(new FakeDriver()) - - expect(transactionPoolManager.connection()).toBeInstanceOf(FakeDriver) - }) - - it('should return the drive-connection for a different name', async () => { - await transactionPoolManager.makeConnection(new FakeDriver(), 'testing') - - expect(transactionPoolManager.connection('testing')).toBeInstanceOf( - FakeDriver, - ) - }) - }) - - describe('makeConnection', () => { - it('should be a function', () => { - expect(transactionPoolManager.makeConnection).toBeFunction() - }) - }) -}) diff --git a/packages/core-transaction-pool/__tests__/manager.test.ts b/packages/core-transaction-pool/__tests__/manager.test.ts new file mode 100644 index 0000000000..2ed028da2c --- /dev/null +++ b/packages/core-transaction-pool/__tests__/manager.test.ts @@ -0,0 +1,24 @@ +import "jest-extended"; +import { transactionPoolManager } from "../src/manager"; + +class FakeDriver { + public make() { + return this; + } +} + +describe("Transaction Pool Manager", () => { + describe("connection", () => { + it("should return the drive-connection", async () => { + await transactionPoolManager.makeConnection(new FakeDriver()); + + expect(transactionPoolManager.connection()).toBeInstanceOf(FakeDriver); + }); + + it("should return the drive-connection for a different name", async () => { + await transactionPoolManager.makeConnection(new FakeDriver(), "testing"); + + expect(transactionPoolManager.connection("testing")).toBeInstanceOf(FakeDriver); + }); + }); +}); diff --git a/packages/core-transaction-pool/__tests__/mem-pool-transaction.test.ts b/packages/core-transaction-pool/__tests__/mem-pool-transaction.test.ts new file mode 100644 index 0000000000..a80d129c8d --- /dev/null +++ b/packages/core-transaction-pool/__tests__/mem-pool-transaction.test.ts @@ -0,0 +1,28 @@ +import { constants } from "@arkecosystem/crypto"; +import { MemPoolTransaction } from "../src/mem-pool-transaction"; +import { transactions } from "./__fixtures__/transactions"; + +describe("MemPoolTransaction", () => { + describe("expireAt", () => { + it("should return transaction expiration when it is set", () => { + const transaction = transactions.dummy1; + transaction.expiration = 1123; + const memPoolTransaction = new MemPoolTransaction(transaction); + expect(memPoolTransaction.expireAt(1)).toBe(1123); + }); + + it("should return timestamp + maxTransactionAge when expiration is not set", () => { + const transaction = transactions.dummy2; + transaction.timestamp = 344; + const memPoolTransaction = new MemPoolTransaction(transaction); + expect(memPoolTransaction.expireAt(131)).toBe(transaction.timestamp + 131); + }); + + it("should return null for timelock transfer with no expiration set", () => { + const transaction = transactions.dummy3; + transaction.type = constants.TransactionTypes.TimelockTransfer; + const memPoolTransaction = new MemPoolTransaction(transaction); + expect(memPoolTransaction.expireAt(1)).toBe(null); + }); + }); +}); diff --git a/packages/core-transaction-pool/__tests__/pool-wallet-manager.test.js b/packages/core-transaction-pool/__tests__/pool-wallet-manager.test.js deleted file mode 100644 index 85f9271df7..0000000000 --- a/packages/core-transaction-pool/__tests__/pool-wallet-manager.test.js +++ /dev/null @@ -1,218 +0,0 @@ -const { crypto } = require('@arkecosystem/crypto') -const { Block } = require('@arkecosystem/crypto').models -const bip39 = require('bip39') -const delegates = require('@arkecosystem/core-test-utils/fixtures/testnet/delegates') -const generateTransfer = require('@arkecosystem/core-test-utils/lib/generators/transactions/transfer') -const generateWallets = require('@arkecosystem/core-test-utils/lib/generators/wallets') -const blocks2to100 = require('@arkecosystem/core-test-utils/fixtures/testnet/blocks.2-100') -const app = require('./__support__/setup') - -const arktoshi = 10 ** 8 -let container -let poolWalletManager -let blockchain - -beforeAll(async () => { - container = await app.setUp() - poolWalletManager = new (require('../lib/pool-wallet-manager'))() - blockchain = container.resolvePlugin('blockchain') -}) - -afterAll(async () => { - await app.tearDown() -}) - -describe('applyPoolTransactionToSender', () => { - describe('update the balance', () => { - it('should only update the balance of the sender', async () => { - const delegate0 = delegates[0] - const { publicKey } = crypto.getKeys(bip39.generateMnemonic()) - const newAddress = crypto.getAddress(publicKey) - - const delegateWallet = poolWalletManager.findByAddress(delegate0.address) - const newWallet = poolWalletManager.findByAddress(newAddress) - - expect(+delegateWallet.balance).toBe(+delegate0.balance) - expect(+newWallet.balance).toBe(0) - - const amount1 = 123 * 10 ** 8 - const transfer = generateTransfer( - 'testnet', - delegate0.secret, - newAddress, - amount1, - 1, - )[0] - - delegateWallet.applyTransactionToSender(transfer) - - expect(+delegateWallet.balance).toBe( - +delegate0.balance - amount1 - 0.1 * 10 ** 8, - ) - expect(newWallet.balance.isZero()).toBeTrue() - }) - - it('should only update the balance of the sender with dyn fees', async () => { - const delegate0 = delegates[1] - const { publicKey } = crypto.getKeys(bip39.generateMnemonic()) - const newAddress = crypto.getAddress(publicKey) - - const delegateWallet = poolWalletManager.findByAddress(delegate0.address) - const newWallet = poolWalletManager.findByAddress(newAddress) - - expect(+delegateWallet.balance).toBe(+delegate0.balance) - expect(+newWallet.balance).toBe(0) - - const amount1 = 123 * 10 ** 8 - const fee = 10 - const transfer = generateTransfer( - 'testnet', - delegate0.secret, - newAddress, - amount1, - 1, - false, - fee, - )[0] - - delegateWallet.applyTransactionToSender(transfer) - - expect(+delegateWallet.balance).toBe(+delegate0.balance - amount1 - fee) - expect(newWallet.balance.isZero()).toBeTrue() - }) - - it('should not apply chained transfers', async () => { - const delegate = delegates[7] - const delegateWallet = poolWalletManager.findByPublicKey( - delegate.publicKey, - ) - - const wallets = generateWallets('testnet', 4) - const poolWallets = wallets.map(w => - poolWalletManager.findByAddress(w.address), - ) - - expect(+delegateWallet.balance).toBe(+delegate.balance) - poolWallets.forEach(w => { - expect(+w.balance).toBe(0) - }) - - const transfers = [ - { - // transfer from delegate to wallet 0 - from: delegate, - to: wallets[0], - amount: 100 * arktoshi, - }, - { - // transfer from wallet 0 to delegatej - from: wallets[0], - to: delegate, - amount: 55 * arktoshi, - }, - ] - - transfers.forEach(t => { - const transfer = generateTransfer( - 'testnet', - t.from.passphrase, - t.to.address, - t.amount, - 1, - )[0] - - // This is normally refused because it's a cold wallet, but since we want - // to test if chained transfers are refused, pretent it is not a cold wallet. - container - .resolvePlugin('database') - .walletManager.findByPublicKey(transfer.senderPublicKey) - - const errors = [] - if (poolWalletManager.canApply(transfer, errors)) { - poolWalletManager - .findByPublicKey(transfer.senderPublicKey) - .applyTransactionToSender(transfer) - - expect(t.from).toBe(delegate) - } else { - expect(t.from).toBe(wallets[0]) - expect(JSON.stringify(errors)).toEqual( - `["[PoolWalletManager] Can't apply transaction id:${ - transfer.id - } from sender:${ - t.from.address - }","Insufficient balance in the wallet"]`, - ) - } - - container - .resolvePlugin('database') - .walletManager.forgetByPublicKey(transfer.publicKey) - }) - - expect(+delegateWallet.balance).toBe( - delegate.balance - (100 + 0.1) * arktoshi, - ) - expect(poolWallets[0].balance.isZero()).toBeTrue() - }) - }) -}) - -describe('Apply transactions and block rewards to wallets on new block', () => { - const __resetToHeight1 = async () => - blockchain.removeBlocks(blockchain.getLastHeight() - 1) - - beforeEach(__resetToHeight1) - afterEach(__resetToHeight1) - - it.each([2 * arktoshi, 0])( - 'should apply forged block reward %i to delegate wallet', - async reward => { - const forgingDelegate = delegates[reward ? 2 : 3] // use different delegate to have clean initial balance - const generatorPublicKey = forgingDelegate.publicKey - - const wallet = generateWallets('testnet', 1)[0] - const transferAmount = 1234 - const transferDelegate = delegates[4] - const transfer = generateTransfer( - 'testnet', - transferDelegate.passphrase, - wallet.address, - transferAmount, - 1, - true, - )[0] - - const totalFee = 0.1 * arktoshi - const blockWithReward = Object.assign({}, blocks2to100[0], { - reward, - generatorPublicKey, - transactions: [transfer], - numberOfTransactions: 1, - totalFee, - }) - const blockWithRewardVerified = new Block(blockWithReward) - blockWithRewardVerified.verification.verified = true - - await blockchain.processBlock(blockWithRewardVerified, () => null) - - const delegateWallet = poolWalletManager.findByPublicKey( - generatorPublicKey, - ) - - const poolWallet = poolWalletManager.findByAddress(wallet.address) - expect(+poolWallet.balance).toBe(transferAmount) - - const transferDelegateWallet = poolWalletManager.findByAddress( - transferDelegate.address, - ) - expect(+transferDelegateWallet.balance).toBe( - +transferDelegate.balance - transferAmount - totalFee, - ) - - expect(+delegateWallet.balance).toBe( - +forgingDelegate.balance + reward + totalFee, - ) // balance increased by reward + fee - }, - ) -}) diff --git a/packages/core-transaction-pool/__tests__/pool-wallet-manager.test.ts b/packages/core-transaction-pool/__tests__/pool-wallet-manager.test.ts new file mode 100644 index 0000000000..1c67dd8526 --- /dev/null +++ b/packages/core-transaction-pool/__tests__/pool-wallet-manager.test.ts @@ -0,0 +1,210 @@ +import { Blockchain, Container, Database } from "@arkecosystem/core-interfaces"; +import { generators } from "@arkecosystem/core-test-utils"; +import { delegates, genesisBlock, wallets } from "@arkecosystem/core-test-utils/src/fixtures/unitnet"; +import { crypto, models } from "@arkecosystem/crypto"; +import bip39 from "bip39"; +import { setUpFull, tearDownFull } from "./__support__/setup"; + +const { Block } = models; +const { generateTransfers, generateWallets, generateDelegateRegistration, generateVote } = generators; + +const arktoshi = 10 ** 8; +let container: Container.IContainer; +let PoolWalletManager; +let poolWalletManager; +let blockchain: Blockchain.IBlockchain; + +beforeAll(async () => { + container = await setUpFull(); + + PoolWalletManager = require("../src").PoolWalletManager; + poolWalletManager = new PoolWalletManager(); + blockchain = container.resolvePlugin("blockchain"); +}); + +afterAll(async () => { + await tearDownFull(); +}); + +describe("canApply", () => { + it("should add an error for delegate registration when username is already taken", () => { + const delegateReg = generateDelegateRegistration("unitnet", wallets[11].passphrase, 1, false, "genesis_11")[0]; + const errors = []; + + expect(poolWalletManager.canApply(delegateReg, errors)).toBeFalse(); + expect(errors).toEqual([`Can't apply transaction ${delegateReg.id}: delegate name already taken.`]); + }); + + it("should add an error when voting for a delegate that doesn't exist", () => { + const vote = generateVote("unitnet", wallets[11].passphrase, wallets[12].keys.publicKey, 1)[0]; + const errors = []; + + expect(poolWalletManager.canApply(vote, errors)).toBeFalse(); + expect(errors).toEqual([ + `Can't apply transaction ${vote.id}: delegate +${wallets[12].keys.publicKey} does not exist.`, + ]); + }); +}); + +describe("applyPoolTransactionToSender", () => { + describe("update the balance", () => { + it("should only update the balance of the sender", async () => { + const delegate0 = delegates[0]; + const { publicKey } = crypto.getKeys(bip39.generateMnemonic()); + const newAddress = crypto.getAddress(publicKey); + + const delegateWallet = poolWalletManager.findByAddress(delegate0.address); + const newWallet = poolWalletManager.findByAddress(newAddress); + + expect(+delegateWallet.balance).toBe(+delegate0.balance); + expect(+newWallet.balance).toBe(0); + + const amount1 = 123 * 10 ** 8; + const transfer = generateTransfers("unitnet", delegate0.secret, newAddress, amount1, 1)[0]; + + delegateWallet.applyTransactionToSender(transfer); + + expect(+delegateWallet.balance).toBe(+delegate0.balance - amount1 - 0.1 * 10 ** 8); + expect(newWallet.balance.isZero()).toBeTrue(); + }); + + it("should only update the balance of the sender with dyn fees", async () => { + const delegate0 = delegates[1]; + const { publicKey } = crypto.getKeys(bip39.generateMnemonic()); + const newAddress = crypto.getAddress(publicKey); + + const delegateWallet = poolWalletManager.findByAddress(delegate0.address); + const newWallet = poolWalletManager.findByAddress(newAddress); + + expect(+delegateWallet.balance).toBe(+delegate0.balance); + expect(+newWallet.balance).toBe(0); + + const amount1 = 123 * 10 ** 8; + const fee = 10; + const transfer = generateTransfers("unitnet", delegate0.secret, newAddress, amount1, 1, false, fee)[0]; + + delegateWallet.applyTransactionToSender(transfer); + + expect(+delegateWallet.balance).toBe(+delegate0.balance - amount1 - fee); + expect(newWallet.balance.isZero()).toBeTrue(); + }); + + it("should not apply chained transfers", async () => { + const delegate = delegates[7]; + const delegateWallet = poolWalletManager.findByPublicKey(delegate.publicKey); + + const walletsGen = generateWallets("unitnet", 4); + const poolWallets = walletsGen.map(w => poolWalletManager.findByAddress(w.address)); + + expect(+delegateWallet.balance).toBe(+delegate.balance); + poolWallets.forEach(w => { + expect(+w.balance).toBe(0); + }); + + const transfers = [ + { + // transfer from delegate to wallet 0 + from: delegate, + to: walletsGen[0], + amount: 100 * arktoshi, + }, + { + // transfer from wallet 0 to delegatej + from: walletsGen[0], + to: delegate, + amount: 55 * arktoshi, + }, + ]; + + transfers.forEach(t => { + const transfer = generateTransfers("unitnet", t.from.passphrase, t.to.address, t.amount, 1)[0]; + + // This is normally refused because it's a cold wallet, but since we want + // to test if chained transfers are refused, pretent it is not a cold wallet. + container + .resolvePlugin("database") + .walletManager.findByPublicKey(transfer.senderPublicKey); + + const errors = []; + if (poolWalletManager.canApply(transfer, errors)) { + poolWalletManager.findByPublicKey(transfer.senderPublicKey).applyTransactionToSender(transfer); + + expect(t.from).toBe(delegate); + } else { + expect(t.from).toBe(walletsGen[0]); + expect(JSON.stringify(errors)).toEqual( + `["[PoolWalletManager] Can't apply transaction id:${transfer.id} from sender:${ + t.from.address + }","Insufficient balance in the wallet"]`, + ); + } + + (container.resolvePlugin("database").walletManager as any).forgetByPublicKey( + transfer.publicKey, + ); + }); + + expect(+delegateWallet.balance).toBe(delegate.balance - (100 + 0.1) * arktoshi); + expect(poolWallets[0].balance.isZero()).toBeTrue(); + }); + }); +}); + +describe("Apply transactions and block rewards to wallets on new block", () => { + // tslint:disable-next-line:variable-name + const __resetToHeight1 = async () => blockchain.removeBlocks(blockchain.getLastHeight() - 1); + + beforeEach(__resetToHeight1); + afterEach(__resetToHeight1); + + it.each([2 * arktoshi, 0])("should apply forged block reward %i to delegate wallet", async reward => { + const forgingDelegate = delegates[reward ? 2 : 3]; // use different delegate to have clean initial balance + const generatorPublicKey = forgingDelegate.publicKey; + + const wallet = generateWallets("unitnet", 1)[0]; + const transferAmount = 1234; + const transferDelegate = delegates[4]; + const transfer = generateTransfers( + "unitnet", + transferDelegate.passphrase, + wallet.address, + transferAmount, + 1, + true, + )[0]; + + const totalFee = 0.1 * arktoshi; + const blockWithReward = { + id: "17882607875259085966", + version: 0, + timestamp: 46583330, + height: 2, + reward, + previousBlock: genesisBlock.id, + numberOfTransactions: 1, + transactions: [transfer], + totalAmount: transfer.amount, + totalFee, + payloadLength: 0, + payloadHash: genesisBlock.payloadHash, + generatorPublicKey, + blockSignature: + "3045022100e7385c6ea42bd950f7f6ab8c8619cf2f66a41d8f8f185b0bc99af032cb25f30d02200b6210176a6cedfdcbe483167fd91c21d740e0e4011d24d679c601fdd46b0de9", + createdAt: "2019-07-11T16:48:50.550Z", + }; + const blockWithRewardVerified = new Block(blockWithReward); + blockWithRewardVerified.verification.verified = true; + + await blockchain.processBlock(blockWithRewardVerified, () => null); + + const delegateWallet = poolWalletManager.findByPublicKey(generatorPublicKey); + + const poolWallet = poolWalletManager.findByAddress(wallet.address); + expect(+poolWallet.balance).toBe(transferAmount); + + const transferDelegateWallet = poolWalletManager.findByAddress(transferDelegate.address); + expect(+transferDelegateWallet.balance).toBe(+transferDelegate.balance - transferAmount - totalFee); + + expect(+delegateWallet.balance).toBe(+forgingDelegate.balance + reward + totalFee); // balance increased by reward + fee + }); +}); diff --git a/packages/core-transaction-pool/__tests__/storage.test.ts b/packages/core-transaction-pool/__tests__/storage.test.ts new file mode 100644 index 0000000000..7a81a7e955 --- /dev/null +++ b/packages/core-transaction-pool/__tests__/storage.test.ts @@ -0,0 +1,45 @@ +import { MemPoolTransaction } from "../src/mem-pool-transaction"; +import { Storage } from "../src/storage"; +import { transactions } from "./__fixtures__/transactions"; + +describe("Storage", () => { + const storage = new Storage("./tmp"); + + beforeEach(() => storage.deleteAll()); + afterAll(() => { + storage.deleteAll(); + storage.close(); + }); + + describe("bulkAdd", () => { + it("should add the transactions provided", () => { + const memPoolTransactions = [ + new MemPoolTransaction(transactions.dummy1), + new MemPoolTransaction(transactions.dummy2), + ]; + + storage.bulkAdd(memPoolTransactions); + const allMemPoolTransactions = storage.loadAll(); + expect(allMemPoolTransactions.map(pooltx => pooltx.transaction)).toEqual( + memPoolTransactions.map(pooltx => pooltx.transaction), + ); + }); + }); + + describe("bulkRemoveById", () => { + it("should remove the transactions corresponding to the ids provided", () => { + const memPoolTransactions = [ + new MemPoolTransaction(transactions.dummy1), + new MemPoolTransaction(transactions.dummy2), + ]; + const anotherMemPoolTransaction = new MemPoolTransaction(transactions.dummy3); + + storage.bulkAdd([...memPoolTransactions, anotherMemPoolTransaction]); + storage.bulkRemoveById([transactions.dummy3.id]); + const allMemPoolTransactions = storage.loadAll(); + expect(allMemPoolTransactions.map(pooltx => pooltx.transaction)).toEqual( + memPoolTransactions.map(pooltx => pooltx.transaction), + ); + }); + }); +}); diff --git a/packages/core-transaction-pool/__tests__/storage.with-mock.test.ts b/packages/core-transaction-pool/__tests__/storage.with-mock.test.ts new file mode 100644 index 0000000000..c0c2849e6b --- /dev/null +++ b/packages/core-transaction-pool/__tests__/storage.with-mock.test.ts @@ -0,0 +1,33 @@ +import { MemPoolTransaction } from "../src/mem-pool-transaction"; +import { Storage } from "../src/storage"; +import { transactions } from "./__fixtures__/transactions"; + +// first mock the whole better-sqlite3 module to be able to control 'inTransaction' prop +const mockPrepare = jest.fn().mockImplementation(str => ({ + run: () => true, + all: () => [], +})); +jest.mock("better-sqlite3", () => { + // tslint:disable-next-line:only-arrow-functions + return function(file) { + return { + inTransaction: true, + prepare: mockPrepare, + exec: () => true, + }; + }; +}); + +describe("Storage", () => { + describe("bulkAdd", () => { + it("should rollback if this.db is 'inTransaction'", () => { + const storage = new Storage("./tmp"); + const memPoolTransaction = new MemPoolTransaction(transactions.dummy1); + storage.bulkAdd([memPoolTransaction]); + + expect(mockPrepare).toHaveBeenLastCalledWith("ROLLBACK;"); + + jest.unmock("better-sqlite3"); + }); + }); +}); diff --git a/packages/core-transaction-pool/jest.config.js b/packages/core-transaction-pool/jest.config.js deleted file mode 100644 index 4db45c2003..0000000000 --- a/packages/core-transaction-pool/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - silent: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-transaction-pool/lib/guard.js b/packages/core-transaction-pool/lib/guard.js deleted file mode 100644 index 8102adf1d4..0000000000 --- a/packages/core-transaction-pool/lib/guard.js +++ /dev/null @@ -1,340 +0,0 @@ -/* eslint max-len: "off" */ - -const app = require('@arkecosystem/core-container') -const crypto = require('@arkecosystem/crypto') -const pluralize = require('pluralize') - -const { - configManager, - constants: { TRANSACTION_TYPES }, - models: { Transaction }, - slots, -} = crypto -const isRecipientOnActiveNetwork = require('./utils/is-on-active-network') - -const dynamicFeeMatch = require('./utils/dynamicfee-matcher') - -module.exports = class TransactionGuard { - /** - * Create a new transaction guard instance. - * @param {TransactionPoolInterface} pool - * @return {void} - */ - constructor(pool) { - this.pool = pool - - this.transactions = [] - this.excess = [] - this.accept = new Map() - this.broadcast = new Map() - this.invalid = new Map() - this.errors = {} - } - - /** - * Validate the specified transactions and accepted transactions to the pool. - * @param {Array} transactions - * @return Object { - * accept: array of transaction ids that qualify for entering the pool - * broadcast: array of of transaction ids that qualify for broadcasting - * invalid: array of invalid transaction ids - * excess: array of transaction ids that exceed sender's quota in the pool - * errors: Object with - * keys=transaction id (for each element in invalid[]), - * value=[ { type, message }, ... ] - * } - */ - async validate(transactionsJson) { - this.pool.loggedAllowedSenders = [] - - // Cache transactions - this.transactions = this.__cacheTransactions(transactionsJson) - - if (this.transactions.length > 0) { - // Filter transactions and create Transaction instances from accepted ones - this.__filterAndTransformTransactions(this.transactions) - - // Remove already forged tx... Not optimal here - await this.__removeForgedTransactions() - - // Add transactions to the pool - this.__addTransactionsToPool() - - this.__printStats() - } - - return { - accept: Array.from(this.accept.keys()), - broadcast: Array.from(this.broadcast.keys()), - invalid: Array.from(this.invalid.keys()), - excess: this.excess, - errors: Object.keys(this.errors).length > 0 ? this.errors : null, - } - } - - /** - * Cache the given transactions and return which got added. Already cached - * transactions are not returned. - * @return {Array} - */ - __cacheTransactions(transactions) { - const { added, notAdded } = app - .resolve('state') - .cacheTransactions(transactions) - - notAdded.forEach(transaction => { - if (!this.errors[transaction.id]) { - this.__pushError(transaction, 'ERR_DUPLICATE', 'Already in cache.') - } - }) - - return added - } - - /** - * Get broadcast transactions. - * @return {Array} - */ - getBroadcastTransactions() { - return Array.from(this.broadcast.values()) - } - - /** - * Transforms and filters incoming transactions. - * It skips: - * - transactions already in the pool - * - transactions from blocked senders - * - transactions from the future - * - dynamic fee mismatch - * - transactions based on type specific restrictions - * - not valid crypto transactions - * @param {Array} transactions - * @return {void} - */ - __filterAndTransformTransactions(transactions) { - transactions.forEach(transaction => { - const exists = this.pool.transactionExists(transaction.id) - - if (exists) { - this.__pushError( - transaction, - 'ERR_DUPLICATE', - `Duplicate transaction ${transaction.id}`, - ) - } else if (this.pool.isSenderBlocked(transaction.senderPublicKey)) { - this.__pushError( - transaction, - 'ERR_SENDER_BLOCKED', - `Transaction ${transaction.id} rejected. Sender ${ - transaction.senderPublicKey - } is blocked.`, - ) - } else if (this.pool.hasExceededMaxTransactions(transaction)) { - this.excess.push(transaction.id) - } else if (this.__validateTransaction(transaction)) { - try { - const trx = new Transaction(transaction) - if (trx.verified) { - const dynamicFee = dynamicFeeMatch(trx) - if (dynamicFee.enterPool) { - this.accept.set(trx.id, trx) - } else { - this.__pushError( - transaction, - 'ERR_LOW_FEE', - 'Too low fee to be accepted in the pool', - ) - } - - if (dynamicFee.broadcast) { - this.broadcast.set(trx.id, trx) - } else { - this.__pushError( - transaction, - 'ERR_LOW_FEE', - 'Too low fee for broadcast', - ) - } - } else { - this.__pushError( - transaction, - 'ERR_BAD_DATA', - "Transaction didn't pass the verification process.", - ) - } - } catch (error) { - this.__pushError(transaction, 'ERR_UNKNOWN', error.message) - } - } - }) - } - - /** - * Determines valid transactions by checking rules, according to: - * - transaction timestamp - * - wallet balance - * - transaction type specifics: - * - if recipient is on the same network - * - if sender already has another transaction of the same type, for types that - * - only allow one transaction at a time in the pool (e.g. vote) - */ - __validateTransaction(transaction) { - const now = slots.getTime() - if (transaction.timestamp > now + 3600) { - const secondsInFuture = transaction.timestamp - now - this.__pushError( - transaction, - 'ERR_FROM_FUTURE', - `Transaction ${ - transaction.id - } is ${secondsInFuture} seconds in the future`, - ) - return false - } - - const errors = [] - if (!this.pool.walletManager.canApply(transaction, errors)) { - this.__pushError(transaction, 'ERR_APPLY', JSON.stringify(errors)) - return false - } - - switch (transaction.type) { - case TRANSACTION_TYPES.TRANSFER: - if (!isRecipientOnActiveNetwork(transaction)) { - this.__pushError( - transaction, - 'ERR_INVALID_RECIPIENT', - `Recipient ${ - transaction.recipientId - } is not on the same network: ${configManager.get('pubKeyHash')}`, - ) - return false - } - break - case TRANSACTION_TYPES.SECOND_SIGNATURE: - case TRANSACTION_TYPES.DELEGATE_REGISTRATION: - case TRANSACTION_TYPES.VOTE: - if ( - this.pool.senderHasTransactionsOfType( - transaction.senderPublicKey, - transaction.type, - ) - ) { - this.__pushError( - transaction, - 'ERR_PENDING', - `Sender ${ - transaction.senderPublicKey - } already has a transaction of type ` + - `'${TRANSACTION_TYPES.toString(transaction.type)}' in the pool`, - ) - return false - } - break - case TRANSACTION_TYPES.MULTI_SIGNATURE: - case TRANSACTION_TYPES.IPFS: - case TRANSACTION_TYPES.TIMELOCK_TRANSFER: - case TRANSACTION_TYPES.MULTI_PAYMENT: - case TRANSACTION_TYPES.DELEGATE_RESIGNATION: - default: - this.__pushError( - transaction, - 'ERR_UNSUPPORTED', - 'Invalidating transaction of unsupported type ' + - `'${TRANSACTION_TYPES.toString(transaction.type)}'`, - ) - return false - } - - return true - } - - /** - * Remove already forged transactions. - * @return {void} - */ - async __removeForgedTransactions() { - const database = app.resolvePlugin('database') - - const forgedIdsSet = await database.getForgedTransactionsIds([ - ...new Set([...this.accept.keys(), ...this.broadcast.keys()]), - ]) - - app.resolve('state').removeCachedTransactionIds(forgedIdsSet) - - forgedIdsSet.forEach(id => { - this.__pushError(this.accept.get(id), 'ERR_FORGED', 'Already forged.') - - this.accept.delete(id) - this.broadcast.delete(id) - }) - } - - /** - * Add accepted transactions to the pool and filter rejected ones. - * @return {void} - */ - __addTransactionsToPool() { - // Add transactions to the transaction pool - const { added, notAdded } = this.pool.addTransactions( - Array.from(this.accept.values()), - ) - - // Exclude transactions which were refused from the pool - notAdded.forEach(item => { - this.accept.delete(item.transaction.id) - - // The transaction should still be broadcasted if the pool is full - if (item.type !== 'ERR_POOL_FULL') { - this.broadcast.delete(item.transaction.id) - } - - this.__pushError(item.transaction, item.type, item.message) - }) - } - - /** - * Adds a transaction to the errors object. The transaction id is mapped to an - * array of errors. There may be multiple errors associated with a transaction in - * which case __pushError is called multiple times. - * @param {Transaction} transaction - * @param {String} type - * @param {String} message - * @return {void} - */ - __pushError(transaction, type, message) { - if (!this.errors[transaction.id]) { - this.errors[transaction.id] = [] - } - - this.errors[transaction.id].push({ type, message }) - - this.invalid.set(transaction.id, transaction) - } - - /** - * Print compact transaction stats. - * @return {void} - */ - __printStats() { - const properties = ['accept', 'broadcast', 'excess', 'invalid'] - const stats = properties - .map( - prop => - `${prop}: ${ - this[prop] instanceof Array ? this[prop].length : this[prop].size - }`, - ) - .join(' ') - - app - .resolvePlugin('logger') - .info( - `Received ${pluralize( - 'transaction', - this.transactions.length, - true, - )} (${stats}).`, - ) - } -} diff --git a/packages/core-transaction-pool/lib/index.js b/packages/core-transaction-pool/lib/index.js deleted file mode 100644 index 6e24a61e41..0000000000 --- a/packages/core-transaction-pool/lib/index.js +++ /dev/null @@ -1,25 +0,0 @@ -const transactionPoolManager = require('./manager') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - alias: 'transactionPoolManager', - async register(container, options) { - return transactionPoolManager - }, -} - -/** - * The interface used by concrete implementations. - * @type {TransactionPoolInterface} - */ -exports.TransactionPoolInterface = require('./interface') - -/** - * The guard used to handle transaction validation. - * @type {TransactionGuard} - */ -exports.TransactionGuard = require('./guard') diff --git a/packages/core-transaction-pool/lib/interface.js b/packages/core-transaction-pool/lib/interface.js deleted file mode 100644 index 3eeee9391c..0000000000 --- a/packages/core-transaction-pool/lib/interface.js +++ /dev/null @@ -1,345 +0,0 @@ -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') - -const dayjs = require('dayjs-ext') -const PoolWalletManager = require('./pool-wallet-manager') - -const database = app.resolvePlugin('database') -const dynamicFeeMatch = require('./utils/dynamicfee-matcher') - -module.exports = class TransactionPoolInterface { - /** - * Create a new transaction pool instance. - * @param {Object} options - */ - constructor(options) { - this.options = options - this.walletManager = new PoolWalletManager() - - this.blockedByPublicKey = {} - } - - /** - * Get a driver instance. - * @return {TransactionPoolInterface} - */ - driver() { - return this.driver - } - - /** - * Disconnect from transaction pool. - * @return {void} - */ - disconnect() { - throw new Error('Method [disconnect] not implemented!') - } - - /** - * Get the number of transactions in the pool. - * @return {Number} - */ - getPoolSize() { - throw new Error('Method [getPoolSize] not implemented!') - } - - /** - * Get the number of transaction in the pool from specific sender - * @param {String} senderPublicKey - * @return {Number} - */ - getSenderSize(senderPublicKey) { - throw new Error('Method [getSenderSize] not implemented!') - } - - /** - * Add a transaction to the pool. - * @param {Transaction} transaction - */ - addTransaction(transaction) { - throw new Error('Method [addTransaction] not implemented!') - } - - /** - * Remove a transaction from the pool by transaction object. - * @param {Transaction} transaction - * @return {void} - */ - removeTransaction(transaction) { - throw new Error('Method [removeTransaction] not implemented!') - } - - /** - * Remove a transaction from the pool by id. - * @param {Number} id - * @return {void} - */ - removeTransactionById(id) { - throw new Error('Method [removeTransactionById] not implemented!') - } - - /** - * Get all transactions that are ready to be forged. - * @param {Number} blockSize - * @return {(Array|void)} - */ - getTransactionsForForging(blockSize) { - throw new Error('Method [getTransactionsForForging] not implemented!') - } - - /** - * Get a transaction from the pool by transaction id. - * @param {Number} id - * @return {(Transaction|String)} - */ - getTransaction(id) { - throw new Error('Method [getTransaction] not implemented!') - } - - /** - * Get all transactions within the specified range. - * @param {Number} start - * @param {Number} size - * @return {Array} - */ - getTransactions(start, size) { - throw new Error('Method [getTransactions] not implemented!') - } - - /** - * Get all cleans transactions IDs within the specified range from transaction pool. - * @param {Number} start - * @param {Number} size - * @return {Array} - */ - getTransactionIdsForForging(start, size) { - throw new Error('Method [getTransactionIdsForForging] not implemented!') - } - - /** - * Remove all transactions from transaction pool belonging to specific sender - * @param {String} senderPublicKey - * @return {void} - */ - removeTransactionsForSender(senderPublicKey) { - throw new Error('Method [removeTransactionsForSender] not implemented!') - } - - /** - * Add many transaction to the pool. Method called from blockchain, upon receiving payload. - * @param {Array} transactions - */ - addTransactions(transactions) { - throw new Error('Method [addTransactions] not implemented!') - } - - /** - * Check whether sender of transaction has exceeded max transactions in queue. - * @param {String} transaction - * @return {(Boolean|void)} - */ - hasExceededMaxTransactions(transaction) { - throw new Error('Method [hasExceededMaxTransactions] not implemented!') - } - - /** - * Check whether transaction is already in pool - * @param {Transaction} transaction - * @return {Boolean} - */ - transactionExists(transaction) { - throw new Error('Method [transactionExists] not implemented!') - } - - /** - * Check if transaction sender is blocked - * @param {String} senderPublicKey - * @return {Boolean} - */ - isSenderBlocked(senderPublicKey) { - if (!this.blockedByPublicKey[senderPublicKey]) { - return false - } - - if (this.blockedByPublicKey[senderPublicKey] < dayjs()) { - delete this.blockedByPublicKey[senderPublicKey] - return false - } - - return true - } - - /** - * Blocks sender for a specified time - * @param {String} senderPublicKey - * @return {Time} blockReleaseTime - */ - blockSender(senderPublicKey) { - const blockReleaseTime = dayjs().add(1, 'hours') - - this.blockedByPublicKey[senderPublicKey] = blockReleaseTime - - logger.warn( - `Sender ${senderPublicKey} blocked until ${ - this.blockedByPublicKey[senderPublicKey] - } :stopwatch:`, - ) - - return blockReleaseTime - } - - /** - * Processes recently accepted block by the blockchain. - * It removes block transaction from the pool and adjusts - * pool wallets for non existing transactions. - * - * @param {Object} block - * @return {void} - */ - acceptChainedBlock(block) { - for (const { data } of block.transactions) { - const exists = this.transactionExists(data.id) - const senderPublicKey = data.senderPublicKey - - const senderWallet = this.walletManager.exists(senderPublicKey) - ? this.walletManager.findByPublicKey(senderPublicKey) - : false - - const recipientWallet = this.walletManager.exists(data.recipientId) - ? this.walletManager.findByAddress(data.recipientId) - : false - - if (recipientWallet) { - recipientWallet.applyTransactionToRecipient(data) - } - - if (exists) { - this.removeTransaction(data) - } else if (senderWallet) { - const errors = [] - if (senderWallet.canApply(data, errors)) { - senderWallet.applyTransactionToSender(data) - } else { - this.purgeByPublicKey(data.senderPublicKey) - this.blockSender(data.senderPublicKey) - - logger.error( - `CanApply transaction test failed on acceptChainedBlock() in transaction pool for transaction id:${ - data.id - } due to ${JSON.stringify( - errors, - )}. Possible double spending attack :bomb:`, - ) - } - } - - if ( - senderWallet.balance === 0 && - this.getSenderSize(senderPublicKey) === 0 - ) { - this.walletManager.deleteWallet(senderPublicKey) - } - } - - // if delegate in poll wallet manager - apply rewards and fees - if (this.walletManager.exists(block.data.generatorPublicKey)) { - const delegateWallet = this.walletManager.findByPublicKey( - block.data.generatorPublicKey, - ) - const increase = block.data.reward.plus(block.data.totalFee) - delegateWallet.balance = delegateWallet.balance.plus(increase) - } - - app - .resolve('state') - .removeCachedTransactionIds(block.transactions.map(tx => tx.id)) - } - - /** - * Rebuild pool manager wallets - * Removes all the wallets from pool manager and applies transaction from pool - if any - * It waits for the node to sync, and then check the transactions in pool - * and validates them and apply to the pool manager. - * @return {void} - */ - async buildWallets() { - this.walletManager.reset() - const poolTransactionIds = await this.getTransactionIdsForForging( - 0, - this.getPoolSize(), - ) - - app.resolve('state').removeCachedTransactionIds(poolTransactionIds) - - poolTransactionIds.forEach(transactionId => { - const transaction = this.getTransaction(transactionId) - if (!transaction) { - return - } - - const senderWallet = this.walletManager.findByPublicKey( - transaction.senderPublicKey, - ) - const errors = [] - if (senderWallet && senderWallet.canApply(transaction, errors)) { - senderWallet.applyTransactionToSender(transaction) - } else { - logger.error('BuildWallets from pool:', errors) - this.purgeByPublicKey(transaction.senderPublicKey) - } - }) - logger.info('Transaction Pool Manager build wallets complete') - } - - purgeByPublicKey(senderPublicKey) { - logger.debug(`Purging sender: ${senderPublicKey} from pool wallet manager`) - - this.removeTransactionsForSender(senderPublicKey) - - this.walletManager.deleteWallet(senderPublicKey) - } - - /** - * Purges all transactions from senders with at least one - * invalid transaction. - * @param {Block} block - */ - purgeSendersWithInvalidTransactions(block) { - const publicKeys = new Set( - block.transactions - .filter(tx => !tx.verified) - .map(tx => tx.senderPublicKey), - ) - - publicKeys.forEach(publicKey => this.purgeByPublicKey(publicKey)) - } - - /** - * Purges all transactions from the block. - * Purges if transaction exists. It assumes that if trx exists that also wallet exists in pool - * @param {Block} block - */ - purgeBlock(block) { - block.transactions.forEach(tx => { - if (this.transactionExists(tx.id)) { - this.removeTransaction(tx) - this.walletManager - .findByPublicKey(tx.senderPublicKey) - .revertTransactionForSender(tx) - } - }) - } - - /** - * Check whether a given sender has any transactions of the specified type - * in the pool. - * @param {String} senderPublicKey public key of the sender - * @param {Number} transactionType transaction type, must be one of - * TRANSACTION_TYPES.* and is compared against transaction.type. - * @return {Boolean} true if exist - */ - senderHasTransactionsOfType(senderPublicKey, transactionType) { - throw new Error('Method [senderHasTransactionsOfType] not implemented!') - } -} diff --git a/packages/core-transaction-pool/lib/manager.js b/packages/core-transaction-pool/lib/manager.js deleted file mode 100644 index 8431331910..0000000000 --- a/packages/core-transaction-pool/lib/manager.js +++ /dev/null @@ -1,30 +0,0 @@ -class TransactionPoolManager { - /** - * Create a new transaction pool manager instance. - * @constructor - */ - constructor() { - this.connections = {} - } - - /** - * Get a transaction pool instance. - * @param {String} name - * @return {TransactionPoolInterface} - */ - connection(name = 'default') { - return this.connections[name] - } - - /** - * Make the transaction pool instance. - * @param {TransactionPoolInterface} connection - * @param {String} name - * @return {void} - */ - async makeConnection(connection, name = 'default') { - this.connections[name] = await connection.make() - } -} - -module.exports = new TransactionPoolManager() diff --git a/packages/core-transaction-pool/lib/pool-wallet-manager.js b/packages/core-transaction-pool/lib/pool-wallet-manager.js deleted file mode 100644 index 53b8e4c274..0000000000 --- a/packages/core-transaction-pool/lib/pool-wallet-manager.js +++ /dev/null @@ -1,152 +0,0 @@ -const app = require('@arkecosystem/core-container') -const { Wallet } = require('@arkecosystem/crypto').models -const { WalletManager } = require('@arkecosystem/core-database') - -const logger = app.resolvePlugin('logger') -const database = app.resolvePlugin('database') -const config = app.resolvePlugin('config') -const { crypto } = require('@arkecosystem/crypto') -const { TRANSACTION_TYPES } = require('@arkecosystem/crypto').constants - -module.exports = class PoolWalletManager extends WalletManager { - /** - * Create a new pool wallet manager instance. - * @constructor - */ - constructor() { - super() - - this.emitEvents = false - } - - /** - * Get a wallet by the given address. If wallet is not found it is copied from blockchain - * wallet manager. Method overrides base class method from WalletManager. - * WARNING: call only upon guard apply, as if wallet not found it gets it from blockchain. - * For existing key checks use function exists(key) - * @param {String} address - * @return {(Wallet|null)} - */ - findByAddress(address) { - if (!this.byAddress[address]) { - const blockchainWallet = database.walletManager.findByAddress(address) - const wallet = Object.assign(new Wallet(address), blockchainWallet) // do not modify - - this.reindex(wallet) - } - - return this.byAddress[address] - } - - /** - * Checks if wallet exits in pool wallet manager - * Method overrides base class method from WalletManager. - * @param {String} key can be publicKey or address of wallet - * @return {Boolean} true if exists - */ - exists(key) { - if (this.byPublicKey[key]) { - return true - } - - if (this.byAddress[key]) { - return true - } - return false - } - - deleteWallet(publicKey) { - this.forgetByPublicKey(publicKey) - this.forgetByAddress( - crypto.getAddress(publicKey, config.network.pubKeyHash), - ) - } - - /** - * Checks if the transaction can be applied. - * @param {Object|Transaction} transaction - * @param {Array} errors The errors are written into the array. - * @return {Boolean} - */ - canApply(transaction, errors) { - // Edge case if sender is unknown and has no balance. - // NOTE: Check is performed against the database wallet manager. - if (!database.walletManager.byPublicKey[transaction.senderPublicKey]) { - const senderAddress = crypto.getAddress( - transaction.senderPublicKey, - config.network.pubKeyHash, - ) - - if ( - database.walletManager.findByAddress(senderAddress).balance.isZero() - ) { - errors.push( - 'Cold wallet is not allowed to send until receiving transaction is confirmed.', - ) - return false - } - } - - const sender = this.findByPublicKey(transaction.senderPublicKey) - const { type, asset } = transaction - - if ( - type === TRANSACTION_TYPES.DELEGATE_REGISTRATION && - database.walletManager.byUsername[asset.delegate.username.toLowerCase()] - ) { - logger.error( - `[PoolWalletManager] Can't apply transaction ${ - transaction.id - }: delegate name already taken. Data: ${JSON.stringify(transaction)}`, - ) - - errors.push( - `Can't apply transaction ${ - transaction.id - }: delegate name already taken.`, - ) - // NOTE: We use the vote public key, because vote transactions have the same sender and recipient. - } else if ( - type === TRANSACTION_TYPES.VOTE && - !database.walletManager.__isDelegate(asset.votes[0].slice(1)) - ) { - logger.error( - `[PoolWalletManager] Can't apply vote transaction: delegate ${ - asset.votes[0] - } does not exist. Data: ${JSON.stringify(transaction)}`, - ) - - errors.push( - `Can't apply transaction ${transaction.id}: delegate ${ - asset.votes[0] - } does not exist.`, - ) - } else if (this.__isException(transaction)) { - logger.warn( - `Transaction forcibly applied because it has been added as an exception: ${transaction}`, - ) - } else if (!sender.canApply(transaction, errors)) { - const message = `[PoolWalletManager] Can't apply transaction id:${ - transaction.id - } from sender:${sender.address}` - logger.error(`${message} due to ${JSON.stringify(errors)}`) - errors.unshift(message) - } - - return errors.length === 0 - } - - /** - * Remove the given transaction from a sender only. - * @param {Transaction} transaction - * @return {Transaction} - */ - revertTransactionForSender(transaction) { - const { data } = transaction - const sender = this.findByPublicKey(data.senderPublicKey) // Should exist - - sender.revertTransactionForSender(data) - - return data - } -} diff --git a/packages/core-transaction-pool/lib/utils/dynamicfee-matcher.js b/packages/core-transaction-pool/lib/utils/dynamicfee-matcher.js deleted file mode 100644 index de8b708dd9..0000000000 --- a/packages/core-transaction-pool/lib/utils/dynamicfee-matcher.js +++ /dev/null @@ -1,95 +0,0 @@ -const app = require('@arkecosystem/core-container') -const { - feeManager, - dynamicFeeManager, - formatArktoshi, -} = require('@arkecosystem/crypto') - -/** - * Determine if a transaction's fee meets the minimum requirements for broadcasting - * and for entering the transaction pool. - * @param {Transaction} Transaction - transaction to check - * @return {Object} { broadcast: Boolean, enterPool: Boolean } - */ -module.exports = transaction => { - const config = app.resolvePlugin('config') - const logger = app.resolvePlugin('logger') - - const fee = +transaction.fee.toFixed() - const id = transaction.id - - const blockchain = app.resolvePlugin('blockchain') - const fees = config.getConstants(blockchain.getLastBlock().data.height).fees - - let broadcast - let enterPool - - if (fees.dynamic) { - const minFeeBroadcast = dynamicFeeManager.calculateFee( - fees.dynamicFees.minFeeBroadcast, - transaction, - ) - if (fee >= minFeeBroadcast) { - broadcast = true - logger.debug( - `Transaction ${id} eligible for broadcast - fee of ${formatArktoshi( - fee, - )} is ${ - fee === minFeeBroadcast ? 'equal to' : 'greater than' - } minimum fee (${formatArktoshi(minFeeBroadcast)})`, - ) - } else { - broadcast = false - logger.debug( - `Transaction ${id} not eligible for broadcast - fee of ${formatArktoshi( - fee, - )} is smaller than minimum fee (${formatArktoshi(minFeeBroadcast)})`, - ) - } - - const minFeePool = dynamicFeeManager.calculateFee( - fees.dynamicFees.minFeePool, - transaction, - ) - if (fee >= minFeePool) { - enterPool = true - logger.debug( - `Transaction ${id} eligible to enter pool - fee of ${formatArktoshi( - fee, - )} is ${ - fee === minFeePool ? 'equal to' : 'greater than' - } minimum fee (${formatArktoshi(minFeePool)})`, - ) - } else { - enterPool = false - logger.debug( - `Transaction ${id} not eligible to enter pool - fee of ${formatArktoshi( - fee, - )} is smaller than minimum fee (${formatArktoshi(minFeePool)})`, - ) - } - } else { - // Static fees - const staticFee = feeManager.getForTransaction(transaction) - - if (fee === staticFee) { - broadcast = true - enterPool = true - logger.debug( - `Transaction ${id} eligible for broadcast and to enter pool - fee of ${formatArktoshi( - fee, - )} is equal to static fee (${formatArktoshi(staticFee)})`, - ) - } else { - broadcast = false - enterPool = false - logger.debug( - `Transaction ${id} not eligible for broadcast and not eligible to enter pool - fee of ${formatArktoshi( - fee, - )} does not match static fee (${formatArktoshi(staticFee)})`, - ) - } - } - - return { broadcast, enterPool } -} diff --git a/packages/core-transaction-pool/lib/utils/is-on-active-network.js b/packages/core-transaction-pool/lib/utils/is-on-active-network.js deleted file mode 100644 index f65baaf211..0000000000 --- a/packages/core-transaction-pool/lib/utils/is-on-active-network.js +++ /dev/null @@ -1,26 +0,0 @@ -const app = require('@arkecosystem/core-container') -const bs58check = require('bs58check') -const { configManager } = require('@arkecosystem/crypto') - -const logger = app.resolvePlugin('logger') - -/** - * Checks if transaction recipient is on the same network as blockchain - * @param {Transaction} - * @return {Boolean} - */ -module.exports = transaction => { - const recipientPrefix = bs58check.decode(transaction.recipientId).readUInt8(0) - - if (recipientPrefix === configManager.get('pubKeyHash')) { - return true - } - - logger.error( - `Recipient ${ - transaction.recipientId - } is not on the same network: ${configManager.get('pubKeyHash')}`, - ) - - return false -} diff --git a/packages/core-transaction-pool/package.json b/packages/core-transaction-pool/package.json index eed66bc895..ac05693c10 100644 --- a/packages/core-transaction-pool/package.json +++ b/packages/core-transaction-pool/package.json @@ -1,38 +1,66 @@ { - "name": "@arkecosystem/core-transaction-pool", - "description": "Transaction Pool Manager for Ark Core", - "version": "0.2.1", - "contributors": [ - "Kristjan Košič ", - "Brian Faust ", - "Alex Barnsley " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-database": "~0.2", - "@arkecosystem/crypto": "~0.2", - "bs58check": "^2.1.2", - "dayjs-ext": "^2.2.0", - "pluralize": "^7.0.0" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2", - "bip39": "^2.5.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-transaction-pool", + "description": "Transaction Pool Manager for Ark Core", + "version": "2.1.0", + "contributors": [ + "Kristjan Košič ", + "Brian Faust ", + "Alex Barnsley ", + "Vasil Dimov ", + "Joshua Noack " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-database": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@arkecosystem/core-interfaces": "^2.1.0", + "@types/better-sqlite3": "^5.2.0", + "@types/fs-extra": "^5.0.4", + "@types/pluralize": "^0.0.29", + "better-sqlite3": "^5.2.1", + "bs58check": "^2.1.2", + "dayjs-ext": "^2.2.0", + "delay": "^4.1.0", + "fs-extra": "^7.0.1", + "pluralize": "^7.0.0" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0", + "@arkecosystem/core-utils": "^2.1.0", + "@types/bip39": "^2.4.1", + "@types/random-seed": "^0.3.3", + "bip39": "^2.5.0", + "random-seed": "^0.3.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-transaction-pool/src/config.ts b/packages/core-transaction-pool/src/config.ts new file mode 100644 index 0000000000..1b95990a82 --- /dev/null +++ b/packages/core-transaction-pool/src/config.ts @@ -0,0 +1,2 @@ +import { Shared } from "@arkecosystem/core-interfaces"; +export const config = new Shared.Config(); diff --git a/packages/core-transaction-pool/src/connection.ts b/packages/core-transaction-pool/src/connection.ts new file mode 100644 index 0000000000..70f27ec364 --- /dev/null +++ b/packages/core-transaction-pool/src/connection.ts @@ -0,0 +1,581 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, EventEmitter, Logger, TransactionPool as transactionPool } from "@arkecosystem/core-interfaces"; + +import assert from "assert"; +import dayjs from "dayjs-ext"; +import { PoolWalletManager } from "./pool-wallet-manager"; + +import { Mem } from "./mem"; +import { MemPoolTransaction } from "./mem-pool-transaction"; +import { Storage } from "./storage"; + +const databaseService = app.resolvePlugin("database"); +const emitter = app.resolvePlugin("event-emitter"); +const logger = app.resolvePlugin("logger"); + +/** + * Transaction pool. It uses a hybrid storage - caching the data + * in memory and occasionally saving it to a persistent, on-disk storage (SQLite), + * every N modifications, and also during shutdown. The operations that only read + * data (everything other than add or remove transaction) are served from the + * in-memory storage. + */ +export class TransactionPool implements transactionPool.ITransactionPool { + public walletManager: any; + public blockedByPublicKey: any; + public mem: any; + public storage: any; + public loggedAllowedSenders: any[]; + + /** + * Create a new transaction pool instance. + * @param {Object} options + */ + constructor(public options) { + this.walletManager = new PoolWalletManager(); + this.blockedByPublicKey = {}; + } + /** + * Make the transaction pool instance. Load all transactions in the pool from + * the on-disk database, saved there from a previous run. + * @return {TransactionPool} + */ + public async make() { + this.mem = new Mem(); + this.storage = new Storage(this.options.storage); + this.loggedAllowedSenders = []; + + const all = this.storage.loadAll(); + all.forEach(t => this.mem.add(t, this.options.maxTransactionAge, true)); + + this.__purgeExpired(); + + // Remove transactions that were forged while we were offline. + const allIds = all.map(memPoolTransaction => memPoolTransaction.transaction.id); + + const forgedIds = await databaseService.getForgedTransactionsIds(allIds); + + forgedIds.forEach(id => this.removeTransactionById(id)); + + return this; + } + + /** + * Get a driver instance. + * @return {TransactionPoolInterface} + */ + public driver() { + return this.driver; + } + + /** + * Disconnect from transaction pool. + * @return {void} + */ + public disconnect() { + this.__syncToPersistentStorage(); + this.storage.close(); + } + + /** + * Get all transactions of a given type from the pool. + * @param {Number} type of transaction + * @return {Set of MemPoolTransaction} all transactions of the given type, could be empty Set + */ + public getTransactionsByType(type) { + this.__purgeExpired(); + + return this.mem.getByType(type); + } + + /** + * Get the number of transactions in the pool. + * @return {Number} + */ + public getPoolSize() { + this.__purgeExpired(); + + return this.mem.getSize(); + } + + /** + * Get the number of transactions in the pool from a specific sender + * @param {String} senderPublicKey + * @returns {Number} + */ + public getSenderSize(senderPublicKey) { + this.__purgeExpired(); + + return this.mem.getBySender(senderPublicKey).size; + } + + /** + * Add many transactions to the pool. + * @param {Array} transactions, already transformed and verified + * by transaction guard - must have serialized field + * @return {Object} like + * { + * added: [ ... successfully added transactions ... ], + * notAdded: [ { transaction: Transaction, type: String, message: String }, ... ] + * } + */ + public addTransactions(transactions) { + const added = []; + const notAdded = []; + + for (const t of transactions) { + const result = this.addTransaction(t); + + if (result.success) { + added.push(t); + } else { + notAdded.push(result); + } + } + + return { added, notAdded }; + } + + /** + * Add a transaction to the pool. + * @param {Transaction} transaction + * @return {Object} The success property indicates wether the transaction was successfully added + * and applied to the pool or not. In case it was not successful, the type and message + * property yield information about the error. + */ + public addTransaction(transaction) { + if (this.transactionExists(transaction.id)) { + logger.debug( + "Transaction pool: ignoring attempt to add a transaction that is already " + + `in the pool, id: ${transaction.id}`, + ); + + return this.__createError(transaction, "ERR_ALREADY_IN_POOL", "Already in pool"); + } + + const poolSize = this.mem.getSize(); + + if (this.options.maxTransactionsInPool <= poolSize) { + // The pool can't accommodate more transactions. Either decline the newcomer or remove + // an existing transaction from the pool in order to free up space. + const all = this.mem.getTransactionsOrderedByFee(); + const lowest = all[all.length - 1].transaction; + + if (lowest.fee.isLessThan(transaction.fee)) { + this.walletManager.revertTransactionForSender(lowest); + this.mem.remove(lowest.id, lowest.senderPublicKey); + } else { + return this.__createError( + transaction, + "ERR_POOL_FULL", + `Pool is full (has ${poolSize} transactions) and this transaction's fee ` + + `${transaction.fee.toFixed()} is not higher than the lowest fee already in pool ` + + `${lowest.fee.toFixed()}`, + ); + } + } + + this.mem.add(new MemPoolTransaction(transaction), this.options.maxTransactionAge); + + // Apply transaction to pool wallet manager. + const senderWallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + + const errors = []; + if (this.walletManager.canApply(transaction, errors)) { + senderWallet.applyTransactionToSender(transaction); + } else { + // Remove tx again from the pool + this.mem.remove(transaction.id); + + return this.__createError(transaction, "ERR_APPLY", JSON.stringify(errors)); + } + + this.__syncToPersistentStorageIfNecessary(); + return { success: true }; + } + + /** + * Remove a transaction from the pool by transaction object. + * @param {Transaction} transaction + * @return {void} + */ + public removeTransaction(transaction) { + this.removeTransactionById(transaction.id, transaction.senderPublicKey); + } + + /** + * Remove a transaction from the pool by id. + * @param {String} id + * @param {String} senderPublicKey + * @return {void} + */ + public removeTransactionById(id, senderPublicKey?) { + this.mem.remove(id, senderPublicKey); + + this.__syncToPersistentStorageIfNecessary(); + } + + /** + * Get all transactions that are ready to be forged. + * @param {Number} blockSize + * @return {(Array|void)} + */ + public getTransactionsForForging(blockSize) { + return this.getTransactions(0, blockSize); + } + + /** + * Get a transaction by transaction id. + * @param {String} id + * @return {(Transaction|undefined)} + */ + public getTransaction(id) { + this.__purgeExpired(); + + return this.mem.getTransactionById(id); + } + + /** + * Get all transactions within the specified range [start, start + size), ordered by fee. + * @param {Number} start + * @param {Number} size + * @return {(Array|void)} array of serialized transaction hex strings + */ + public getTransactions(start, size) { + return this.getTransactionsData(start, size, "serialized"); + } + + /** + * Get all transactions within the specified range [start, start + size). + * @param {Number} start + * @param {Number} size + * @return {Array} array of transactions IDs in the specified range + */ + public getTransactionIdsForForging(start, size) { + return this.getTransactionsData(start, size, "id"); + } + + /** + * Get data from all transactions within the specified range [start, start + size). + * Transactions are ordered by fee (highest fee first) or by + * insertion time, if fees equal (earliest transaction first). + * @param {Number} start + * @param {Number} size + * @param {String} property + * @return {Array} array of transaction[property] + */ + public getTransactionsData(start, size, property) { + this.__purgeExpired(); + + const data = []; + + let i = 0; + for (const memPoolTransaction of this.mem.getTransactionsOrderedByFee()) { + if (i >= start + size) { + break; + } + + if (i >= start) { + assert.notStrictEqual(memPoolTransaction.transaction[property], undefined); + data.push(memPoolTransaction.transaction[property]); + } + + i++; + } + + return data; + } + + /** + * Remove all transactions from the transaction pool belonging to specific sender. + * @param {String} senderPublicKey + * @return {void} + */ + public removeTransactionsForSender(senderPublicKey) { + this.mem.getBySender(senderPublicKey).forEach(e => this.removeTransactionById(e.transaction.id)); + } + + /** + * Check whether sender of transaction has exceeded max transactions in queue. + * @param {Transaction} transaction + * @return {Boolean} true if exceeded + */ + public hasExceededMaxTransactions(transaction) { + this.__purgeExpired(); + + if (this.options.allowedSenders.includes(transaction.senderPublicKey)) { + if (!this.loggedAllowedSenders.includes(transaction.senderPublicKey)) { + logger.debug( + `Transaction pool: allowing sender public key: ${ + transaction.senderPublicKey + } (listed in options.allowedSenders), thus skipping throttling.`, + ); + this.loggedAllowedSenders.push(transaction.senderPublicKey); + } + + return false; + } + + const count = this.mem.getBySender(transaction.senderPublicKey).size; + + return !(count <= this.options.maxTransactionsPerSender); + } + + /** + * Flush the pool (delete all transactions from it). + * @return {void} + */ + public flush() { + this.mem.flush(); + + this.storage.deleteAll(); + } + + /** + * Checks if a transaction exists in the pool. + * @param {String} transactionId + * @return {Boolean} + */ + public transactionExists(transactionId) { + if (!this.mem.transactionExists(transactionId)) { + // If it does not exist then no need to purge expired transactions because + // we know it will not exist after purge too. + return false; + } + + this.__purgeExpired(); + + return this.mem.transactionExists(transactionId); + } + + /** + * Check if transaction sender is blocked + * @param {String} senderPublicKey + * @return {Boolean} + */ + public isSenderBlocked(senderPublicKey) { + if (!this.blockedByPublicKey[senderPublicKey]) { + return false; + } + + if (this.blockedByPublicKey[senderPublicKey] < dayjs()) { + delete this.blockedByPublicKey[senderPublicKey]; + return false; + } + + return true; + } + + /** + * Blocks sender for a specified time + * @param {String} senderPublicKey + * @return {Time} blockReleaseTime + */ + public blockSender(senderPublicKey) { + const blockReleaseTime = dayjs().add(1, "hour"); + + this.blockedByPublicKey[senderPublicKey] = blockReleaseTime; + + logger.warn(`Sender ${senderPublicKey} blocked until ${this.blockedByPublicKey[senderPublicKey]} :stopwatch:`); + + return blockReleaseTime; + } + + /** + * Processes recently accepted block by the blockchain. + * It removes block transaction from the pool and adjusts + * pool wallets for non existing transactions. + * + * @param {Object} block + * @return {void} + */ + public acceptChainedBlock(block) { + for (const { data } of block.transactions) { + const exists = this.transactionExists(data.id); + const senderPublicKey = data.senderPublicKey; + + const senderWallet = this.walletManager.exists(senderPublicKey) + ? this.walletManager.findByPublicKey(senderPublicKey) + : false; + + const recipientWallet = this.walletManager.exists(data.recipientId) + ? this.walletManager.findByAddress(data.recipientId) + : false; + + if (recipientWallet) { + recipientWallet.applyTransactionToRecipient(data); + } + + if (exists) { + this.removeTransaction(data); + } else if (senderWallet) { + const errors = []; + if (senderWallet.canApply(data, errors)) { + senderWallet.applyTransactionToSender(data); + } else { + this.purgeByPublicKey(data.senderPublicKey); + this.blockSender(data.senderPublicKey); + + logger.error( + `CanApply transaction test failed on acceptChainedBlock() in transaction pool for transaction id:${ + data.id + } due to ${JSON.stringify(errors)}. Possible double spending attack :bomb:`, + ); + } + } + + if ( + senderWallet && + this.walletManager.canBePurged(senderWallet) && + this.getSenderSize(senderPublicKey) === 0 + ) { + this.walletManager.deleteWallet(senderPublicKey); + } + } + + // if delegate in poll wallet manager - apply rewards and fees + if (this.walletManager.exists(block.data.generatorPublicKey)) { + const delegateWallet = this.walletManager.findByPublicKey(block.data.generatorPublicKey); + const increase = block.data.reward.plus(block.data.totalFee); + delegateWallet.balance = delegateWallet.balance.plus(increase); + } + + app.resolve("state").removeCachedTransactionIds(block.transactions.map(tx => tx.id)); + } + + /** + * Rebuild pool manager wallets + * Removes all the wallets from pool manager and applies transaction from pool - if any + * It waits for the node to sync, and then check the transactions in pool + * and validates them and apply to the pool manager. + * @return {void} + */ + public async buildWallets() { + this.walletManager.reset(); + const poolTransactionIds = await this.getTransactionIdsForForging(0, this.getPoolSize()); + + app.resolve("state").removeCachedTransactionIds(poolTransactionIds); + + poolTransactionIds.forEach(transactionId => { + const transaction = this.getTransaction(transactionId); + if (!transaction) { + return; + } + + const senderWallet = this.walletManager.findByPublicKey(transaction.senderPublicKey); + const errors = []; + if (senderWallet && senderWallet.canApply(transaction, errors)) { + senderWallet.applyTransactionToSender(transaction); + } else { + logger.error(`BuildWallets from pool: ${JSON.stringify(errors)}`); + this.purgeByPublicKey(transaction.senderPublicKey); + } + }); + logger.info("Transaction Pool Manager build wallets complete"); + } + + public purgeByPublicKey(senderPublicKey) { + logger.debug(`Purging sender: ${senderPublicKey} from pool wallet manager`); + + this.removeTransactionsForSender(senderPublicKey); + + this.walletManager.deleteWallet(senderPublicKey); + } + + /** + * Purges all transactions from senders with at least one + * invalid transaction. + * @param {Block} block + */ + public purgeSendersWithInvalidTransactions(block) { + const publicKeys = new Set(block.transactions.filter(tx => !tx.verified).map(tx => tx.senderPublicKey)); + + publicKeys.forEach(publicKey => this.purgeByPublicKey(publicKey)); + } + + /** + * Purges all transactions from the block. + * Purges if transaction exists. It assumes that if trx exists that also wallet exists in pool + * @param {Block} block + */ + public purgeBlock(block) { + block.transactions.forEach(tx => { + if (this.transactionExists(tx.id)) { + this.removeTransaction(tx); + this.walletManager.findByPublicKey(tx.senderPublicKey).revertTransactionForSender(tx); + } + }); + } + + /** + * Check whether a given sender has any transactions of the specified type + * in the pool. + * @param {String} senderPublicKey public key of the sender + * @param {Number} transactionType transaction type, must be one of + * TransactionTypes.* and is compared against transaction.type. + * @return {Boolean} true if exist + */ + public senderHasTransactionsOfType(senderPublicKey, transactionType) { + this.__purgeExpired(); + + for (const memPoolTransaction of this.mem.getBySender(senderPublicKey)) { + if (memPoolTransaction.transaction.type === transactionType) { + return true; + } + } + + return false; + } + + /** + * Sync the in-memory storage to the persistent (on-disk) storage if too + * many changes have been accumulated in-memory. + * @return {void} + */ + public __syncToPersistentStorageIfNecessary() { + if (this.options.syncInterval <= this.mem.getNumberOfDirty()) { + this.__syncToPersistentStorage(); + } + } + + /** + * Sync the in-memory storage to the persistent (on-disk) storage. + */ + public __syncToPersistentStorage() { + const added = this.mem.getDirtyAddedAndForget(); + this.storage.bulkAdd(added); + + const removed = this.mem.getDirtyRemovedAndForget(); + this.storage.bulkRemoveById(removed); + } + + /** + * Create an error object which the TransactionGuard understands. + * @param {Transaction} transaction + * @param {String} type + * @param {String} message + * @return {Object} + */ + public __createError(transaction, type, message) { + return { + transaction, + type, + message, + success: false, + }; + } + + /** + * Remove all transactions from the pool that have expired. + * @return {void} + */ + private __purgeExpired() { + for (const transaction of this.mem.getExpired(this.options.maxTransactionAge)) { + emitter.emit("transaction.expired", transaction.data); + + this.walletManager.revertTransactionForSender(transaction); + this.mem.remove(transaction.id, transaction.senderPublicKey); + this.__syncToPersistentStorageIfNecessary(); + } + } +} diff --git a/packages/core-transaction-pool/src/defaults.ts b/packages/core-transaction-pool/src/defaults.ts new file mode 100644 index 0000000000..55c87d370d --- /dev/null +++ b/packages/core-transaction-pool/src/defaults.ts @@ -0,0 +1,30 @@ +export const defaults = { + enabled: !process.env.CORE_TRANSACTION_POOL_DISABLED, + syncInterval: 512, + storage: `${process.env.CORE_PATH_DATA}/transaction-pool.sqlite`, + // When the pool contains that many transactions, then a new transaction is + // only accepted if its fee is higher than the transaction with the lowest + // fee in the pool. In this case the transaction with the lowest fee is removed + // from the pool in order to accommodate the new one. + maxTransactionsInPool: process.env.CORE_MAX_TRANSACTIONS_IN_POOL || 100000, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + maxTransactionsPerRequest: process.env.CORE_TRANSACTION_POOL_MAX_PER_REQUEST || 40, + maxTransactionAge: 21600, + dynamicFees: { + enabled: true, + minFeePool: 3000, + minFeeBroadcast: 3000, + addonBytes: { + transfer: 100, + secondSignature: 250, + delegateRegistration: 400000, + vote: 100, + multiSignature: 500, + ipfs: 250, + timelockTransfer: 500, + multiPayment: 500, + delegateResignation: 400000, + }, + }, +}; diff --git a/packages/core-transaction-pool/src/dynamic-fee/index.ts b/packages/core-transaction-pool/src/dynamic-fee/index.ts new file mode 100644 index 0000000000..9bf385052f --- /dev/null +++ b/packages/core-transaction-pool/src/dynamic-fee/index.ts @@ -0,0 +1,3 @@ +import { calculateFee, dynamicFeeMatcher } from "./matcher"; + +export { calculateFee, dynamicFeeMatcher }; diff --git a/packages/core-transaction-pool/src/dynamic-fee/matcher.ts b/packages/core-transaction-pool/src/dynamic-fee/matcher.ts new file mode 100644 index 0000000000..efb2e24a9a --- /dev/null +++ b/packages/core-transaction-pool/src/dynamic-fee/matcher.ts @@ -0,0 +1,104 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { constants, feeManager, formatArktoshi } from "@arkecosystem/crypto"; +import camelCase from "lodash/camelCase"; +import { config as localConfig } from "../config"; + +/** + * Calculate minimum fee of a transaction for entering the pool. + * @param {Number} Minimum fee ARKTOSHI/byte + * @param {Transaction} Transaction for which we calculate the fee + * @returns {Number} Calculated minimum acceptable fee in ARKTOSHI + */ +export function calculateFee(arktoshiPerByte, transaction) { + if (arktoshiPerByte <= 0) { + arktoshiPerByte = 1; + } + + const addonBytes = localConfig.get("dynamicFees.addonBytes")[ + camelCase(constants.TransactionTypes[transaction.type]) + ]; + + // serialized is in hex + const transactionSizeInBytes = transaction.serialized.length / 2; + + return (addonBytes + transactionSizeInBytes) * arktoshiPerByte; +} + +/** + * Determine if a transaction's fee meets the minimum requirements for broadcasting + * and for entering the transaction pool. + * @param {Transaction} Transaction - transaction to check + * @return {Object} { broadcast: Boolean, enterPool: Boolean } + */ +export function dynamicFeeMatcher(transaction) { + const logger = app.resolvePlugin("logger"); + + const fee = +transaction.fee.toFixed(); + const id = transaction.id; + + const dynamicFees = localConfig.get("dynamicFees"); + + let broadcast; + let enterPool; + + if (dynamicFees.enabled) { + const minFeeBroadcast = calculateFee(dynamicFees.minFeeBroadcast, transaction); + + if (fee >= minFeeBroadcast) { + broadcast = true; + logger.debug( + `Transaction ${id} eligible for broadcast - fee of ${formatArktoshi(fee)} is ${ + fee === minFeeBroadcast ? "equal to" : "greater than" + } minimum fee (${formatArktoshi(minFeeBroadcast)})`, + ); + } else { + broadcast = false; + logger.debug( + `Transaction ${id} not eligible for broadcast - fee of ${formatArktoshi( + fee, + )} is smaller than minimum fee (${formatArktoshi(minFeeBroadcast)})`, + ); + } + + const minFeePool = calculateFee(dynamicFees.minFeePool, transaction); + if (fee >= minFeePool) { + enterPool = true; + logger.debug( + `Transaction ${id} eligible to enter pool - fee of ${formatArktoshi(fee)} is ${ + fee === minFeePool ? "equal to" : "greater than" + } minimum fee (${formatArktoshi(minFeePool)})`, + ); + } else { + enterPool = false; + logger.debug( + `Transaction ${id} not eligible to enter pool - fee of ${formatArktoshi( + fee, + )} is smaller than minimum fee (${formatArktoshi(minFeePool)})`, + ); + } + } else { + // Static fees + const staticFee = feeManager.getForTransaction(transaction); + + if (fee === staticFee) { + broadcast = true; + enterPool = true; + logger.debug( + `Transaction ${id} eligible for broadcast and to enter pool - fee of ${formatArktoshi( + fee, + )} is equal to static fee (${formatArktoshi(staticFee)})`, + ); + } else { + broadcast = false; + enterPool = false; + logger.debug( + `Transaction ${id} not eligible for broadcast and not eligible to enter pool - fee of ${formatArktoshi( + fee, + )} does not match static fee (${formatArktoshi(staticFee)})`, + ); + } + } + + return { broadcast, enterPool }; +} diff --git a/packages/core-transaction-pool/src/guard.ts b/packages/core-transaction-pool/src/guard.ts new file mode 100644 index 0000000000..9aa58c0c00 --- /dev/null +++ b/packages/core-transaction-pool/src/guard.ts @@ -0,0 +1,340 @@ +import { app } from "@arkecosystem/core-container"; +import { Database, Logger, TransactionPool as transactionPool } from "@arkecosystem/core-interfaces"; +import { configManager, constants, models, slots } from "@arkecosystem/crypto"; +import pluralize from "pluralize"; +import { TransactionPool } from "./connection"; +import { dynamicFeeMatcher } from "./dynamic-fee"; +import { MemPoolTransaction } from "./mem-pool-transaction"; +import { isRecipientOnActiveNetwork } from "./utils/is-on-active-network"; + +const { TransactionTypes } = constants; +const { Transaction } = models; + +export class TransactionGuard implements transactionPool.ITransactionGuard { + public transactions: models.Transaction[] = []; + public excess: string[] = []; + public accept: Map = new Map(); + public broadcast: Map = new Map(); + public invalid: Map = new Map(); + public errors: { [key: string]: transactionPool.TransactionErrorDTO[] } = {}; + + /** + * Create a new transaction guard instance. + * @param {TransactionPoolInterface} pool + * @return {void} + */ + constructor(private pool: TransactionPool) {} + + /** + * Validate the specified transactions and accepted transactions to the pool. + * @param {Array} transactions + * @return Object { + * accept: array of transaction ids that qualify for entering the pool + * broadcast: array of of transaction ids that qualify for broadcasting + * invalid: array of invalid transaction ids + * excess: array of transaction ids that exceed sender's quota in the pool + * errors: Object with + * keys=transaction id (for each element in invalid[]), + * value=[ { type, message }, ... ] + * } + */ + public async validate(transactions: models.Transaction[]): Promise { + this.pool.loggedAllowedSenders = []; + + // Cache transactions + this.transactions = this.__cacheTransactions(transactions); + + if (this.transactions.length > 0) { + // Filter transactions and create Transaction instances from accepted ones + this.__filterAndTransformTransactions(this.transactions); + + // Remove already forged tx... Not optimal here + await this.__removeForgedTransactions(); + + // Add transactions to the pool + this.__addTransactionsToPool(); + + this.__printStats(); + } + + return { + accept: Array.from(this.accept.keys()), + broadcast: Array.from(this.broadcast.keys()), + invalid: Array.from(this.invalid.keys()), + excess: this.excess, + errors: Object.keys(this.errors).length > 0 ? this.errors : null, + }; + } + + /** + * Cache the given transactions and return which got added. Already cached + * transactions are not returned. + * @return {Array} + */ + public __cacheTransactions(transactions) { + const { added, notAdded } = app.resolve("state").cacheTransactions(transactions); + + notAdded.forEach(transaction => { + if (!this.errors[transaction.id]) { + this.__pushError(transaction, "ERR_DUPLICATE", "Already in cache."); + } + }); + + return added; + } + + /** + * Get broadcast transactions. + * @return {Array} + */ + public getBroadcastTransactions(): models.Transaction[] { + return Array.from(this.broadcast.values()); + } + + /** + * Transforms and filters incoming transactions. + * It skips: + * - transactions already in the pool + * - transactions from blocked senders + * - transactions from the future + * - dynamic fee mismatch + * - transactions based on type specific restrictions + * - not valid crypto transactions + * @param {Array} transactions + * @return {void} + */ + public __filterAndTransformTransactions(transactions) { + transactions.forEach(transaction => { + const exists = this.pool.transactionExists(transaction.id); + + if (exists) { + this.__pushError(transaction, "ERR_DUPLICATE", `Duplicate transaction ${transaction.id}`); + } else if (this.pool.isSenderBlocked(transaction.senderPublicKey)) { + this.__pushError( + transaction, + "ERR_SENDER_BLOCKED", + `Transaction ${transaction.id} rejected. Sender ${transaction.senderPublicKey} is blocked.`, + ); + } else if (this.pool.hasExceededMaxTransactions(transaction)) { + this.excess.push(transaction.id); + } else if (this.__validateTransaction(transaction)) { + try { + const trx = new Transaction(transaction); + if (trx.verified) { + const dynamicFee = dynamicFeeMatcher(trx); + + if (!dynamicFee.enterPool && !dynamicFee.broadcast) { + this.__pushError( + transaction, + "ERR_LOW_FEE", + "The fee is too low to broadcast and accept the transaction", + ); + } else { + if (dynamicFee.enterPool) { + this.accept.set(trx.id, trx); + } + + if (dynamicFee.broadcast) { + this.broadcast.set(trx.id, trx); + } + } + } else { + this.__pushError( + transaction, + "ERR_BAD_DATA", + "Transaction didn't pass the verification process.", + ); + } + } catch (error) { + this.__pushError(transaction, "ERR_UNKNOWN", error.message); + } + } + }); + } + + /** + * Determines valid transactions by checking rules, according to: + * - transaction timestamp + * - wallet balance + * - network if set + * - transaction type specifics: + * - if recipient is on the same network + * - if sender already has another transaction of the same type, for types that + * - only allow one transaction at a time in the pool (e.g. vote) + */ + public __validateTransaction(transaction) { + const now = slots.getTime(); + if (transaction.timestamp > now + 3600) { + const secondsInFuture = transaction.timestamp - now; + this.__pushError( + transaction, + "ERR_FROM_FUTURE", + `Transaction ${transaction.id} is ${secondsInFuture} seconds in the future`, + ); + return false; + } + + const errors = []; + + // This check must come before canApply otherwise a wallet may be incorrectly assigned a username when multiple + // conflicting delegate registrations for the same username exist in the same transaction payload + if (transaction.type === TransactionTypes.DelegateRegistration) { + const username = transaction.asset.delegate.username; + const delegateRegistrationsInPayload = this.transactions.filter( + tx => tx.type === TransactionTypes.DelegateRegistration && tx.asset.delegate.username === username, + ); + if (delegateRegistrationsInPayload.length > 1) { + this.__pushError( + transaction, + "ERR_CONFLICT", + `Multiple delegate registrations for "${username}" in transaction payload`, + ); + return false; + } + + const delegateRegistrationsInPool: MemPoolTransaction[] = Array.from( + this.pool.getTransactionsByType(TransactionTypes.DelegateRegistration), + ); + if (delegateRegistrationsInPool.some(memTx => memTx.transaction.asset.delegate.username === username)) { + this.__pushError( + transaction, + "ERR_PENDING", + `Delegate registration for "${username}" already in the pool`, + ); + return false; + } + } + + if (!this.pool.walletManager.canApply(transaction, errors)) { + this.__pushError(transaction, "ERR_APPLY", JSON.stringify(errors)); + return false; + } + + if (transaction.network && transaction.network !== configManager.get("pubKeyHash")) { + this.__pushError( + transaction, + "ERR_WRONG_NETWORK", + `Transaction network '${transaction.network}' does not match '${configManager.get("pubKeyHash")}'`, + ); + return false; + } + + switch (transaction.type) { + case TransactionTypes.Transfer: + if (!isRecipientOnActiveNetwork(transaction)) { + this.__pushError( + transaction, + "ERR_INVALID_RECIPIENT", + `Recipient ${transaction.recipientId} is not on the same network: ${configManager.get( + "pubKeyHash", + )}`, + ); + return false; + } + break; + case TransactionTypes.SecondSignature: + case TransactionTypes.DelegateRegistration: + case TransactionTypes.Vote: + if (this.pool.senderHasTransactionsOfType(transaction.senderPublicKey, transaction.type)) { + this.__pushError( + transaction, + "ERR_PENDING", + `Sender ${transaction.senderPublicKey} already has a transaction of type ` + + `'${TransactionTypes[transaction.type]}' in the pool`, + ); + return false; + } + break; + case TransactionTypes.MultiSignature: + case TransactionTypes.Ipfs: + case TransactionTypes.TimelockTransfer: + case TransactionTypes.MultiPayment: + case TransactionTypes.DelegateResignation: + default: + this.__pushError( + transaction, + "ERR_UNSUPPORTED", + "Invalidating transaction of unsupported type " + `'${TransactionTypes[transaction.type]}'`, + ); + return false; + } + + return true; + } + + /** + * Remove already forged transactions. + * @return {void} + */ + public async __removeForgedTransactions() { + const databaseService = app.resolvePlugin("database"); + + const forgedIdsSet = await databaseService.getForgedTransactionsIds([ + ...new Set([...this.accept.keys(), ...this.broadcast.keys()]), + ]); + + app.resolve("state").removeCachedTransactionIds(forgedIdsSet); + + forgedIdsSet.forEach(id => { + this.__pushError(this.accept.get(id), "ERR_FORGED", "Already forged."); + + this.accept.delete(id); + this.broadcast.delete(id); + }); + } + + /** + * Add accepted transactions to the pool and filter rejected ones. + * @return {void} + */ + public __addTransactionsToPool() { + // Add transactions to the transaction pool + const { added, notAdded } = this.pool.addTransactions(Array.from(this.accept.values())); + + // Exclude transactions which were refused from the pool + notAdded.forEach(item => { + this.accept.delete(item.transaction.id); + + // The transaction should still be broadcasted if the pool is full + if (item.type !== "ERR_POOL_FULL") { + this.broadcast.delete(item.transaction.id); + } + + this.__pushError(item.transaction, item.type, item.message); + }); + } + + /** + * Adds a transaction to the errors object. The transaction id is mapped to an + * array of errors. There may be multiple errors associated with a transaction in + * which case __pushError is called multiple times. + * @param {Transaction} transaction + * @param {String} type + * @param {String} message + * @return {void} + */ + public __pushError(transaction, type, message) { + if (!this.errors[transaction.id]) { + this.errors[transaction.id] = []; + } + + this.errors[transaction.id].push({ type, message }); + + this.invalid.set(transaction.id, transaction); + } + + /** + * Print compact transaction stats. + * @return {void} + */ + public __printStats() { + const properties = ["accept", "broadcast", "excess", "invalid"]; + const stats = properties + .map(prop => `${prop}: ${this[prop] instanceof Array ? this[prop].length : this[prop].size}`) + .join(" "); + + app.resolvePlugin("logger").info( + `Received ${pluralize("transaction", this.transactions.length, true)} (${stats}).`, + ); + } +} diff --git a/packages/core-transaction-pool/src/index.ts b/packages/core-transaction-pool/src/index.ts new file mode 100644 index 0000000000..5bc6a74484 --- /dev/null +++ b/packages/core-transaction-pool/src/index.ts @@ -0,0 +1,6 @@ +export * from "./guard"; +export * from "./connection"; +export * from "./config"; +export * from "./manager"; +export * from "./pool-wallet-manager"; +export * from "./plugin"; diff --git a/packages/core-transaction-pool/src/manager.ts b/packages/core-transaction-pool/src/manager.ts new file mode 100644 index 0000000000..57af9922bc --- /dev/null +++ b/packages/core-transaction-pool/src/manager.ts @@ -0,0 +1,32 @@ +class TransactionPoolManager { + private connections: { [key: string]: any }; + + /** + * Create a new transaction pool manager instance. + * @constructor + */ + constructor() { + this.connections = {}; + } + + /** + * Get a transaction pool instance. + * @param {String} name + * @return {TransactionPoolInterface} + */ + public connection(name = "default") { + return this.connections[name]; + } + + /** + * Make the transaction pool instance. + * @param {TransactionPoolInterface} connection + * @param {String} name + * @return {void} + */ + public async makeConnection(connection, name = "default") { + this.connections[name] = await connection.make(); + } +} + +export const transactionPoolManager = new TransactionPoolManager(); diff --git a/packages/core-transaction-pool/src/mem-pool-transaction.ts b/packages/core-transaction-pool/src/mem-pool-transaction.ts new file mode 100644 index 0000000000..7ca0e015ef --- /dev/null +++ b/packages/core-transaction-pool/src/mem-pool-transaction.ts @@ -0,0 +1,69 @@ +// tslint:disable:variable-name + +import { constants, models } from "@arkecosystem/crypto"; +import assert from "assert"; + +const { TransactionTypes } = constants; +const { Transaction } = models; + +/** + * A mem pool transaction. + * A normal transaction + * + a sequence number used to order by insertion time + * + a get-expiration-time method used to remove old transactions from the pool + */ +export class MemPoolTransaction { + private _transaction: any; + private _sequence: number; + + /** + * Construct a MemPoolTransaction object. + * @param {Transaction} transaction base transaction object + * @param {Number} sequence insertion order sequence or undefined; + * if this is undefined at creation time, + * then it is assigned later using the + * setter method below + */ + constructor(transaction, sequence?) { + assert(transaction instanceof Transaction); + this._transaction = transaction; + + if (sequence !== undefined) { + assert(Number.isInteger(sequence)); + this._sequence = sequence; + } + } + + get transaction() { + return this._transaction; + } + + get sequence() { + return this._sequence; + } + + set sequence(seq) { + assert.strictEqual(this._sequence, undefined); + this._sequence = seq; + } + + /** + * Derive the transaction expiration time in number of seconds since + * the genesis block. + * @param {Number} maxTransactionAge maximum age (in seconds) of a transaction + * @return {Number} expiration time or null if the transaction does not expire + */ + public expireAt(maxTransactionAge) { + const t = this._transaction; + + if (t.expiration > 0) { + return t.expiration; + } + + if (t.type !== TransactionTypes.TimelockTransfer) { + return t.timestamp + maxTransactionAge; + } + + return null; + } +} diff --git a/packages/core-transaction-pool/src/mem.ts b/packages/core-transaction-pool/src/mem.ts new file mode 100644 index 0000000000..2c5aaa090c --- /dev/null +++ b/packages/core-transaction-pool/src/mem.ts @@ -0,0 +1,359 @@ +import { slots } from "@arkecosystem/crypto"; +import assert from "assert"; +import { MemPoolTransaction } from "./mem-pool-transaction"; + +export class Mem { + public sequence: number; + public all: MemPoolTransaction[]; + public allIsSorted: boolean; + public byId: { [key: string]: MemPoolTransaction }; + public bySender: { [key: string]: Set }; + public byType: { [key: number]: Set }; + public byExpiration: MemPoolTransaction[]; + public byExpirationIsSorted: boolean; + public dirty: { added: Set; removed: Set }; + + /** + * Create the in-memory transaction pool structures. + */ + constructor() { + /** + * A monotonically increasing number, assigned to each new transaction and + * then incremented. + * Used to: + * - keep insertion order. + */ + this.sequence = 0; + + /** + * An array of MemPoolTransaction sorted by fee (the transaction with the + * highest fee is first). If the fee is equal, they are sorted by insertion + * order. + * Used to: + * - get the transactions with the highest fee + * - get the number of all transactions in the pool + */ + this.all = []; + + /** + * A boolean flag indicating whether `this.all` is indeed sorted or + * temporarily left unsorted. We use lazy sorting of `this.all`: + * - insertion just appends at the end (O(1)) + flag it as unsorted + * - deletion removes by using splice() (O(n)) + flag it as unsorted + * - lookup sorts if it is not sorted (O(n*log(n)) + flag it as sorted + */ + this.allIsSorted = true; + + /** + * A map of (key=transaction id, value=MemPoolTransaction). + * Used to: + * - get a transaction, given its ID + */ + this.byId = {}; + + /** + * A map of (key=sender public key, value=Set of MemPoolTransaction). + * Used to: + * - get all transactions from a given sender + * - get the number of all transactions from a given sender. + */ + this.bySender = {}; + + /** + * A map of (key=transaction type, value=Set of MemPoolTransaction). + * Used to: + * - get all transactions of a given type + */ + this.byType = {}; + + /** + * An array of MemPoolTransaction, sorted by expiration (earliest date + * comes first). This array may not contain all transactions that are + * in the pool, transactions that are without expiration are not included. + * Used to: + * - find all transactions that have expired (have an expiration date + * earlier than a given date) - they are at the beginning of the array. + */ + this.byExpiration = []; + this.byExpirationIsSorted = true; + + /** + * List of dirty transactions ids (that are not saved in the on-disk + * database yet). Used to delay and group operations to the on-disk database. + */ + this.dirty = { + added: new Set(), + removed: new Set(), + }; + } + + /** + * Add a transaction. + * @param {MemPoolTransaction} memPoolTransaction transaction to add + * @param {Number} maxTransactionAge maximum age of a transaction in seconds + * @param {Boolean} thisIsDBLoad if true, then this is the initial + * loading from the database and we do + * not need to schedule the transaction + * that is being added for saving to disk + */ + public add(memPoolTransaction, maxTransactionAge, thisIsDBLoad = false) { + const transaction = memPoolTransaction.transaction; + + assert.strictEqual(this.byId[transaction.id], undefined); + + if (thisIsDBLoad) { + // Sequence is provided from outside, make sure we avoid duplicates + // later when we start using our this.sequence. + assert.strictEqual(typeof memPoolTransaction.sequence, "number"); + this.sequence = Math.max(this.sequence, memPoolTransaction.sequence) + 1; + } else { + // Sequence should only be set during DB load (when sequences come + // from the database). In other scenarios sequence is not set and we + // set it here. + memPoolTransaction.sequence = this.sequence++; + } + + this.all.push(memPoolTransaction); + this.allIsSorted = false; + + this.byId[transaction.id] = memPoolTransaction; + + const sender = transaction.senderPublicKey; + const type = transaction.type; + + if (this.bySender[sender] === undefined) { + // First transaction from this sender, create a new Set. + this.bySender[sender] = new Set([memPoolTransaction]); + } else { + // Append to existing transaction ids for this sender. + this.bySender[sender].add(memPoolTransaction); + } + + if (this.byType[type] === undefined) { + // First transaction of this type, create a new Set. + this.byType[type] = new Set([memPoolTransaction]); + } else { + // Append to existing transaction ids for this type. + this.byType[type].add(memPoolTransaction); + } + + if (memPoolTransaction.expireAt(maxTransactionAge) !== null) { + this.byExpiration.push(memPoolTransaction); + this.byExpirationIsSorted = false; + } + + if (!thisIsDBLoad) { + if (this.dirty.removed.has(transaction.id)) { + // If the transaction has been already in the pool and has been removed + // and the removal has not propagated to disk yet, just wipe it from the + // list of removed transactions, so that the old copy stays on disk. + this.dirty.removed.delete(transaction.id); + } else { + this.dirty.added.add(transaction.id); + } + } + } + + /** + * Remove a transaction. + * @param {String} id id of the transaction to remove + * @param {String} senderPublicKey public key of the sender, could be undefined + */ + public remove(id, senderPublicKey) { + if (this.byId[id] === undefined) { + // Not found, not in pool + return; + } + + if (senderPublicKey === undefined) { + senderPublicKey = this.byId[id].transaction.senderPublicKey; + } + + const memPoolTransaction = this.byId[id]; + const type = this.byId[id].transaction.type; + + // XXX worst case: O(n) + let i = this.byExpiration.findIndex(e => e.transaction.id === id); + if (i !== -1) { + this.byExpiration.splice(i, 1); + } + + this.bySender[senderPublicKey].delete(memPoolTransaction); + if (this.bySender[senderPublicKey].size === 0) { + delete this.bySender[senderPublicKey]; + } + + this.byType[type].delete(memPoolTransaction); + if (this.byType[type].size === 0) { + delete this.byType[type]; + } + + delete this.byId[id]; + + i = this.all.findIndex(e => e.transaction.id === id); + assert.notStrictEqual(i, -1); + this.all.splice(i, 1); + this.allIsSorted = false; + + if (this.dirty.added.has(id)) { + // This transaction has been added and deleted without data being synced + // to disk in between, so it will never touch the disk, just remove it + // from the added list. + this.dirty.added.delete(id); + } else { + this.dirty.removed.add(id); + } + } + + /** + * Get the number of transactions. + * @return Number + */ + public getSize() { + return this.all.length; + } + + /** + * Get all transactions from a given sender. + * @param {String} senderPublicKey public key of the sender + * @return {Set of MemPoolTransaction} all transactions for the given sender, could be empty Set + */ + public getBySender(senderPublicKey) { + const memPoolTransactions = this.bySender[senderPublicKey]; + if (memPoolTransactions !== undefined) { + return memPoolTransactions; + } + return new Set(); + } + + /** + * Get all transactions of a given type. + * @param {Number} type of transaction + * @return {Set of MemPoolTransaction} all transactions of the given type, could be empty Set + */ + public getByType(type) { + const memPoolTransactions = this.byType[type]; + if (memPoolTransactions !== undefined) { + return memPoolTransactions; + } + return new Set(); + } + + /** + * Get a transaction, given its id. + * @param {String} id transaction id + * @return {Transaction|undefined} + */ + public getTransactionById(id) { + if (this.byId[id] === undefined) { + return undefined; + } + return this.byId[id].transaction; + } + + /** + * Get an array of all transactions ordered by fee. + * Transactions are ordered by fee (highest fee first) or by + * insertion time, if fees equal (earliest transaction first). + * @return {Array of MemPoolTransaction} transactions + */ + public getTransactionsOrderedByFee() { + if (!this.allIsSorted) { + this.all.sort((a, b) => { + if (a.transaction.fee.isGreaterThan(b.transaction.fee)) { + return -1; + } + if (a.transaction.fee.isLessThan(b.transaction.fee)) { + return 1; + } + return a.sequence - b.sequence; + }); + this.allIsSorted = true; + } + + return this.all; + } + + /** + * Check if a transaction with a given id exists. + * @param {String} id transaction id + * @return {Boolean} true if exists + */ + public transactionExists(id) { + return this.byId[id] !== undefined; + } + + /** + * Get the expired transactions. + * @param {Number} maxTransactionAge maximum age of a transaction in seconds + * @return {Array of Transaction} expired transactions + */ + public getExpired(maxTransactionAge) { + if (!this.byExpirationIsSorted) { + this.byExpiration.sort((a, b) => a.expireAt(maxTransactionAge) - b.expireAt(maxTransactionAge)); + this.byExpirationIsSorted = true; + } + + const now = slots.getTime(); + + const transactions = []; + + for (const memPoolTransaction of this.byExpiration) { + if (memPoolTransaction.expireAt(maxTransactionAge) <= now) { + transactions.push(memPoolTransaction.transaction); + } else { + break; + } + } + + return transactions; + } + + /** + * Remove all transactions. + */ + public flush() { + this.all = []; + this.allIsSorted = true; + this.byId = {}; + this.bySender = {}; + this.byExpiration = []; + this.byExpirationIsSorted = true; + this.dirty.added.clear(); + this.dirty.removed.clear(); + } + + /** + * Get the number of dirty transactions (added or removed, but those additions or + * removals have not been applied to the persistent storage). + * @return {Number} number of dirty transactions + */ + public getNumberOfDirty() { + return this.dirty.added.size + this.dirty.removed.size; + } + + /** + * Get the dirty transactions that were added and forget they are dirty. + * In other words, get the transactions that were added since the last + * call to this method (or to the flush() method). + * @return {Array of MemPoolTransaction} + */ + public getDirtyAddedAndForget() { + const added = []; + this.dirty.added.forEach(id => added.push(this.byId[id])); + this.dirty.added.clear(); + return added; + } + + /** + * Get the ids of dirty transactions that were removed and forget them completely. + * In other words, get the transactions that were removed since the last + * call to this method (or to the flush() method). + * @return {Array of String} transaction ids + */ + public getDirtyRemovedAndForget() { + const removed = Array.from(this.dirty.removed); + this.dirty.removed.clear(); + return removed; + } +} diff --git a/packages/core-transaction-pool/src/plugin.ts b/packages/core-transaction-pool/src/plugin.ts new file mode 100644 index 0000000000..8795125ec1 --- /dev/null +++ b/packages/core-transaction-pool/src/plugin.ts @@ -0,0 +1,25 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { config } from "./config"; +import { TransactionPool } from "./connection"; +import { defaults } from "./defaults"; +import { transactionPoolManager } from "./manager"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "transactionPool", + async register(container: Container.IContainer, options) { + config.init(options); + + container.resolvePlugin("logger").info("Connecting to transaction pool"); + + await transactionPoolManager.makeConnection(new TransactionPool(options)); + + return transactionPoolManager.connection(); + }, + async deregister(container: Container.IContainer, options) { + container.resolvePlugin("logger").info("Disconnecting from transaction pool"); + + return transactionPoolManager.connection().disconnect(); + }, +}; diff --git a/packages/core-transaction-pool/src/pool-wallet-manager.ts b/packages/core-transaction-pool/src/pool-wallet-manager.ts new file mode 100644 index 0000000000..167ca5a147 --- /dev/null +++ b/packages/core-transaction-pool/src/pool-wallet-manager.ts @@ -0,0 +1,116 @@ +import { app } from "@arkecosystem/core-container"; +import { WalletManager } from "@arkecosystem/core-database"; +import { Database } from "@arkecosystem/core-interfaces"; +import { constants, crypto, isException, models } from "@arkecosystem/crypto"; + +const { Wallet } = models; +const { TransactionTypes } = constants; + +export class PoolWalletManager extends WalletManager { + public databaseService = app.resolvePlugin("database"); + + /** + * Create a new pool wallet manager instance. + * @constructor + */ + constructor() { + super(); + } + + /** + * Get a wallet by the given address. If wallet is not found it is copied from blockchain + * wallet manager. Method overrides base class method from WalletManager. + * WARNING: call only upon guard apply, as if wallet not found it gets it from blockchain. + * For existing key checks use function exists(key) + * @param {String} address + * @return {(Wallet|null)} + */ + public findByAddress(address) { + if (!this.byAddress[address]) { + const blockchainWallet = this.databaseService.walletManager.findByAddress(address); + const wallet = Object.assign(new Wallet(address), blockchainWallet); // do not modify + + this.reindex(wallet); + } + + return this.byAddress[address]; + } + + public deleteWallet(publicKey) { + this.forgetByPublicKey(publicKey); + this.forgetByAddress(crypto.getAddress(publicKey, this.networkId)); + } + + /** + * Checks if the transaction can be applied. + * @param {Object|Transaction} transaction + * @param {Array} errors The errors are written into the array. + * @return {Boolean} + */ + public canApply(transaction, errors) { + // Edge case if sender is unknown and has no balance. + // NOTE: Check is performed against the database wallet manager. + if (!this.databaseService.walletManager.exists(transaction.senderPublicKey)) { + const senderAddress = crypto.getAddress(transaction.senderPublicKey, this.networkId); + + if (this.databaseService.walletManager.findByAddress(senderAddress).balance.isZero()) { + errors.push("Cold wallet is not allowed to send until receiving transaction is confirmed."); + return false; + } + } + + const sender = this.findByPublicKey(transaction.senderPublicKey); + const { type, asset } = transaction; + + if ( + type === TransactionTypes.DelegateRegistration && + this.databaseService.walletManager.findByUsername(asset.delegate.username.toLowerCase()) + ) { + this.logger.error( + `[PoolWalletManager] Can't apply transaction ${ + transaction.id + }: delegate name already taken. Data: ${JSON.stringify(transaction)}`, + ); + + errors.push(`Can't apply transaction ${transaction.id}: delegate name already taken.`); + // NOTE: We use the vote public key, because vote transactions have the same sender and recipient. + } else if ( + type === TransactionTypes.Vote && + !this.databaseService.walletManager.isDelegate(asset.votes[0].slice(1)) + ) { + this.logger.error( + `[PoolWalletManager] Can't apply vote transaction: delegate ${ + asset.votes[0] + } does not exist. Data: ${JSON.stringify(transaction)}`, + ); + + errors.push(`Can't apply transaction ${transaction.id}: delegate ${asset.votes[0]} does not exist.`); + } else if (isException(transaction)) { + this.logger.warn( + `Transaction forcibly applied because it has been added as an exception: ${transaction.id}`, + ); + } else if (!sender.canApply(transaction, errors)) { + const message = `[PoolWalletManager] Can't apply transaction id:${transaction.id} from sender:${ + sender.address + }`; + this.logger.error(`${message} due to ${JSON.stringify(errors)}`); + errors.unshift(message); + } + + return errors.length === 0; + } + + /** + * Remove the given transaction from a sender only. + * @param {Transaction} transaction + * @return {Transaction} + */ + public revertTransactionForSender(transaction) { + const { data } = transaction; + const sender = this.findByPublicKey(data.senderPublicKey); // Should exist + + sender.revertTransactionForSender(data); + + return data; + } +} diff --git a/packages/core-transaction-pool/src/storage.ts b/packages/core-transaction-pool/src/storage.ts new file mode 100644 index 0000000000..36a1bda3ef --- /dev/null +++ b/packages/core-transaction-pool/src/storage.ts @@ -0,0 +1,114 @@ +import { models } from "@arkecosystem/crypto"; +import BetterSqlite3 from "better-sqlite3"; +import fs from "fs-extra"; +import { MemPoolTransaction } from "./mem-pool-transaction"; + +const { Transaction } = models; + +/** + * A permanent storage (on-disk), supporting some basic functionalities required + * by the transaction pool. + */ +export class Storage { + private table: string; + private db: BetterSqlite3; + + /** + * Construct the storage. + * @param {String} file + */ + constructor(file) { + this.table = "pool"; + + fs.ensureFileSync(file); + + this.db = new BetterSqlite3(file); + + this.db.exec(` + PRAGMA journal_mode=WAL; + CREATE TABLE IF NOT EXISTS ${this.table} ( + "sequence" INTEGER PRIMARY KEY, + "id" VARCHAR(64) UNIQUE, + "serialized" BLOB NOT NULL + ); + `); + } + + /** + * Close the storage. + */ + public close() { + this.db.close(); + this.db = null; + } + + /** + * Add a bunch of new entries to the storage. + * @param {Array of MemPoolTransaction} data new entries to be added + */ + public bulkAdd(data: MemPoolTransaction[]) { + if (data.length === 0) { + return; + } + + const insertStatement = this.db.prepare( + `INSERT INTO ${this.table} ` + "(sequence, id, serialized) VALUES " + "(:sequence, :id, :serialized);", + ); + + try { + this.db.prepare("BEGIN;").run(); + + data.forEach(d => + insertStatement.run({ + sequence: d.sequence, + id: d.transaction.id, + serialized: Buffer.from(d.transaction.serialized, "hex"), + }), + ); + + this.db.prepare("COMMIT;").run(); + } finally { + if (this.db.inTransaction) { + this.db.prepare("ROLLBACK;").run(); + } + } + } + + /** + * Remove a bunch of entries, given their ids. + * @param {Array of String} ids ids of the elements to be removed + */ + public bulkRemoveById(ids: string[]) { + if (ids.length === 0) { + return; + } + + const deleteStatement = this.db.prepare(`DELETE FROM ${this.table} WHERE id = :id;`); + + this.db.prepare("BEGIN;").run(); + + ids.forEach(id => deleteStatement.run({ id })); + + this.db.prepare("COMMIT;").run(); + } + + /** + * Load all entries. + * @return {Array of MemPoolTransaction} + */ + public loadAll(): MemPoolTransaction[] { + const rows = this.db.prepare(`SELECT sequence, lower(HEX(serialized)) AS serialized FROM ${this.table};`).all(); + + return rows + .map(r => ({ tx: new Transaction(r.serialized), ...r })) + .filter(r => r.tx.verified) + .map(r => new MemPoolTransaction(r.tx, r.sequence)); + } + + /** + * Delete all entries. + */ + public deleteAll() { + this.db.exec(`DELETE FROM ${this.table};`); + } +} diff --git a/packages/core-transaction-pool/src/utils/is-on-active-network.ts b/packages/core-transaction-pool/src/utils/is-on-active-network.ts new file mode 100644 index 0000000000..40c32a9707 --- /dev/null +++ b/packages/core-transaction-pool/src/utils/is-on-active-network.ts @@ -0,0 +1,23 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { configManager } from "@arkecosystem/crypto"; +import bs58check from "bs58check"; + +const logger = app.resolvePlugin("logger"); + +/** + * Checks if transaction recipient is on the same network as blockchain + * @param {Transaction} + * @return {Boolean} + */ +export function isRecipientOnActiveNetwork(transaction) { + const recipientPrefix = bs58check.decode(transaction.recipientId).readUInt8(0); + + if (recipientPrefix === configManager.get("pubKeyHash")) { + return true; + } + + logger.error(`Recipient ${transaction.recipientId} is not on the same network: ${configManager.get("pubKeyHash")}`); + + return false; +} diff --git a/packages/core-transaction-pool/tsconfig.json b/packages/core-transaction-pool/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-transaction-pool/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-utils/CHANGELOG.md b/packages/core-utils/CHANGELOG.md deleted file mode 100644 index 0664a2186a..0000000000 --- a/packages/core-utils/CHANGELOG.md +++ /dev/null @@ -1,29 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Added - -- Tests for timestamp formatting -- Helper to create tables in the CLI - -### Changelog - -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Properly calculate delegate approval - -## 0.1.0 - 2018-10-08 - -### Added - -- initial release diff --git a/packages/core-utils/LICENSE b/packages/core-utils/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-utils/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-utils/README.md b/packages/core-utils/README.md index 1e9057172b..f0a6c9a94d 100644 --- a/packages/core-utils/README.md +++ b/packages/core-utils/README.md @@ -14,8 +14,9 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [Joshua Noack](https://github.com/supaiku0) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-utils/__tests__/__support__/mocks/core-container-calculator.ts b/packages/core-utils/__tests__/__support__/mocks/core-container-calculator.ts new file mode 100644 index 0000000000..d895682cbe --- /dev/null +++ b/packages/core-utils/__tests__/__support__/mocks/core-container-calculator.ts @@ -0,0 +1,41 @@ +jest.mock("@arkecosystem/core-container", () => { + return { + app: { + getConfig: () => { + return { + get: () => 1000000 * 1e8, + getMilestone: () => ({ + height: 1, + reward: 2 * 1e8, + }), + genesisBlock: { + totalAmount: 1000000 * 1e8, + }, + }; + }, + resolvePlugin: name => { + if (name === "config") { + return { + getMilestone: () => ({ + height: 1, + reward: 2 * 1e8, + }), + genesisBlock: { + totalAmount: 1000000 * 1e8, + }, + }; + } + + if (name === "blockchain") { + return { + getLastBlock: () => ({ + data: { height: 1 }, + }), + }; + } + + return {}; + }, + }, + }; +}); diff --git a/packages/core-utils/__tests__/__support__/mocks/core-container.ts b/packages/core-utils/__tests__/__support__/mocks/core-container.ts new file mode 100644 index 0000000000..17aa34a5e2 --- /dev/null +++ b/packages/core-utils/__tests__/__support__/mocks/core-container.ts @@ -0,0 +1,14 @@ +jest.mock("@arkecosystem/core-container", () => { + return { + app: { + getConfig: () => { + return { + getMilestone: () => ({ + epoch: "2017-03-21T13:00:00.000Z", + activeDelegates: 51, + }), + }; + }, + }, + }; +}); diff --git a/packages/core-utils/__tests__/bignumify.test.ts b/packages/core-utils/__tests__/bignumify.test.ts new file mode 100644 index 0000000000..baee3f80c2 --- /dev/null +++ b/packages/core-utils/__tests__/bignumify.test.ts @@ -0,0 +1,21 @@ +import { Bignum } from "@arkecosystem/crypto"; +import "jest-extended"; +import { bignumify } from "../src/bignumify"; + +describe("Bignumify", () => { + it("should create a bignumber instance", () => { + expect(bignumify(1)).toBeInstanceOf(Bignum); + }); + + it("should create a fixed number", () => { + expect(bignumify(3.14).toFixed()).toBe("3.14"); + }); + + it("should create a number", () => { + expect(bignumify(3.14).toNumber()).toBe(3.14); + }); + + it("should create a string", () => { + expect(bignumify(3.14).toString()).toBe("3.14"); + }); +}); diff --git a/packages/core-utils/__tests__/delegate-calculator.test.js b/packages/core-utils/__tests__/delegate-calculator.test.js deleted file mode 100644 index 0d4b7c0641..0000000000 --- a/packages/core-utils/__tests__/delegate-calculator.test.js +++ /dev/null @@ -1,84 +0,0 @@ -const { Bignum } = require('@arkecosystem/crypto') -const { Wallet } = require('@arkecosystem/crypto').models -const app = require('@arkecosystem/core-container') -const delegateCalculator = require('../lib/delegate-calculator') - -let delegate - -beforeEach(() => { - delegate = new Wallet('D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7') - Object.entries({ - producedBlocks: 0, - missedBlocks: 0, - }).forEach((key, value) => { - delegate[key] = value - }) -}) - -describe('Delegate Calculator', () => { - describe('calculateApproval', () => { - it('should be a function', () => { - expect(delegateCalculator.calculateApproval).toBeFunction() - }) - - it('should calculate correctly', () => { - delegate.voteBalance = new Bignum(10000 * 1e8) - - app.resolvePlugin = jest.fn(plugin => { - if (plugin === 'config') { - return { - getConstants: () => ({ - height: 1, - reward: 2 * 1e8, - }), - genesisBlock: { - totalAmount: 1000000 * 1e8, - }, - } - } - }) - - expect(delegateCalculator.calculateApproval(delegate, 1)).toBe(1) - }) - - it('should calculate correctly with 2 decimals', () => { - delegate.voteBalance = new Bignum(16500 * 1e8) - - app.resolvePlugin = jest.fn(plugin => { - if (plugin === 'config') { - return { - getConstants: () => ({ - height: 1, - reward: 2 * 1e8, - }), - genesisBlock: { - totalAmount: 1000000 * 1e8, - }, - } - } - }) - - expect(delegateCalculator.calculateApproval(delegate, 1)).toBe(1.65) - }) - }) - - describe('calculateProductivity', () => { - it('should be a function', () => { - expect(delegateCalculator.calculateProductivity).toBeFunction() - }) - - it('should calculate correctly for a value above 0', () => { - delegate.missedBlocks = 10 - delegate.producedBlocks = 100 - - expect(delegateCalculator.calculateProductivity(delegate)).toBe(90.91) - }) - - it('should calculate correctly for a value of 0', () => { - delegate.missedBlocks = 0 - delegate.producedBlocks = 0 - - expect(delegateCalculator.calculateProductivity(delegate)).toBe(0.0) - }) - }) -}) diff --git a/packages/core-utils/__tests__/delegate-calculator.test.ts b/packages/core-utils/__tests__/delegate-calculator.test.ts new file mode 100644 index 0000000000..eef8b99b41 --- /dev/null +++ b/packages/core-utils/__tests__/delegate-calculator.test.ts @@ -0,0 +1,51 @@ +import "./__support__/mocks/core-container-calculator"; + +import { Bignum, models } from "@arkecosystem/crypto"; +import "jest-extended"; +import { calculateApproval, calculateProductivity } from "../src/delegate-calculator"; + +let delegate; + +beforeEach(() => { + delegate = new models.Wallet("D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7"); + delegate.producedBlocks = 0; + delegate.missedBlocks = 0; +}); + +describe("Delegate Calculator", () => { + describe("calculateApproval", () => { + it("should calculate correctly with a height", () => { + delegate.voteBalance = new Bignum(10000 * 1e8); + + expect(calculateApproval(delegate, 1)).toBe(1); + }); + + it("should calculate correctly without a height", () => { + delegate.voteBalance = new Bignum(10000 * 1e8); + + expect(calculateApproval(delegate)).toBe(1); + }); + + it("should calculate correctly with 2 decimals", () => { + delegate.voteBalance = new Bignum(16500 * 1e8); + + expect(calculateApproval(delegate, 1)).toBe(1.65); + }); + }); + + describe("calculateProductivity", () => { + it("should calculate correctly for a value above 0", () => { + delegate.missedBlocks = 10; + delegate.producedBlocks = 100; + + expect(calculateProductivity(delegate)).toBe(90.91); + }); + + it("should calculate correctly for a value of 0", () => { + delegate.missedBlocks = 0; + delegate.producedBlocks = 0; + + expect(calculateProductivity(delegate)).toBe(0.0); + }); + }); +}); diff --git a/packages/core-utils/__tests__/format-timestamp.test.js b/packages/core-utils/__tests__/format-timestamp.test.js deleted file mode 100644 index b6bd1c0e16..0000000000 --- a/packages/core-utils/__tests__/format-timestamp.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const app = require('@arkecosystem/core-container') -const formatTimestamp = require('../lib/format-timestamp') - -app.resolvePlugin = jest.fn(plugin => { - if (plugin === 'config') { - return { - getConstants: () => ({ - epoch: '2017-03-21T13:00:00.000Z', - }), - } - } -}) - -describe('Format Timestamp', () => { - it('should be a function', () => { - expect(formatTimestamp).toBeFunction() - }) - - it('should compute the correct epoch value', () => { - expect(formatTimestamp(100).epoch).toBe(100) - }) - - it('should compute the correct unix value', () => { - expect(formatTimestamp(100).unix).toBe(1490101300) - }) - - it('should compute the correct human value', () => { - expect(formatTimestamp(100).human).toBe('2017-03-21T13:01:40.000Z') - }) -}) diff --git a/packages/core-utils/__tests__/format-timestamp.test.ts b/packages/core-utils/__tests__/format-timestamp.test.ts new file mode 100644 index 0000000000..17a7a19370 --- /dev/null +++ b/packages/core-utils/__tests__/format-timestamp.test.ts @@ -0,0 +1,19 @@ +import "./__support__/mocks/core-container"; + +import { app } from "@arkecosystem/core-container"; +import "jest-extended"; +import { formatTimestamp } from "../src/format-timestamp"; + +describe("Format Timestamp", () => { + it("should compute the correct epoch value", () => { + expect(formatTimestamp(100).epoch).toBe(100); + }); + + it("should compute the correct unix value", () => { + expect(formatTimestamp(100).unix).toBe(1490101300); + }); + + it("should compute the correct human value", () => { + expect(formatTimestamp(100).human).toBe("2017-03-21T13:01:40.000Z"); + }); +}); diff --git a/packages/core-utils/__tests__/round-calculator.test.js b/packages/core-utils/__tests__/round-calculator.test.js deleted file mode 100644 index b1f4a69a1a..0000000000 --- a/packages/core-utils/__tests__/round-calculator.test.js +++ /dev/null @@ -1,44 +0,0 @@ -const app = require('@arkecosystem/core-container') -const roundCalculator = require('../lib/round-calculator') - -app.resolvePlugin = jest.fn(plugin => { - if (plugin === 'config') { - return { - getConstants: () => ({ - activeDelegates: 51, - }), - } - } -}) - -describe('Round calculator', () => { - describe('calculateRound', () => { - it('should be a function', () => { - expect(roundCalculator.calculateRound).toBeFunction() - }) - - it('should calculate the round when nextRound is the same', () => { - const { round, nextRound } = roundCalculator.calculateRound(1) - expect(round).toBe(1) - expect(nextRound).toBe(1) - }) - - it('should calculate the round when nextRound is not the same', () => { - const { round, nextRound } = roundCalculator.calculateRound(51) - expect(round).toBe(1) - expect(nextRound).toBe(2) - }) - }) - - describe('isNewRound', () => { - it('should be a function', () => { - expect(roundCalculator.isNewRound).toBeFunction() - }) - - it('should determine the beginning of a new round', () => { - expect(roundCalculator.isNewRound(1)).toBeTrue() - expect(roundCalculator.isNewRound(2)).toBeFalse() - expect(roundCalculator.isNewRound(52)).toBeTrue() - }) - }) -}) diff --git a/packages/core-utils/__tests__/round-calculator.test.ts b/packages/core-utils/__tests__/round-calculator.test.ts new file mode 100644 index 0000000000..7466050694 --- /dev/null +++ b/packages/core-utils/__tests__/round-calculator.test.ts @@ -0,0 +1,29 @@ +import "./__support__/mocks/core-container"; + +import { app } from "@arkecosystem/core-container"; +import "jest-extended"; +import { calculateRound, isNewRound } from "../src/round-calculator"; + +describe("Round calculator", () => { + describe("calculateRound", () => { + it("should calculate the round when nextRound is the same", () => { + const { round, nextRound } = calculateRound(1); + expect(round).toBe(1); + expect(nextRound).toBe(1); + }); + + it("should calculate the round when nextRound is not the same", () => { + const { round, nextRound } = calculateRound(51); + expect(round).toBe(1); + expect(nextRound).toBe(2); + }); + }); + + describe("isNewRound", () => { + it("should determine the beginning of a new round", () => { + expect(isNewRound(1)).toBeTrue(); + expect(isNewRound(2)).toBeFalse(); + expect(isNewRound(52)).toBeTrue(); + }); + }); +}); diff --git a/packages/core-utils/__tests__/supply-calculator.test.js b/packages/core-utils/__tests__/supply-calculator.test.js deleted file mode 100644 index d99dc8ac90..0000000000 --- a/packages/core-utils/__tests__/supply-calculator.test.js +++ /dev/null @@ -1,107 +0,0 @@ -const app = require('@arkecosystem/core-container') - -let supplyCalculator -let config - -const mockConfig = { - genesisBlock: { totalAmount: 1000 }, - network: { constants: [{ height: 1, reward: 2 }] }, -} - -app.resolvePlugin = jest.fn(plugin => { - if (plugin === 'config') { - return mockConfig - } -}) - -beforeAll(() => { - config = app.resolvePlugin('config') - supplyCalculator = require('../lib/supply-calculator') -}) - -describe('Supply calculator', () => { - it('should calculate supply with milestone at height 2', () => { - mockConfig.network.constants[0].height = 2 - expect(supplyCalculator.calculate(1)).toBe( - mockConfig.genesisBlock.totalAmount, - ) - mockConfig.network.constants[0].height = 1 - }) - - describe.each([0, 5, 100, 2000, 4000, 8000])('at height %s', height => { - it('should calculate the genesis supply without milestone', () => { - const genesisSupply = config.genesisBlock.totalAmount - expect(supplyCalculator.calculate(height)).toBe( - genesisSupply + height * config.network.constants[0].reward, - ) - }) - }) - - describe.each([0, 2000, 4000, 8000, 16000])('at height %s', height => { - it('should calculate the genesis supply with one milestone', () => { - mockConfig.network.constants.push({ height: 8000, reward: 3 }) - - const reward = current => { - if (current < 8000) { - return current * 2 - } - - return 7999 * 2 + (current - 7999) * 3 - } - - const genesisSupply = config.genesisBlock.totalAmount - expect(supplyCalculator.calculate(height)).toBe( - genesisSupply + reward(height), - ) - - mockConfig.network.constants = [{ height: 1, reward: 2 }] - }) - }) - - describe.each([ - 0, - 4000, - 8000, - 12000, - 16000, - 20000, - 32000, - 48000, - 64000, - 128000, - ])('at height %s', height => { - it('should calculate the genesis supply with four milestones', () => { - mockConfig.network.constants.push({ height: 8000, reward: 4 }) - mockConfig.network.constants.push({ height: 16000, reward: 5 }) - mockConfig.network.constants.push({ height: 32000, reward: 10 }) - mockConfig.network.constants.push({ height: 64000, reward: 15 }) - - const reward = current => { - if (current < 8000) { - return current * 2 - } - - if (current < 16000) { - return reward(7999) + (current - 7999) * 4 - } - - if (current < 32000) { - return reward(15999) + (current - 15999) * 5 - } - - if (current < 64000) { - return reward(31999) + (current - 31999) * 10 - } - - return reward(63999) + (current - 63999) * 15 - } - - const genesisSupply = config.genesisBlock.totalAmount - expect(supplyCalculator.calculate(height)).toBe( - genesisSupply + reward(height), - ) - - mockConfig.network.constants = [{ height: 1, reward: 2 }] - }) - }) -}) diff --git a/packages/core-utils/__tests__/supply-calculator.test.ts b/packages/core-utils/__tests__/supply-calculator.test.ts new file mode 100644 index 0000000000..af27cf13bc --- /dev/null +++ b/packages/core-utils/__tests__/supply-calculator.test.ts @@ -0,0 +1,106 @@ +import { app } from "@arkecosystem/core-container"; +import "jest-extended"; +import { calculate } from "../src/supply-calculator"; + +let config; + +const mockConfig = { + genesisBlock: { totalAmount: 1000 }, + milestones: [{ height: 1, reward: 2 }], +}; + +app.getConfig = jest.fn(() => { + return { + all: () => { + return mockConfig; + }, + }; +}); + +app.resolvePlugin = jest.fn(plugin => { + if (plugin === "blockchain") { + return { + getLastBlock: () => { + return { + data: { + height: 0, + }, + }; + }, + }; + } + + return {}; +}); + +beforeAll(() => { + config = app.getConfig().all(); +}); + +describe("Supply calculator", () => { + it("should calculate supply with milestone at height 2", () => { + mockConfig.milestones[0].height = 2; + expect(calculate(1)).toBe(mockConfig.genesisBlock.totalAmount); + mockConfig.milestones[0].height = 1; + }); + + describe.each([0, 5, 100, 2000, 4000, 8000])("at height %s", height => { + it("should calculate the genesis supply without milestone", () => { + const genesisSupply = config.genesisBlock.totalAmount; + expect(calculate(height)).toBe(genesisSupply + height * config.milestones[0].reward); + }); + }); + + describe.each([0, 2000, 4000, 8000, 16000])("at height %s", height => { + it("should calculate the genesis supply with one milestone", () => { + mockConfig.milestones.push({ height: 8000, reward: 3 }); + + const reward = current => { + if (current < 8000) { + return current * 2; + } + + return 7999 * 2 + (current - 7999) * 3; + }; + + const genesisSupply = config.genesisBlock.totalAmount; + expect(calculate(height)).toBe(genesisSupply + reward(height)); + + mockConfig.milestones = [{ height: 1, reward: 2 }]; + }); + }); + + describe.each([0, 4000, 8000, 12000, 16000, 20000, 32000, 48000, 64000, 128000])("at height %s", height => { + it("should calculate the genesis supply with four milestones", () => { + mockConfig.milestones.push({ height: 8000, reward: 4 }); + mockConfig.milestones.push({ height: 16000, reward: 5 }); + mockConfig.milestones.push({ height: 32000, reward: 10 }); + mockConfig.milestones.push({ height: 64000, reward: 15 }); + + const reward = current => { + if (current < 8000) { + return current * 2; + } + + if (current < 16000) { + return reward(7999) + (current - 7999) * 4; + } + + if (current < 32000) { + return reward(15999) + (current - 15999) * 5; + } + + if (current < 64000) { + return reward(31999) + (current - 31999) * 10; + } + + return reward(63999) + (current - 63999) * 15; + }; + + const genesisSupply = config.genesisBlock.totalAmount; + expect(calculate(height)).toBe(genesisSupply + reward(height)); + + mockConfig.milestones = [{ height: 1, reward: 2 }]; + }); + }); +}); diff --git a/packages/core-utils/jest.config.js b/packages/core-utils/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-utils/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-utils/lib/bignumify.js b/packages/core-utils/lib/bignumify.js deleted file mode 100644 index f3eeb2c986..0000000000 --- a/packages/core-utils/lib/bignumify.js +++ /dev/null @@ -1,3 +0,0 @@ -const { Bignum } = require('@arkecosystem/crypto') - -module.exports = value => new Bignum(value) diff --git a/packages/core-utils/lib/create-table.js b/packages/core-utils/lib/create-table.js deleted file mode 100644 index bae1f3d4b5..0000000000 --- a/packages/core-utils/lib/create-table.js +++ /dev/null @@ -1,11 +0,0 @@ -const Table = require('cli-table3') - -module.exports = (opts, data) => { - const table = new Table(opts) - - for (const item of data) { - table.push(item) - } - - return table.toString() -} diff --git a/packages/core-utils/lib/delegate-calculator.js b/packages/core-utils/lib/delegate-calculator.js deleted file mode 100644 index 30313899ae..0000000000 --- a/packages/core-utils/lib/delegate-calculator.js +++ /dev/null @@ -1,49 +0,0 @@ -const app = require('@arkecosystem/core-container') - -let { Bignum } = require('@arkecosystem/crypto') - -Bignum = Bignum.clone({ DECIMAL_PLACES: 2 }) - -/** - * Calculate the approval for the given delegate. - * @param {Delegate} delegate - * @param {Number} height - * @return {Number} Approval, with 2 decimals - */ -exports.calculateApproval = (delegate, height) => { - const config = app.resolvePlugin('config') - - if (!height) { - height = app.resolvePlugin('blockchain').getLastBlock().data.height - } - - const constants = config.getConstants(height) - const totalSupply = new Bignum(config.genesisBlock.totalAmount).plus( - (height - constants.height) * constants.reward, - ) - const voteBalance = new Bignum(delegate.voteBalance) - - return +voteBalance - .times(100) - .dividedBy(totalSupply) - .toFixed(2) -} - -/** - * Calculate the productivity of the given delegate. - * @param {Delegate} delegate - * @return {Number} Productivity, with 2 decimals - */ -exports.calculateProductivity = delegate => { - const missedBlocks = +delegate.missedBlocks - const producedBlocks = +delegate.producedBlocks - - if (!missedBlocks && !producedBlocks) { - return +(0).toFixed(2) - } - - return +( - 100 - - missedBlocks / ((producedBlocks + missedBlocks) / 100) - ).toFixed(2) -} diff --git a/packages/core-utils/lib/format-timestamp.js b/packages/core-utils/lib/format-timestamp.js deleted file mode 100644 index 4d3d6c0596..0000000000 --- a/packages/core-utils/lib/format-timestamp.js +++ /dev/null @@ -1,18 +0,0 @@ -const dayjs = require('dayjs-ext') -const app = require('@arkecosystem/core-container') - -/** - * Format the given epoch based timestamp into human and unix. - * @param {Number} epochStamp - * @return {Object} - */ -module.exports = epochStamp => { - const constants = app.resolvePlugin('config').getConstants(1) - const timestamp = dayjs(constants.epoch).add(epochStamp, 'seconds') - - return { - epoch: epochStamp, - unix: timestamp.unix(), - human: timestamp.toISOString(), - } -} diff --git a/packages/core-utils/lib/index.js b/packages/core-utils/lib/index.js deleted file mode 100644 index a00ea0b449..0000000000 --- a/packages/core-utils/lib/index.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = { - bignumify: require('./bignumify'), - createTable: require('./create-table'), - delegateCalculator: require('./delegate-calculator'), - formatTimestamp: require('./format-timestamp'), - roundCalculator: require('./round-calculator'), - supplyCalculator: require('./supply-calculator'), -} diff --git a/packages/core-utils/lib/round-calculator.js b/packages/core-utils/lib/round-calculator.js deleted file mode 100644 index e6cec2f09a..0000000000 --- a/packages/core-utils/lib/round-calculator.js +++ /dev/null @@ -1,29 +0,0 @@ -const app = require('@arkecosystem/core-container') - -/** - * Calculate the round and nextRound based on the height and active delegates. - * @param {Number} height - * @param {Number} maxDelegates - * @return {Object} - */ -exports.calculateRound = (height, maxDelegates = undefined) => { - const config = app.resolvePlugin('config') - maxDelegates = maxDelegates || config.getConstants(height).activeDelegates - - const round = Math.floor((height - 1) / maxDelegates) + 1 - const nextRound = Math.floor(height / maxDelegates) + 1 - - return { round, nextRound, maxDelegates } -} - -/** - * Detect if height is the beginning of a new round. - * @param {Number} height - * @return {boolean} true if new round, false if not - */ -exports.isNewRound = height => { - const config = app.resolvePlugin('config') - const maxDelegates = config.getConstants(height).activeDelegates - - return height % maxDelegates === 1 -} diff --git a/packages/core-utils/lib/supply-calculator.js b/packages/core-utils/lib/supply-calculator.js deleted file mode 100644 index 5ec5ee280a..0000000000 --- a/packages/core-utils/lib/supply-calculator.js +++ /dev/null @@ -1,50 +0,0 @@ -const { Bignum } = require('@arkecosystem/crypto') -const app = require('@arkecosystem/core-container') - -/** - * Calculate the total supply at the given height - * @param {Number} height - * @return {Number} - */ -exports.calculate = height => { - const config = app.resolvePlugin('config') - const network = config.network - - if (!height) { - const blockchain = app.resolvePlugin('blockchain') - height = blockchain ? blockchain.getLastBlock().data.height : 0 - } - - if (height === 0 || network.constants.length === 0) { - return config.genesisBlock.totalAmount - } - - let rewards = Bignum.ZERO - let currentHeight = 0 - let constantIndex = 0 - - while (currentHeight < height) { - const constants = network.constants[constantIndex] - const nextConstants = network.constants[constantIndex + 1] - - let heightJump = 0 - if ( - nextConstants && - height >= nextConstants.height && - currentHeight < nextConstants.height - 1 - ) { - heightJump = nextConstants.height - 1 - currentHeight - constantIndex += 1 - } else { - heightJump = height - currentHeight - } - - currentHeight += heightJump - - if (currentHeight >= constants.height) { - rewards = rewards.plus(new Bignum(constants.reward).times(heightJump)) - } - } - - return +new Bignum(config.genesisBlock.totalAmount).plus(rewards).toFixed() -} diff --git a/packages/core-utils/package.json b/packages/core-utils/package.json index b83737db94..1ebcbe341d 100644 --- a/packages/core-utils/package.json +++ b/packages/core-utils/package.json @@ -1,30 +1,44 @@ { - "name": "@arkecosystem/core-utils", - "description": "Utilities for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/crypto": "~0.2", - "cli-table3": "^0.5.1", - "dayjs-ext": "^2.2.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-utils", + "description": "Utilities for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "cli-table3": "^0.5.1", + "dayjs-ext": "^2.2.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-utils/src/bignumify.ts b/packages/core-utils/src/bignumify.ts new file mode 100644 index 0000000000..256078ff02 --- /dev/null +++ b/packages/core-utils/src/bignumify.ts @@ -0,0 +1,5 @@ +import { Bignum } from "@arkecosystem/crypto"; + +export function bignumify(value) { + return new Bignum(value); +} diff --git a/packages/core-utils/src/delegate-calculator.ts b/packages/core-utils/src/delegate-calculator.ts new file mode 100644 index 0000000000..dd097f74bd --- /dev/null +++ b/packages/core-utils/src/delegate-calculator.ts @@ -0,0 +1,48 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { Bignum } from "@arkecosystem/crypto"; + +const BignumMod = Bignum.clone({ DECIMAL_PLACES: 2 }); + +/** + * Calculate the approval for the given delegate. + * @param {Delegate} delegate + * @param {Number} height + * @return {Number} Approval, with 2 decimals + */ +function calculateApproval(delegate, height: any = null) { + const config = app.getConfig(); + + if (!height) { + height = app.resolvePlugin("blockchain").getLastBlock().data.height; + } + + const constants = config.getMilestone(height); + const totalSupply = new BignumMod(config.get("genesisBlock.totalAmount")).plus( + (height - constants.height) * constants.reward, + ); + const voteBalance = new BignumMod(delegate.voteBalance); + + return +voteBalance + .times(100) + .dividedBy(totalSupply) + .toFixed(2); +} + +/** + * Calculate the productivity of the given delegate. + * @param {Delegate} delegate + * @return {Number} Productivity, with 2 decimals + */ +function calculateProductivity(delegate) { + const missedBlocks = +delegate.missedBlocks; + const producedBlocks = +delegate.producedBlocks; + + if (!missedBlocks && !producedBlocks) { + return +(0).toFixed(2); + } + + return +(100 - missedBlocks / ((producedBlocks + missedBlocks) / 100)).toFixed(2); +} + +export { calculateApproval, calculateProductivity }; diff --git a/packages/core-utils/src/format-timestamp.ts b/packages/core-utils/src/format-timestamp.ts new file mode 100644 index 0000000000..fccb15518b --- /dev/null +++ b/packages/core-utils/src/format-timestamp.ts @@ -0,0 +1,21 @@ +import { app } from "@arkecosystem/core-container"; +import dayjs from "dayjs-ext"; + +/** + * Format the given epoch based timestamp into human and unix. + * @param {Number} epochStamp + * @return {Object} + */ +function formatTimestamp(epochStamp) { + const constants = app.getConfig().getMilestone(1); + // @ts-ignore + const timestamp = dayjs(constants.epoch).add(epochStamp, "seconds"); + + return { + epoch: epochStamp, + unix: timestamp.unix(), + human: timestamp.toISOString(), + }; +} + +export { formatTimestamp }; diff --git a/packages/core-utils/src/index.ts b/packages/core-utils/src/index.ts new file mode 100644 index 0000000000..9a71650e76 --- /dev/null +++ b/packages/core-utils/src/index.ts @@ -0,0 +1,11 @@ +import { bignumify } from "./bignumify"; +import { calculateApproval, calculateProductivity } from "./delegate-calculator"; +import { formatTimestamp } from "./format-timestamp"; +import { calculateRound, isNewRound } from "./round-calculator"; +import { calculate } from "./supply-calculator"; + +const delegateCalculator = { calculateApproval, calculateProductivity }; +const roundCalculator = { calculateRound, isNewRound }; +const supplyCalculator = { calculate }; + +export { bignumify, delegateCalculator, formatTimestamp, roundCalculator, supplyCalculator }; diff --git a/packages/core-utils/src/round-calculator.ts b/packages/core-utils/src/round-calculator.ts new file mode 100644 index 0000000000..0ef1b6694c --- /dev/null +++ b/packages/core-utils/src/round-calculator.ts @@ -0,0 +1,31 @@ +import { app } from "@arkecosystem/core-container"; + +/** + * Calculate the round and nextRound based on the height and active delegates. + * @param {Number} height + * @param {Number} maxDelegates + * @return {Object} + */ +function calculateRound(height, maxDelegates: any = null) { + const config = app.getConfig(); + maxDelegates = maxDelegates || config.getMilestone(height).activeDelegates; + + const round = Math.floor((height - 1) / maxDelegates) + 1; + const nextRound = Math.floor(height / maxDelegates) + 1; + + return { round, nextRound, maxDelegates }; +} + +/** + * Detect if height is the beginning of a new round. + * @param {Number} height + * @return {boolean} true if new round, false if not + */ +function isNewRound(height) { + const config = app.getConfig(); + const maxDelegates = config.getMilestone(height).activeDelegates; + + return height % maxDelegates === 1; +} + +export { calculateRound, isNewRound }; diff --git a/packages/core-utils/src/supply-calculator.ts b/packages/core-utils/src/supply-calculator.ts new file mode 100644 index 0000000000..6b6624da21 --- /dev/null +++ b/packages/core-utils/src/supply-calculator.ts @@ -0,0 +1,48 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain } from "@arkecosystem/core-interfaces"; +import { Bignum, configManager } from "@arkecosystem/crypto"; + +/** + * Calculate the total supply at the given height + * @param {Number} height + * @return {Number} + */ +function calculate(height) { + const { genesisBlock, milestones } = app.getConfig().all(); + + if (!height) { + const blockchain = app.resolvePlugin("blockchain"); + height = blockchain ? blockchain.getLastBlock().data.height : 0; + } + + if (height === 0 || milestones.length === 0) { + return genesisBlock.totalAmount; + } + + let rewards = Bignum.ZERO; + let currentHeight = 0; + let constantIndex = 0; + + while (currentHeight < height) { + const constants = milestones[constantIndex]; + const nextConstants = milestones[constantIndex + 1]; + + let heightJump = 0; + if (nextConstants && height >= nextConstants.height && currentHeight < nextConstants.height - 1) { + heightJump = nextConstants.height - 1 - currentHeight; + constantIndex += 1; + } else { + heightJump = height - currentHeight; + } + + currentHeight += heightJump; + + if (currentHeight >= constants.height) { + rewards = rewards.plus(new Bignum(constants.reward).times(heightJump)); + } + } + + return +new Bignum(genesisBlock.totalAmount).plus(rewards).toFixed(); +} + +export { calculate }; diff --git a/packages/core-utils/tsconfig.json b/packages/core-utils/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-utils/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-vote-report/CHANGELOG.md b/packages/core-vote-report/CHANGELOG.md deleted file mode 100644 index 6842caf9df..0000000000 --- a/packages/core-vote-report/CHANGELOG.md +++ /dev/null @@ -1,14 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.1.0 - 2018-12-03 - -### Added - -- initial release diff --git a/packages/core-vote-report/LICENSE b/packages/core-vote-report/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-vote-report/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-vote-report/README.md b/packages/core-vote-report/README.md index f4b8319277..d517f17f8f 100644 --- a/packages/core-vote-report/README.md +++ b/packages/core-vote-report/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-vote-report/__tests__/__support__/setup.ts b/packages/core-vote-report/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..c23a414551 --- /dev/null +++ b/packages/core-vote-report/__tests__/__support__/setup.ts @@ -0,0 +1,22 @@ +import { app } from "@arkecosystem/core-container"; +import { setUpContainer } from "@arkecosystem/core-test-utils/src/helpers/container"; +import { defaults } from "../../src/defaults"; +import { startServer } from "../../src/server"; + +jest.setTimeout(60000); + +let server; +async function setUp() { + await setUpContainer({ + exit: "@arkecosystem/core-blockchain", + }); + + server = await startServer(defaults); +} + +async function tearDown() { + await server.stop(); + await app.tearDown(); +} + +export { setUp, tearDown }; diff --git a/packages/core-vote-report/__tests__/server.test.ts b/packages/core-vote-report/__tests__/server.test.ts new file mode 100644 index 0000000000..f81c4cc817 --- /dev/null +++ b/packages/core-vote-report/__tests__/server.test.ts @@ -0,0 +1,20 @@ +import axios from "axios"; +import "jest-extended"; +import { setUp, tearDown } from "./__support__/setup"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +describe("Server", () => { + it("should render the page", async () => { + const response = await axios.get("http://localhost:4006/"); + + expect(response.status).toBe(200); + expect(response.data).toContain("Top 51 Delegates Stats"); + }); +}); diff --git a/packages/core-vote-report/jest.config.js b/packages/core-vote-report/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-vote-report/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-vote-report/lib/defaults.js b/packages/core-vote-report/lib/defaults.js deleted file mode 100644 index 3fc1848f6a..0000000000 --- a/packages/core-vote-report/lib/defaults.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - host: process.env.ARK_VOTE_REPORT_HOST || '0.0.0.0', - port: process.env.ARK_VOTE_REPORT_PORT || 4006, - delegateRows: process.env.ARK_VOTE_REPORT_DELEGATE_ROWS || 80, -} diff --git a/packages/core-vote-report/lib/handler.js b/packages/core-vote-report/lib/handler.js deleted file mode 100644 index 0e4ea4861f..0000000000 --- a/packages/core-vote-report/lib/handler.js +++ /dev/null @@ -1,98 +0,0 @@ -const sumBy = require('lodash/sumBy') -const { configManager } = require('@arkecosystem/crypto') -const { - delegateCalculator, - supplyCalculator, -} = require('@arkecosystem/core-utils') -const app = require('@arkecosystem/core-container') - -const config = app.resolvePlugin('config') -const blockchain = app.resolvePlugin('blockchain') -const database = app.resolvePlugin('database') - -const formatDelegates = delegates => - delegates.map(delegate => { - const voters = database.walletManager - .allByPublicKey() - .filter( - wallet => - wallet.vote === delegate.publicKey && wallet.balance > 0.1 * 1e8, - ) - - const approval = Number( - delegateCalculator.calculateApproval(delegate), - ).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }) - - const rank = delegate.rate.toLocaleString(undefined, { - minimumIntegerDigits: 2, - }) - - const votes = Number(delegate.voteBalance.div(1e8)).toLocaleString( - undefined, - { maximumFractionDigits: 0 }, - ) - const voterCount = voters.length.toLocaleString(undefined, { - maximumFractionDigits: 0, - }) - - return { - rank, - username: delegate.username.padEnd(25), - approval: approval.padEnd(4), - votes: votes.padStart(10), - voterCount: voterCount.padStart(5), - } - }) - -module.exports = (request, h) => { - const lastBlock = blockchain.getLastBlock() - const constants = config.getConstants(lastBlock.data.height) - const delegateRows = app.resolveOptions('vote-report').delegateRows - - const supply = supplyCalculator.calculate(lastBlock.data.height) - - const active = database.walletManager - .allByUsername() - .sort((a, b) => a.rate - b.rate) - .slice(0, constants.activeDelegates) - - const standby = database.walletManager - .allByUsername() - .sort((a, b) => a.rate - b.rate) - .slice(constants.activeDelegates + 1, delegateRows) - - const voters = database.walletManager - .allByPublicKey() - .filter(wallet => wallet.vote && wallet.balance > 0.1 * 1e8) - - const totalVotes = sumBy(voters, wallet => +wallet.balance.toFixed()) - const percentage = (totalVotes * 100) / supply - - const client = configManager.get('client') - - return h - .view('index', { - client, - voteHeader: `Vote ${client.token}`.padStart(10), - activeDelegatesCount: constants.activeDelegates, - activeDelegates: formatDelegates(active), - standbyDelegates: formatDelegates(standby), - voters: voters.length.toLocaleString(undefined, { - maximumFractionDigits: 0, - }), - supply: (supply / 1e8).toLocaleString(undefined, { - maximumFractionDigits: 0, - }), - totalVotes: (totalVotes / 1e8).toLocaleString(undefined, { - maximumFractionDigits: 0, - }), - percentage: percentage.toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }), - }) - .type('text/plain') -} diff --git a/packages/core-vote-report/lib/index.js b/packages/core-vote-report/lib/index.js deleted file mode 100644 index bb3c13089c..0000000000 --- a/packages/core-vote-report/lib/index.js +++ /dev/null @@ -1,11 +0,0 @@ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'vote-report', - async register(container, options) { - return require('./server')(options) - }, - async deregister(container, options) { - return container.resolvePlugin('vote-report').stop() - }, -} diff --git a/packages/core-vote-report/lib/server.js b/packages/core-vote-report/lib/server.js deleted file mode 100644 index 053ed03fa7..0000000000 --- a/packages/core-vote-report/lib/server.js +++ /dev/null @@ -1,25 +0,0 @@ -const Handlebars = require('handlebars') -const { createServer, mountServer } = require('@arkecosystem/core-http-utils') - -module.exports = async config => { - const server = await createServer( - { - host: config.host, - port: config.port, - }, - instance => - instance.views({ - engines: { html: Handlebars }, - relativeTo: __dirname, - path: 'templates', - }), - ) - - server.route({ - method: 'GET', - path: '/', - handler: require('./handler'), - }) - - return mountServer('Vote Report', server) -} diff --git a/packages/core-vote-report/package.json b/packages/core-vote-report/package.json index fa03c75c90..61de3c13e7 100644 --- a/packages/core-vote-report/package.json +++ b/packages/core-vote-report/package.json @@ -1,33 +1,48 @@ { - "name": "@arkecosystem/core-vote-report", - "description": "Vote Report for Ark Core", - "version": "0.1.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "@arkecosystem/core-utils": "~0.2", - "@arkecosystem/crypto": "~0.2", - "handlebars": "^4.0.12", - "lodash.clonedeepwith": "^4.5.0", - "lodash.sumby": "^4.6.0" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-vote-report", + "description": "Vote Report for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile && cp -r src/templates dist/templates", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@arkecosystem/core-utils": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/handlebars": "^4.0.39", + "@types/lodash.sumby": "^4.6.4", + "handlebars": "^4.0.12", + "lodash.sumby": "^4.6.0" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-vote-report/src/defaults.ts b/packages/core-vote-report/src/defaults.ts new file mode 100644 index 0000000000..69b75d21ad --- /dev/null +++ b/packages/core-vote-report/src/defaults.ts @@ -0,0 +1,5 @@ +export const defaults = { + host: process.env.CORE_VOTE_REPORT_HOST || "0.0.0.0", + port: process.env.CORE_VOTE_REPORT_PORT || 4006, + delegateRows: process.env.CORE_VOTE_REPORT_DELEGATE_ROWS || 80, +}; diff --git a/packages/core-vote-report/src/handler.ts b/packages/core-vote-report/src/handler.ts new file mode 100644 index 0000000000..79d91da923 --- /dev/null +++ b/packages/core-vote-report/src/handler.ts @@ -0,0 +1,91 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, Database } from "@arkecosystem/core-interfaces"; +import { delegateCalculator, supplyCalculator } from "@arkecosystem/core-utils"; +import { configManager } from "@arkecosystem/crypto"; +import sumBy from "lodash/sumBy"; + +export function handler(request, h) { + const config = app.getConfig(); + const blockchain = app.resolvePlugin("blockchain"); + const databaseService = app.resolvePlugin("database"); + + const formatDelegates = (delegates, lastHeight) => + delegates.map((delegate, index) => { + const filteredVoters = databaseService.walletManager + .allByPublicKey() + .filter(wallet => wallet.vote === delegate.publicKey && wallet.balance.toNumber() > 0.1 * 1e8); + + const approval = Number(delegateCalculator.calculateApproval(delegate, lastHeight)).toLocaleString( + undefined, + { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }, + ); + + const rank = delegate.rate.toLocaleString(undefined, { + minimumIntegerDigits: 2, + }); + + const votes = Number(delegate.voteBalance.div(1e8)).toLocaleString(undefined, { maximumFractionDigits: 0 }); + const voterCount = filteredVoters.length.toLocaleString(undefined, { + maximumFractionDigits: 0, + }); + + return { + rank, + username: delegate.username.padEnd(25), + approval: approval.padEnd(4), + votes: votes.padStart(10), + voterCount: voterCount.padStart(5), + }; + }); + + const lastBlock = blockchain.getLastBlock(); + const constants = config.getMilestone(lastBlock.data.height); + // @ts-ignore + const delegateRows = request.server.app.config.delegateRows; + + const supply = supplyCalculator.calculate(lastBlock.data.height); + + const allByUsername = databaseService.walletManager + .allByUsername() + .map((delegate, index) => { + (delegate as any).rate = (delegate as any).rate || index + 1; + return delegate; + }) + .sort((a, b) => (a as any).rate - (b as any).rate); + + const active = allByUsername.slice(0, constants.activeDelegates); + const standby = allByUsername.slice(constants.activeDelegates + 1, delegateRows); + + const voters = databaseService.walletManager.allByPublicKey().filter(wallet => wallet.vote && wallet.balance.toNumber() > 0.1 * 1e8); + + const totalVotes = sumBy(voters, (wallet: any) => +wallet.balance.toFixed()); + const percentage = (totalVotes * 100) / supply; + + const client = configManager.get("client"); + + return h + .view("index", { + client, + voteHeader: `Vote ${client.token}`.padStart(10), + activeDelegatesCount: constants.activeDelegates, + activeDelegates: formatDelegates(active, lastBlock.data.height), + standbyDelegates: formatDelegates(standby, lastBlock.data.height), + voters: voters.length.toLocaleString(undefined, { + maximumFractionDigits: 0, + }), + supply: (supply / 1e8).toLocaleString(undefined, { + maximumFractionDigits: 0, + }), + totalVotes: (totalVotes / 1e8).toLocaleString(undefined, { + maximumFractionDigits: 0, + }), + percentage: percentage.toLocaleString(undefined, { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }), + }) + .type("text/plain"); +} diff --git a/packages/core-vote-report/src/index.ts b/packages/core-vote-report/src/index.ts new file mode 100644 index 0000000000..43ad992a47 --- /dev/null +++ b/packages/core-vote-report/src/index.ts @@ -0,0 +1,15 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import { defaults } from "./defaults"; +import { startServer } from "./server"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "vote-report", + async register(container: Container.IContainer, options) { + return startServer(options); + }, + async deregister(container: Container.IContainer, options) { + return container.resolvePlugin("vote-report").stop(); + }, +}; diff --git a/packages/core-vote-report/src/server.ts b/packages/core-vote-report/src/server.ts new file mode 100644 index 0000000000..468edcfa47 --- /dev/null +++ b/packages/core-vote-report/src/server.ts @@ -0,0 +1,29 @@ +import { createServer, mountServer } from "@arkecosystem/core-http-utils"; +import * as Handlebars from "handlebars"; +import { handler } from "./handler"; + +export async function startServer(config) { + const server = await createServer( + { + host: config.host, + port: config.port, + }, + instance => + instance.views({ + engines: { html: Handlebars }, + relativeTo: __dirname, + path: "templates", + }), + ); + + // @ts-ignore + server.app.config = config; + + server.route({ + method: "GET", + path: "/", + handler, + }); + + return mountServer("Vote Report", server); +} diff --git a/packages/core-vote-report/lib/templates/index.html b/packages/core-vote-report/src/templates/index.html similarity index 100% rename from packages/core-vote-report/lib/templates/index.html rename to packages/core-vote-report/src/templates/index.html diff --git a/packages/core-vote-report/tsconfig.json b/packages/core-vote-report/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-vote-report/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-webhooks/CHANGELOG.md b/packages/core-webhooks/CHANGELOG.md deleted file mode 100644 index b98550ed95..0000000000 --- a/packages/core-webhooks/CHANGELOG.md +++ /dev/null @@ -1,26 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.0 - 2018-12-03 - -### Changed - -- Instantly execute webhook jobs instead of queueing a job -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -### Fixed - -- Properly listen for all events -- Properly check for enabled webhooks - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core-webhooks/LICENSE b/packages/core-webhooks/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core-webhooks/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core-webhooks/README.md b/packages/core-webhooks/README.md index 5794bd4144..f71d705e2c 100644 --- a/packages/core-webhooks/README.md +++ b/packages/core-webhooks/README.md @@ -14,8 +14,8 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [Brian Faust](https://github.com/faustbrian) -- [All Contributors](../../../../contributors) +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/core-webhooks/__tests__/__support__/setup.js b/packages/core-webhooks/__tests__/__support__/setup.js deleted file mode 100644 index d43ef14109..0000000000 --- a/packages/core-webhooks/__tests__/__support__/setup.js +++ /dev/null @@ -1,33 +0,0 @@ -const app = require('@arkecosystem/core-container') -const appHelper = require('@arkecosystem/core-test-utils/lib/helpers/container') - -jest.setTimeout(60000) - -exports.setUp = async () => { - process.env.ARK_WEBHOOKS_ENABLED = true - - await appHelper.setUp({ - exclude: [ - '@arkecosystem/core-api', - '@arkecosystem/core-graphql', - '@arkecosystem/core-forger', - ], - }) - - await require('../../lib/manager').setUp({}) - - await require('../../lib/server')({ - enabled: false, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - pagination: { - limit: 100, - include: ['/api/webhooks'], - }, - }) -} - -exports.tearDown = async () => { - await app.tearDown() -} diff --git a/packages/core-webhooks/__tests__/__support__/setup.ts b/packages/core-webhooks/__tests__/__support__/setup.ts new file mode 100644 index 0000000000..7d7053b3b7 --- /dev/null +++ b/packages/core-webhooks/__tests__/__support__/setup.ts @@ -0,0 +1,40 @@ +import { app } from "@arkecosystem/core-container"; +import { setUpContainer } from "@arkecosystem/core-test-utils/src/helpers/container"; +import { database } from "../../src/database"; +import { webhookManager } from "../../src/manager"; +import { startServer } from "../../src/server"; + +jest.setTimeout(60000); + +async function setUp() { + process.env.CORE_WEBHOOKS_ENABLED = "true"; + + await setUpContainer({ + exclude: ["@arkecosystem/core-api", "@arkecosystem/core-graphql", "@arkecosystem/core-forger"], + }); + + await database.setUp({ + dialect: "sqlite", + storage: `${process.env.CORE_PATH_DATA}/webhooks.sqlite`, + logging: process.env.CORE_DB_LOGGING, + }); + + await webhookManager.setUp(); + + await startServer({ + enabled: false, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + pagination: { + limit: 100, + include: ["/api/webhooks"], + }, + }); +} + +async function tearDown() { + await app.tearDown(); +} + +export { setUp, tearDown }; diff --git a/packages/core-webhooks/__tests__/__support__/utils.ts b/packages/core-webhooks/__tests__/__support__/utils.ts new file mode 100644 index 0000000000..8b44919ce8 --- /dev/null +++ b/packages/core-webhooks/__tests__/__support__/utils.ts @@ -0,0 +1,61 @@ +import axios from "axios"; +import "jest-extended"; + +function request(method, path, params = {}) { + const url = `http://localhost:4004/api/${path}`; + const instance = axios[method.toLowerCase()]; + + return ["GET", "DELETE"].includes(method) ? instance(url, { params }) : instance(url, params); +} + +function expectJson(response) { + expect(response.data).toBeObject(); +} + +function expectStatus(response, code) { + expect(response.status).toBe(code); +} + +function expectResource(response) { + expect(response.data.data).toBeObject(); +} + +function expectCollection(response) { + expect(Array.isArray(response.data.data)).toBe(true); +} + +function expectPaginator(response, firstPage = true) { + expect(response.data.meta).toBeObject(); + expect(response.data.meta).toHaveProperty("count"); + expect(response.data.meta).toHaveProperty("pageCount"); + expect(response.data.meta).toHaveProperty("totalCount"); + expect(response.data.meta).toHaveProperty("next"); + expect(response.data.meta).toHaveProperty("previous"); + expect(response.data.meta).toHaveProperty("self"); + expect(response.data.meta).toHaveProperty("first"); + expect(response.data.meta).toHaveProperty("last"); +} + +function expectSuccessful(response, statusCode = 200) { + this.expectStatus(response, statusCode); + this.expectJson(response); +} + +function expectError(response, statusCode = 404) { + this.expectStatus(response, statusCode); + this.expectJson(response); + expect(response.data.statusCode).toBeNumber(); + expect(response.data.error).toBeString(); + expect(response.data.message).toBeString(); +} + +export { + request, + expectJson, + expectStatus, + expectResource, + expectCollection, + expectPaginator, + expectSuccessful, + expectError, +}; diff --git a/packages/core-webhooks/__tests__/conditions.test.ts b/packages/core-webhooks/__tests__/conditions.test.ts new file mode 100644 index 0000000000..26eeed7b51 --- /dev/null +++ b/packages/core-webhooks/__tests__/conditions.test.ts @@ -0,0 +1,144 @@ +import "jest-extended"; +import { between, contains, eq, falsy, gt, gte, lt, lte, ne, notBetween, regexp, truthy } from "../src/conditions"; + +describe("Conditions - between", () => { + it("should be true", () => { + expect( + between(1.5, { + min: 1, + max: 2, + }), + ).toBeTrue(); + }); + + it("should be false", () => { + expect( + between(3, { + min: 1, + max: 2, + }), + ).toBeFalse(); + }); +}); + +describe("Conditions - contains", () => { + it("should be true", () => { + expect(contains("Hello World", "Hello")).toBeTrue(); + }); + + it("should be false", () => { + expect(contains("Hello World", "invalid")).toBeFalse(); + }); +}); + +describe("Conditions - equal", () => { + it("should be true", () => { + expect(eq(1, 1)).toBeTrue(); + }); + + it("should be false", () => { + expect(eq(1, 2)).toBeFalse(); + }); +}); + +describe("Conditions - falsy", () => { + it("should be true", () => { + expect(falsy(false)).toBeTrue(); + }); + + it("should be false", () => { + expect(falsy(true)).toBeFalse(); + }); +}); + +describe("Conditions - greater than", () => { + it("should be true", () => { + expect(gt(2, 1)).toBeTrue(); + }); + + it("should be false", () => { + expect(gt(1, 2)).toBeFalse(); + }); +}); + +describe("Conditions - greater than or equal", () => { + it("should be true", () => { + expect(gte(2, 1)).toBeTrue(); + expect(gte(2, 2)).toBeTrue(); + }); + + it("should be false", () => { + expect(gte(1, 2)).toBeFalse(); + }); +}); + +describe("Conditions - less than", () => { + it("should be true", () => { + expect(lt(1, 2)).toBeTrue(); + }); + + it("should be false", () => { + expect(lt(2, 1)).toBeFalse(); + }); +}); + +describe("Conditions - less than or equal", () => { + it("should be true", () => { + expect(lte(1, 2)).toBeTrue(); + expect(lte(1, 1)).toBeTrue(); + }); + + it("should be false", () => { + expect(lte(2, 1)).toBeFalse(); + }); +}); + +describe("Conditions - not equal", () => { + it("should be true", () => { + expect(ne(1, 2)).toBeTrue(); + }); + + it("should be false", () => { + expect(ne(1, 1)).toBeFalse(); + }); +}); + +describe("Conditions - not-between", () => { + it("should be true", () => { + expect( + notBetween(3, { + min: 1, + max: 2, + }), + ).toBeTrue(); + }); + + it("should be false", () => { + expect( + notBetween(1.5, { + min: 1, + max: 2, + }), + ).toBeFalse(); + }); +}); + +describe("Conditions - regexp", () => { + it("should be true", () => { + expect(regexp("hello world!", "hello")).toBeTrue(); + }); + + it("should be false", () => { + expect(regexp(123, "w+")).toBeFalse(); + }); +}); + +describe("Conditions - truthy", () => { + it("should be true", () => { + expect(truthy(true)).toBeTrue(); + }); + + it("should be false", () => { + expect(truthy(false)).toBeFalse(); + }); +}); diff --git a/packages/core-webhooks/__tests__/conditions/between.test.js b/packages/core-webhooks/__tests__/conditions/between.test.js deleted file mode 100644 index 3543590539..0000000000 --- a/packages/core-webhooks/__tests__/conditions/between.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const condition = require('../../lib/conditions/between') - -describe('Conditions - between', () => { - it('should be true', () => { - expect( - condition(1.5, { - min: 1, - max: 2, - }), - ).toBeTrue() - }) - - it('should be false', () => { - expect( - condition(3, { - min: 1, - max: 2, - }), - ).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/contains.test.js b/packages/core-webhooks/__tests__/conditions/contains.test.js deleted file mode 100644 index 5de1ef8bb7..0000000000 --- a/packages/core-webhooks/__tests__/conditions/contains.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/contains') - -describe('Conditions - contains', () => { - it('should be true', () => { - expect(condition('Hello World', 'Hello')).toBeTrue() - }) - - it('should be false', () => { - expect(condition('Hello World', 'invalid')).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/eq.test.js b/packages/core-webhooks/__tests__/conditions/eq.test.js deleted file mode 100644 index fc637ce423..0000000000 --- a/packages/core-webhooks/__tests__/conditions/eq.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/eq') - -describe('Conditions - equal', () => { - it('should be true', () => { - expect(condition(1, 1)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(1, 2)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/falsy.test.js b/packages/core-webhooks/__tests__/conditions/falsy.test.js deleted file mode 100644 index 1458dc3833..0000000000 --- a/packages/core-webhooks/__tests__/conditions/falsy.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/falsy') - -describe('Conditions - falsy', () => { - it('should be true', () => { - expect(condition(false)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(true)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/gt.test.js b/packages/core-webhooks/__tests__/conditions/gt.test.js deleted file mode 100644 index 681b9e6114..0000000000 --- a/packages/core-webhooks/__tests__/conditions/gt.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/gt') - -describe('Conditions - greater than', () => { - it('should be true', () => { - expect(condition(2, 1)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(1, 2)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/gte.test.js b/packages/core-webhooks/__tests__/conditions/gte.test.js deleted file mode 100644 index f80a98f970..0000000000 --- a/packages/core-webhooks/__tests__/conditions/gte.test.js +++ /dev/null @@ -1,12 +0,0 @@ -const condition = require('../../lib/conditions/gte') - -describe('Conditions - greater than or equal', () => { - it('should be true', () => { - expect(condition(2, 1)).toBeTrue() - expect(condition(2, 2)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(1, 2)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/lt.test.js b/packages/core-webhooks/__tests__/conditions/lt.test.js deleted file mode 100644 index f269dd2bba..0000000000 --- a/packages/core-webhooks/__tests__/conditions/lt.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/lt') - -describe('Conditions - less than', () => { - it('should be true', () => { - expect(condition(1, 2)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(2, 1)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/lte.test.js b/packages/core-webhooks/__tests__/conditions/lte.test.js deleted file mode 100644 index 1fdc49b256..0000000000 --- a/packages/core-webhooks/__tests__/conditions/lte.test.js +++ /dev/null @@ -1,12 +0,0 @@ -const condition = require('../../lib/conditions/lte') - -describe('Conditions - less than or equal', () => { - it('should be true', () => { - expect(condition(1, 2)).toBeTrue() - expect(condition(1, 1)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(2, 1)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/ne.test.js b/packages/core-webhooks/__tests__/conditions/ne.test.js deleted file mode 100644 index b220b168fb..0000000000 --- a/packages/core-webhooks/__tests__/conditions/ne.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/ne') - -describe('Conditions - not equal', () => { - it('should be true', () => { - expect(condition(1, 2)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(1, 1)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/not-between.test.js b/packages/core-webhooks/__tests__/conditions/not-between.test.js deleted file mode 100644 index f275d4a62b..0000000000 --- a/packages/core-webhooks/__tests__/conditions/not-between.test.js +++ /dev/null @@ -1,21 +0,0 @@ -const condition = require('../../lib/conditions/not-between') - -describe('Conditions - not-between', () => { - it('should be true', () => { - expect( - condition(3, { - min: 1, - max: 2, - }), - ).toBeTrue() - }) - - it('should be false', () => { - expect( - condition(1.5, { - min: 1, - max: 2, - }), - ).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/regexp.test.js b/packages/core-webhooks/__tests__/conditions/regexp.test.js deleted file mode 100644 index 6cbee84c35..0000000000 --- a/packages/core-webhooks/__tests__/conditions/regexp.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/regexp') - -describe('Conditions - regexp', () => { - it('should be true', () => { - expect(condition('hello world!', 'hello')).toBeTrue() - }) - - it('should be false', () => { - expect(condition(123, 'w+')).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/conditions/truthy.test.js b/packages/core-webhooks/__tests__/conditions/truthy.test.js deleted file mode 100644 index a67dd0224c..0000000000 --- a/packages/core-webhooks/__tests__/conditions/truthy.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const condition = require('../../lib/conditions/truthy') - -describe('Conditions - truthy', () => { - it('should be true', () => { - expect(condition(true)).toBeTrue() - }) - - it('should be false', () => { - expect(condition(false)).toBeFalse() - }) -}) diff --git a/packages/core-webhooks/__tests__/server.test.ts b/packages/core-webhooks/__tests__/server.test.ts new file mode 100644 index 0000000000..11b644e3a6 --- /dev/null +++ b/packages/core-webhooks/__tests__/server.test.ts @@ -0,0 +1,116 @@ +import "jest-extended"; +import { setUp, tearDown } from "./__support__/setup"; +import * as utils from "./__support__/utils"; + +beforeAll(async () => { + await setUp(); +}); + +afterAll(async () => { + await tearDown(); +}); + +const postData = { + event: "block.forged", + target: "https://httpbin.org/post", + enabled: true, + conditions: [ + { + key: "generatorPublicKey", + condition: "eq", + value: "test-generator", + }, + { + key: "fee", + condition: "gte", + value: "123", + }, + ], +}; + +function createWebhook(data = null) { + return utils.request("POST", "webhooks", data || postData); +} + +describe("API 2.0 - Webhooks", () => { + describe("GET /webhooks", () => { + it("should GET all the webhooks", async () => { + const response = await utils.request("GET", "webhooks"); + utils.expectSuccessful(response); + utils.expectCollection(response); + }); + }); + + describe("POST /webhooks", () => { + it("should POST a new webhook with a simple condition", async () => { + const response = await createWebhook(); + utils.expectSuccessful(response, 201); + utils.expectResource(response); + }); + + it("should POST a new webhook with a complex condition", async () => { + const response = await createWebhook({ + event: "block.forged", + target: "https://httpbin.org/post", + enabled: true, + conditions: [ + { + key: "fee", + condition: "between", + value: { + min: 1, + max: 2, + }, + }, + ], + }); + utils.expectSuccessful(response, 201); + utils.expectResource(response); + }); + + it("should POST a new webhook with an empty array as condition", async () => { + const response = await createWebhook({ + event: "block.forged", + target: "https://httpbin.org/post", + enabled: true, + conditions: [], + }); + utils.expectSuccessful(response, 201); + utils.expectResource(response); + }); + }); + + describe("GET /webhooks/{id}", () => { + it("should GET a webhook by the given id", async () => { + const webhook = await createWebhook(); + + const response = await utils.request("GET", `webhooks/${webhook.data.data.id}`); + utils.expectSuccessful(response); + utils.expectResource(response); + + const { data } = response.data; + const webhookData = Object.assign(webhook.data.data, { + token: data.token.substring(0, 32), + }); + expect(data).toEqual(webhookData); + }); + }); + + describe("PUT /webhooks/{id}", () => { + it("should PUT a webhook by the given id", async () => { + const webhook = await createWebhook(); + + const response = await utils.request("PUT", `webhooks/${webhook.data.data.id}`, postData); + utils.expectStatus(response, 204); + }); + }); + + describe("DELETE /webhooks/{id}", () => { + it("should DELETE a webhook by the given id", async () => { + const webhook = await createWebhook(); + + const response = await utils.request("DELETE", `webhooks/${webhook.data.data.id}`); + utils.expectStatus(response, 204); + }); + }); +}); diff --git a/packages/core-webhooks/__tests__/server/handler.test.js b/packages/core-webhooks/__tests__/server/handler.test.js deleted file mode 100644 index 08a03130bf..0000000000 --- a/packages/core-webhooks/__tests__/server/handler.test.js +++ /dev/null @@ -1,114 +0,0 @@ -const app = require('../__support__/setup') -const utils = require('./utils') - -beforeAll(async () => { - await app.setUp() -}) - -afterAll(async () => { - await app.tearDown() -}) - -const postData = { - event: 'block.forged', - target: 'https://httpbin.org/post', - enabled: true, - conditions: [ - { - key: 'generatorPublicKey', - condition: 'eq', - value: 'test-generator', - }, - { - key: 'fee', - condition: 'gte', - value: '123', - }, - ], -} - -function createWebhook(data = null) { - return utils.request('POST', 'webhooks', data || postData) -} - -describe('API 2.0 - Webhooks', () => { - describe('GET /webhooks', () => { - it('should GET all the webhooks', async () => { - const response = await utils.request('GET', 'webhooks') - utils.expectSuccessful(response) - utils.expectCollection(response) - }) - }) - - describe('POST /webhooks', () => { - it('should POST a new webhook with a simple condition', async () => { - const response = await createWebhook() - utils.expectSuccessful(response, 201) - utils.expectResource(response) - }) - - it('should POST a new webhook with a complex condition', async () => { - const response = await createWebhook({ - event: 'block.forged', - target: 'https://httpbin.org/post', - enabled: true, - conditions: [ - { - key: 'fee', - condition: 'between', - value: { - min: 1, - max: 2, - }, - }, - ], - }) - utils.expectSuccessful(response, 201) - utils.expectResource(response) - }) - }) - - describe('GET /webhooks/{id}', () => { - it('should GET a webhook by the given id', async () => { - const webhook = await createWebhook() - - const response = await utils.request( - 'GET', - `webhooks/${webhook.data.data.id}`, - ) - utils.expectSuccessful(response) - utils.expectResource(response) - - const { data } = response.data - const webhookData = Object.assign(webhook.data.data, { - token: data.token.substring(0, 32), - }) - expect(data).toEqual(webhookData) - }) - }) - - describe('PUT /webhooks/{id}', () => { - it('should PUT a webhook by the given id', async () => { - const webhook = await createWebhook() - - const response = await utils.request( - 'PUT', - `webhooks/${webhook.data.data.id}`, - postData, - ) - utils.expectStatus(response, 204) - }) - }) - - describe('DELETE /webhooks/{id}', () => { - it('should DELETE a webhook by the given id', async () => { - const webhook = await createWebhook() - - const response = await utils.request( - 'DELETE', - `webhooks/${webhook.data.data.id}`, - ) - utils.expectStatus(response, 204) - }) - }) -}) diff --git a/packages/core-webhooks/__tests__/server/utils.js b/packages/core-webhooks/__tests__/server/utils.js deleted file mode 100644 index b558d5826f..0000000000 --- a/packages/core-webhooks/__tests__/server/utils.js +++ /dev/null @@ -1,58 +0,0 @@ -const axios = require('axios') - -class Helpers { - request(method, path, params = {}) { - const url = `http://localhost:4004/api/${path}` - const request = axios[method.toLowerCase()] - - return ['GET', 'DELETE'].includes(method) - ? request(url, { params }) - : request(url, params) - } - - expectJson(response) { - expect(response.data).toBeObject() - } - - expectStatus(response, code) { - expect(response.status).toBe(code) - } - - expectResource(response) { - expect(response.data.data).toBeObject() - } - - expectCollection(response) { - expect(Array.isArray(response.data.data)).toBe(true) - } - - expectPaginator(response, firstPage = true) { - expect(response.data.meta).toBeObject() - expect(response.data.meta).toHaveProperty('count') - expect(response.data.meta).toHaveProperty('pageCount') - expect(response.data.meta).toHaveProperty('totalCount') - expect(response.data.meta).toHaveProperty('next') - expect(response.data.meta).toHaveProperty('previous') - expect(response.data.meta).toHaveProperty('self') - expect(response.data.meta).toHaveProperty('first') - expect(response.data.meta).toHaveProperty('last') - } - - expectSuccessful(response, statusCode = 200) { - this.expectStatus(response, statusCode) - this.expectJson(response) - } - - expectError(response, statusCode = 404) { - this.expectStatus(response, statusCode) - this.expectJson(response) - expect(response.data.statusCode).toBeInteger() - expect(response.data.error).toBeString() - expect(response.data.message).toBeString() - } -} - -/** - * @type {Helpers} - */ -module.exports = new Helpers() diff --git a/packages/core-webhooks/jest.config.js b/packages/core-webhooks/jest.config.js deleted file mode 100644 index 57770a97bb..0000000000 --- a/packages/core-webhooks/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - collectCoverage: false, - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/core-webhooks/lib/conditions/between.js b/packages/core-webhooks/lib/conditions/between.js deleted file mode 100644 index ec8371b8e3..0000000000 --- a/packages/core-webhooks/lib/conditions/between.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if the given value is between min and max. - * @param {*} actual - * @param {*} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual > expected.min && actual < expected.max diff --git a/packages/core-webhooks/lib/conditions/contains.js b/packages/core-webhooks/lib/conditions/contains.js deleted file mode 100644 index bda6d138ae..0000000000 --- a/packages/core-webhooks/lib/conditions/contains.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A contains B. - * @param {*} actual - * @param {*} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual.includes(expected) diff --git a/packages/core-webhooks/lib/conditions/eq.js b/packages/core-webhooks/lib/conditions/eq.js deleted file mode 100644 index d528558f50..0000000000 --- a/packages/core-webhooks/lib/conditions/eq.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A equals B. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual === expected diff --git a/packages/core-webhooks/lib/conditions/falsy.js b/packages/core-webhooks/lib/conditions/falsy.js deleted file mode 100644 index 8cf3018961..0000000000 --- a/packages/core-webhooks/lib/conditions/falsy.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Check if the given value is false. - * @param {*} actual - * @return {Boolean} - */ -module.exports = actual => actual === false diff --git a/packages/core-webhooks/lib/conditions/gt.js b/packages/core-webhooks/lib/conditions/gt.js deleted file mode 100644 index cedc3d2f02..0000000000 --- a/packages/core-webhooks/lib/conditions/gt.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A is greater than B. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual > expected diff --git a/packages/core-webhooks/lib/conditions/gte.js b/packages/core-webhooks/lib/conditions/gte.js deleted file mode 100644 index 9b37bd9524..0000000000 --- a/packages/core-webhooks/lib/conditions/gte.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A is greater than or equal to B. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual >= expected diff --git a/packages/core-webhooks/lib/conditions/lt.js b/packages/core-webhooks/lib/conditions/lt.js deleted file mode 100644 index 0fade24ca2..0000000000 --- a/packages/core-webhooks/lib/conditions/lt.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A is lesser than B. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual < expected diff --git a/packages/core-webhooks/lib/conditions/lte.js b/packages/core-webhooks/lib/conditions/lte.js deleted file mode 100644 index 3ed5e2d525..0000000000 --- a/packages/core-webhooks/lib/conditions/lte.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A is lesser than or equal to B. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual <= expected diff --git a/packages/core-webhooks/lib/conditions/ne.js b/packages/core-webhooks/lib/conditions/ne.js deleted file mode 100644 index e19b931d00..0000000000 --- a/packages/core-webhooks/lib/conditions/ne.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if A does not equal B. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => actual !== expected diff --git a/packages/core-webhooks/lib/conditions/not-between.js b/packages/core-webhooks/lib/conditions/not-between.js deleted file mode 100644 index 8e81b2e587..0000000000 --- a/packages/core-webhooks/lib/conditions/not-between.js +++ /dev/null @@ -1,9 +0,0 @@ -const between = require('./between') - -/** - * Check if the given value is not between min and max. - * @param {Number} actual - * @param {Number} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => !between(actual, expected) diff --git a/packages/core-webhooks/lib/conditions/regexp.js b/packages/core-webhooks/lib/conditions/regexp.js deleted file mode 100644 index 8abc454110..0000000000 --- a/packages/core-webhooks/lib/conditions/regexp.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Check if the given value matches. - * @param {*} actual - * @param {String} expected - * @return {Boolean} - */ -module.exports = (actual, expected) => new RegExp(expected).test(actual) diff --git a/packages/core-webhooks/lib/conditions/truthy.js b/packages/core-webhooks/lib/conditions/truthy.js deleted file mode 100644 index 1ac9e49daf..0000000000 --- a/packages/core-webhooks/lib/conditions/truthy.js +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Check if the given value is true. - * @param {*} actual - * @return {Boolean} - */ -module.exports = actual => actual === true diff --git a/packages/core-webhooks/lib/database/index.js b/packages/core-webhooks/lib/database/index.js deleted file mode 100644 index d640fa54cd..0000000000 --- a/packages/core-webhooks/lib/database/index.js +++ /dev/null @@ -1,140 +0,0 @@ -const Sequelize = require('sequelize') - -const Op = Sequelize.Op -const Umzug = require('umzug') -const path = require('path') -const fs = require('fs-extra') -const app = require('@arkecosystem/core-container') - -class Database { - /** - * Set up the database connection. - * @param {Object} config - * @return {void} - */ - async setUp(config) { - if (this.connection) { - throw new Error('Webhooks database already initialised') - } - - if (config.dialect === 'sqlite' && config.storage !== ':memory:') { - await fs.ensureFile(config.storage) - } - - this.connection = new Sequelize({ - ...config, - ...{ operatorsAliases: Op }, - }) - - try { - await this.connection.authenticate() - await this.__runMigrations() - await this.__registerModels() - } catch (error) { - app.forceExit('Unable to connect to the database!', error) - } - } - - /** - * Paginate all webhooks. - * @param {Object} params - * @return {Object} - */ - paginate(params) { - return this.model.findAndCountAll(params) - } - - /** - * Get a webhook for the given id. - * @param {Number} id - * @return {Object} - */ - findById(id) { - return this.model.findById(id) - } - - /** - * Get all webhooks for the given event. - * @param {String} event - * @return {Array} - */ - findByEvent(event) { - return this.model.findAll({ where: { event } }) - } - - /** - * Store a new webhook. - * @param {Object} data - * @return {Object} - */ - create(data) { - return this.model.create(data) - } - - /** - * Update the webhook for the given id. - * @param {Number} id - * @param {Object} data - * @return {Boolean} - */ - async update(id, data) { - try { - const webhook = await this.model.findById(id) - - webhook.update(data) - - return true - } catch (e) { - return false - } - } - - /** - * Destroy the webhook for the given id. - * @param {Number} id - * @return {Boolean} - */ - async destroy(id) { - try { - const webhook = await this.model.findById(id) - - webhook.destroy() - - return true - } catch (e) { - return false - } - } - - /** - * Run all migrations. - * @return {Boolean} - */ - __runMigrations() { - const umzug = new Umzug({ - storage: 'sequelize', - storageOptions: { - sequelize: this.connection, - }, - migrations: { - params: [this.connection.getQueryInterface(), Sequelize], - path: path.join(__dirname, 'migrations'), - }, - }) - - return umzug.up() - } - - /** - * Register all models. - * @return {void} - */ - __registerModels() { - this.model = this.connection.import('./model') - } -} - -/** - * @type {Database} - */ -module.exports = new Database() diff --git a/packages/core-webhooks/lib/database/migrations/20180305163843-create-webhook.js b/packages/core-webhooks/lib/database/migrations/20180305163843-create-webhook.js deleted file mode 100644 index b48e612516..0000000000 --- a/packages/core-webhooks/lib/database/migrations/20180305163843-create-webhook.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * The webhooks migration. - * @type {Object} - */ -module.exports = { - /** - * Run the migrations. - * @param {Sequelize.QueryInterface} queryInterface - * @param {Sequelize} Sequelize - * @return {void} - */ - async up(queryInterface, Sequelize) { - await queryInterface.createTable('webhooks', { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: Sequelize.INTEGER, - }, - event: Sequelize.STRING, - target: Sequelize.STRING, - conditions: Sequelize.JSON, - token: { - unique: true, - type: Sequelize.STRING, - }, - enabled: Sequelize.BOOLEAN, - createdAt: { - allowNull: false, - type: Sequelize.DATE, - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE, - }, - }) - - queryInterface.addIndex('webhooks', ['event']) - }, - /** - * Reverse the migrations. - * @param {Sequelize.QueryInterface} queryInterface - * @param {Sequelize} Sequelize - * @return {void} - */ - async down(queryInterface, Sequelize) { - return queryInterface.dropTable('webhooks') - }, -} diff --git a/packages/core-webhooks/lib/database/model.js b/packages/core-webhooks/lib/database/model.js deleted file mode 100644 index eeebd70a6d..0000000000 --- a/packages/core-webhooks/lib/database/model.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Define the webhook model. - * @param {Sequelize} sequelize - * @param {Sequelize.DataTypes} DataTypes - * @return {Sequelize.Model} - */ -module.exports = (sequelize, DataTypes) => { - const Webhook = sequelize.define( - 'webhook', - { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: DataTypes.INTEGER, - }, - event: DataTypes.STRING, - target: DataTypes.STRING, - conditions: DataTypes.JSON, - token: { - unique: true, - type: DataTypes.STRING, - }, - enabled: DataTypes.BOOLEAN, - }, - {}, - ) - - return Webhook -} diff --git a/packages/core-webhooks/lib/defaults.js b/packages/core-webhooks/lib/defaults.js deleted file mode 100644 index df0167a8f5..0000000000 --- a/packages/core-webhooks/lib/defaults.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - database: { - dialect: 'sqlite', - storage: `${process.env.ARK_PATH_DATA}/database/webhooks.sqlite`, - logging: process.env.ARK_DB_LOGGING, - }, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - pagination: { - limit: 100, - include: ['/api/webhooks'], - }, - }, -} diff --git a/packages/core-webhooks/lib/index.js b/packages/core-webhooks/lib/index.js deleted file mode 100644 index c705495116..0000000000 --- a/packages/core-webhooks/lib/index.js +++ /dev/null @@ -1,38 +0,0 @@ -const webhookManager = require('./manager') -const database = require('./database') - -/** - * The struct used by the plugin container. - * @type {Object} - */ -exports.plugin = { - pkg: require('../package.json'), - defaults: require('./defaults'), - alias: 'webhooks', - async register(container, options) { - const logger = container.resolvePlugin('logger') - - if (!options.enabled) { - logger.info('Webhooks are disabled :grey_exclamation:') - - return - } - - await database.setUp(options.database) - - await webhookManager.setUp(options) - - if (options.server.enabled) { - return require('./server')(options.server) - } - - logger.info('Webhooks API server is disabled :grey_exclamation:') - }, - async deregister(container, options) { - if (options.server.enabled) { - container.resolvePlugin('logger').info('Stopping Webhook API') - - return container.resolvePlugin('webhooks').stop() - } - }, -} diff --git a/packages/core-webhooks/lib/manager.js b/packages/core-webhooks/lib/manager.js deleted file mode 100644 index 4594d683cc..0000000000 --- a/packages/core-webhooks/lib/manager.js +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint no-await-in-loop: "off" */ - -const axios = require('axios') -const app = require('@arkecosystem/core-container') - -const logger = app.resolvePlugin('logger') -const database = require('./database') - -const emitter = app.resolvePlugin('event-emitter') - -class WebhookManager { - /** - * Set up the webhook app. - * @param {Object} config - * @return {void} - */ - async setUp(config) { - this.config = config - - for (const event of app.resolvePlugin('blockchain').getEvents()) { - emitter.on(event, async payload => { - const webhooks = await database.findByEvent(event) - - for (const webhook of this.getMatchingWebhooks(webhooks, payload)) { - try { - const response = await axios.post( - webhook.target, - { - timestamp: +new Date(), - data: payload, - event: webhook.event, - }, - { - headers: { - Authorization: webhook.token, - }, - }, - ) - - logger.debug( - `Webhooks Job ${webhook.id} completed! Event [${ - webhook.event - }] has been transmitted to [${ - webhook.target - }] with a status of [${response.status}].`, - ) - } catch (error) { - logger.error(`Webhooks Job ${webhook.id} failed: ${error.message}`) - } - } - }) - } - } - - /** - * Get all webhooks. - * @param {Array} webhooks - * @param {Object} payload - * @return {Array} - */ - getMatchingWebhooks(webhooks, payload) { - const matches = [] - - for (const webhook of webhooks) { - if (!webhook.enabled) { - continue - } - - if (!webhook.conditions) { - matches.push(webhook) - - continue - } - - for (const condition of webhook.conditions) { - const satisfies = require(`./conditions/${condition.condition}`) - - if (!satisfies(payload[condition.key], condition.value)) { - continue - } - - matches.push(webhook) - } - } - - return matches - } -} - -module.exports = new WebhookManager() diff --git a/packages/core-webhooks/lib/server/handler.js b/packages/core-webhooks/lib/server/handler.js deleted file mode 100644 index ae31a1d454..0000000000 --- a/packages/core-webhooks/lib/server/handler.js +++ /dev/null @@ -1,109 +0,0 @@ -const database = require('../database') -const utils = require('./utils') -const schema = require('./schema') - -/** - * @type {Object} - */ -exports.index = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const webhooks = await database.paginate(utils.paginate(request)) - - return utils.toPagination(request, webhooks, 'webhook') - }, -} - -/** - * @type {Object} - */ -exports.store = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const token = require('crypto') - .randomBytes(32) - .toString('hex') - request.payload.token = token.substring(0, 32) - - const webhook = await database.create(request.payload) - webhook.token = token - - return h - .response(utils.respondWithResource(request, webhook, 'webhook')) - .code(201) - }, - options: { - plugins: { - pagination: { - enabled: false, - }, - }, - validate: schema.store, - }, -} - -/** - * @type {Object} - */ -exports.show = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - const webhook = await database.findById(request.params.id) - delete webhook.token - - return utils.respondWithResource(request, webhook, 'webhook') - }, - options: { - validate: schema.show, - }, -} - -/** - * @type {Object} - */ -exports.update = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - await database.update(request.params.id, request.payload) - - return h.response(null).code(204) - }, - options: { - validate: schema.update, - }, -} - -/** - * @type {Object} - */ -exports.destroy = { - /** - * @param {Hapi.Request} request - * @param {Hapi.Toolkit} h - * @return {Hapi.Response} - */ - async handler(request, h) { - await database.destroy(request.params.id, request.payload) - - return h.response(null).code(204) - }, - options: { - validate: schema.destroy, - }, -} diff --git a/packages/core-webhooks/lib/server/index.js b/packages/core-webhooks/lib/server/index.js deleted file mode 100644 index 244e2d992c..0000000000 --- a/packages/core-webhooks/lib/server/index.js +++ /dev/null @@ -1,62 +0,0 @@ -const { - createServer, - mountServer, - plugins, -} = require('@arkecosystem/core-http-utils') - -/** - * Creates a new hapi.js server. - * @param {Object} config - * @return {Hapi.Server} - */ -module.exports = async config => { - const server = await createServer({ - host: config.host, - port: config.port, - routes: { - cors: true, - validate: { - async failAction(request, h, err) { - throw err - }, - }, - }, - }) - - await server.register({ - plugin: plugins.whitelist, - options: { - whitelist: config.whitelist, - name: 'Webhook API', - }, - }) - - await server.register({ - plugin: require('hapi-pagination'), - options: { - meta: { - baseUri: '', - }, - query: { - limit: { - default: config.pagination.limit, - }, - }, - results: { - name: 'data', - }, - routes: { - include: config.pagination.include, - exclude: ['*'], - }, - }, - }) - - await server.register({ - plugin: require('./routes'), - routes: { prefix: '/api' }, - options: config, - }) - - return mountServer('Webhook API', server) -} diff --git a/packages/core-webhooks/lib/server/routes.js b/packages/core-webhooks/lib/server/routes.js deleted file mode 100644 index 6f7fe33535..0000000000 --- a/packages/core-webhooks/lib/server/routes.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Register webhook routes. - * @param {Hapi.Server} server - * @param {Object} options - * @return {void} - */ -const register = async (server, options) => { - const handler = require('./handler') - - server.route([ - { - method: 'GET', - path: '/webhooks', - ...handler.index, - }, - { - method: 'POST', - path: '/webhooks', - ...handler.store, - }, - { - method: 'GET', - path: '/webhooks/{id}', - ...handler.show, - }, - { - method: 'PUT', - path: '/webhooks/{id}', - ...handler.update, - }, - { - method: 'DELETE', - path: '/webhooks/{id}', - ...handler.destroy, - }, - ]) -} - -/** - * The struct used by hapi.js. - * @type {Object} - */ -exports.plugin = { - name: 'Ark Webhooks API', - version: '0.1.0', - register, -} diff --git a/packages/core-webhooks/lib/server/schema.js b/packages/core-webhooks/lib/server/schema.js deleted file mode 100644 index 7dc51fe561..0000000000 --- a/packages/core-webhooks/lib/server/schema.js +++ /dev/null @@ -1,89 +0,0 @@ -const Joi = require('joi') - -const conditions = [ - 'between', - 'contains', - 'eq', - 'falsy', - 'gt', - 'gte', - 'lt', - 'lte', - 'ne', - 'not-between', - 'regexp', - 'truthy', -] - -/** - * @return {Object} - */ -exports.index = { - query: { - page: Joi.number() - .integer() - .positive(), - limit: Joi.number() - .integer() - .positive(), - }, -} - -/** - * @return {Object} - */ -exports.show = { - params: { - id: Joi.string(), - }, -} - -/** - * @return {Object} - */ -exports.store = { - payload: { - event: Joi.string().required(), - target: Joi.string() - .required() - .uri(), - enabled: Joi.boolean().default(true), - conditions: Joi.array().items( - Joi.object({ - key: Joi.string(), - value: Joi.any(), - condition: Joi.string().valid(conditions), - }), - ), - }, -} - -/** - * @return {Object} - */ -exports.update = { - params: { - id: Joi.string(), - }, - payload: { - event: Joi.string(), - target: Joi.string().uri(), - enabled: Joi.boolean(), - conditions: Joi.array().items( - Joi.object({ - key: Joi.string(), - value: Joi.any(), - condition: Joi.string().valid(conditions), - }), - ), - }, -} - -/** - * @return {Object} - */ -exports.destroy = { - params: { - id: Joi.string(), - }, -} diff --git a/packages/core-webhooks/lib/server/transformer.js b/packages/core-webhooks/lib/server/transformer.js deleted file mode 100644 index f3a50a77ee..0000000000 --- a/packages/core-webhooks/lib/server/transformer.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Turns a "webhooks" object into a generic object. - * @param {Object} model - * @return {Object} - */ -module.exports = model => ({ - id: model.id, - event: model.event, - target: model.target, - token: model.token, - enabled: model.enabled, - conditions: model.conditions, -}) diff --git a/packages/core-webhooks/lib/server/utils.js b/packages/core-webhooks/lib/server/utils.js deleted file mode 100644 index ad6a5a4ee6..0000000000 --- a/packages/core-webhooks/lib/server/utils.js +++ /dev/null @@ -1,100 +0,0 @@ -/* eslint max-len: "off" */ - -const Boom = require('boom') - -/** - * Transform the given data into a resource. - * @param {Hapi.Request} request - * @param {Object} data - * @return {Object} - */ -const transformResource = (request, data) => require('./transformer')(data) - -/** - * Transform the given data into a collection. - * @param {Hapi.Request} request - * @param {Object} data - * @param {Object} transformer - * @return {Array} - */ -const transformCollection = (request, data, transformer) => - data.map(d => transformResource(request, d, transformer)) - -/** - * Create a pagination object for the request. - * @param {Hapi.Request} request - * @return {Object} - */ -const paginate = request => ({ - offset: (request.query.page - 1) * request.query.limit, - limit: request.query.limit, -}) - -/** - * Respond with a resource. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Hapi.Response} - */ -const respondWithResource = (request, data, transformer) => - data - ? { data: transformResource(request, data, transformer) } - : Boom.notFound() - -/** - * Respond with a collection. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Object} - */ -const respondWithCollection = (request, data, transformer) => ({ - data: transformCollection(request, data, transformer), -}) - -/** - * Alias of "transformResource". - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Hapi.Response} - */ -const toResource = (request, data, transformer) => - transformResource(request, data, transformer) - -/** - * Alias of "transformCollection". - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Hapi.Response} - */ -const toCollection = (request, data, transformer) => - transformCollection(request, data, transformer) - -/** - * Transform the given data into a pagination. - * @param {Hapi.Request} request - * @param {Object} data - * @param {String} transformer - * @return {Hapi.Response} - */ -const toPagination = (request, data, transformer) => ({ - results: transformCollection(request, data.rows, transformer), - totalCount: data.count, -}) - -/** - * @type {Object} - */ -module.exports = { - transformResource, - transformCollection, - paginate, - respondWithResource, - respondWithCollection, - toResource, - toCollection, - toPagination, -} diff --git a/packages/core-webhooks/package.json b/packages/core-webhooks/package.json index 6cde8d0379..2bc0ab404c 100644 --- a/packages/core-webhooks/package.json +++ b/packages/core-webhooks/package.json @@ -1,39 +1,60 @@ { - "name": "@arkecosystem/core-webhooks", - "description": "Webhooks for Ark Core", - "version": "0.2.0", - "contributors": [ - "Brian Faust " - ], - "license": "MIT", - "main": "lib/index.js", - "scripts": { - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "cross-env ARK_ENV=test jest --runInBand --watch", - "test:watch:all": "cross-env ARK_ENV=test jest --runInBand --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-http-utils": "~0.2", - "axios": "^0.18.0", - "boom": "^7.3.0", - "fs-extra": "^7.0.1", - "hapi-pagination": "^2.0.1", - "joi": "^14.3.0", - "sequelize": "^4.41.2", - "sqlite3": "^4.0.4", - "umzug": "^2.2.0" - }, - "devDependencies": { - "@arkecosystem/core-test-utils": "~0.2" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core-webhooks", + "description": "Webhooks for Ark Core", + "version": "2.1.0", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "bash ../../scripts/pre-test.sh", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-http-utils": "^2.1.0", + "@types/fs-extra": "^5.0.4", + "@types/joi": "^14.0.1", + "@types/sequelize": "^4.27.33", + "@types/sqlite3": "^3.1.3", + "@types/umzug": "^2.2.0", + "axios": "^0.18.0", + "boom": "^7.3.0", + "fs-extra": "^7.0.1", + "hapi-pagination": "^2.0.1", + "joi": "^14.3.0", + "sequelize": "^4.42.0", + "sqlite3": "^4.0.4", + "umzug": "^2.2.0" + }, + "devDependencies": { + "@arkecosystem/core-test-utils": "^2.1.0", + "@types/boom": "^7.2.1" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core-webhooks/src/conditions.ts b/packages/core-webhooks/src/conditions.ts new file mode 100644 index 0000000000..055bf5ad45 --- /dev/null +++ b/packages/core-webhooks/src/conditions.ts @@ -0,0 +1,14 @@ +const between = (actual, expected) => actual > expected.min && actual < expected.max; +const contains = (actual, expected) => actual.includes(expected); +const eq = (actual, expected) => actual === expected; +const falsy = actual => actual === false; +const gt = (actual, expected) => actual > expected; +const gte = (actual, expected) => actual >= expected; +const lt = (actual, expected) => actual < expected; +const lte = (actual, expected) => actual <= expected; +const ne = (actual, expected) => actual !== expected; +const notBetween = (actual, expected) => !between(actual, expected); +const regexp = (actual, expected) => new RegExp(expected).test(actual); +const truthy = actual => actual === true; + +export { between, contains, eq, falsy, gt, gte, lt, lte, ne, notBetween, regexp, truthy }; diff --git a/packages/core-webhooks/src/database/index.ts b/packages/core-webhooks/src/database/index.ts new file mode 100644 index 0000000000..ba1d355037 --- /dev/null +++ b/packages/core-webhooks/src/database/index.ts @@ -0,0 +1,157 @@ +import { app } from "@arkecosystem/core-container"; +import fs from "fs-extra"; +import path from "path"; +import Sequelize from "sequelize"; +import Umzug from "umzug"; + +class Database { + public connection: any; + public model: any; + + /** + * Set up the database connection. + * @param {Object} config + * @return {void} + */ + public async setUp(config) { + if (this.connection) { + throw new Error("Webhooks database already initialised"); + } + + if (config.dialect === "sqlite" && config.storage !== ":memory:") { + await fs.ensureFile(config.storage); + } + + this.connection = new Sequelize({ + ...config, + ...{ operatorsAliases: Sequelize.Op }, + }); + + try { + await this.connection.authenticate(); + await this.__runMigrations(); + this.__registerModels(); + } catch (error) { + app.forceExit("Unable to connect to the database!", error); + } + } + + /** + * Paginate all webhooks. + * @param {Object} params + * @return {Object} + */ + public paginate(params) { + return this.model.findAndCountAll(params); + } + + /** + * Get a webhook for the given id. + * @param {Number} id + * @return {Object} + */ + public findById(id) { + return this.model.findById(id); + } + + /** + * Get all webhooks for the given event. + * @param {String} event + * @return {Array} + */ + public findByEvent(event) { + return this.model.findAll({ where: { event } }); + } + + /** + * Store a new webhook. + * @param {Object} data + * @return {Object} + */ + public create(data) { + return this.model.create(data); + } + + /** + * Update the webhook for the given id. + * @param {Number} id + * @param {Object} data + * @return {Boolean} + */ + public async update(id, data) { + try { + const webhook = await this.model.findById(id); + + webhook.update(data); + + return true; + } catch (e) { + return false; + } + } + + /** + * Destroy the webhook for the given id. + * @param {Number} id + * @return {Boolean} + */ + public async destroy(id) { + try { + const webhook = await this.model.findById(id); + + webhook.destroy(); + + return true; + } catch (e) { + return false; + } + } + + /** + * Run all migrations. + * @return {Boolean} + */ + public async __runMigrations() { + const umzug = new Umzug({ + storage: "sequelize", + storageOptions: { + sequelize: this.connection, + }, + migrations: { + params: [this.connection.getQueryInterface(), Sequelize], + path: path.join(__dirname, "migrations"), + }, + }); + + return umzug.up(); + } + + /** + * Register all models. + * @return {void} + */ + public __registerModels() { + this.model = this.connection.define( + "webhook", + { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + event: Sequelize.STRING, + target: Sequelize.STRING, + conditions: Sequelize.JSON, + token: { + unique: true, + type: Sequelize.STRING, + }, + enabled: Sequelize.BOOLEAN, + }, + {}, + ); + } +} + +export const database = new Database(); diff --git a/packages/core-webhooks/src/database/migrations/20180305163843-create-webhook.ts b/packages/core-webhooks/src/database/migrations/20180305163843-create-webhook.ts new file mode 100644 index 0000000000..b3fa71da0f --- /dev/null +++ b/packages/core-webhooks/src/database/migrations/20180305163843-create-webhook.ts @@ -0,0 +1,49 @@ +/** + * The webhooks migration. + * @type {Object} + */ +module.exports = { + /** + * Run the migrations. + * @param {Sequelize.QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @return {void} + */ + async up(queryInterface, Sequelize) { + await queryInterface.createTable("webhooks", { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + event: Sequelize.STRING, + target: Sequelize.STRING, + conditions: Sequelize.JSON, + token: { + unique: true, + type: Sequelize.STRING, + }, + enabled: Sequelize.BOOLEAN, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); + + queryInterface.addIndex("webhooks", ["event"]); + }, + /** + * Reverse the migrations. + * @param {Sequelize.QueryInterface} queryInterface + * @param {Sequelize} Sequelize + * @return {void} + */ + async down(queryInterface, Sequelize) { + return queryInterface.dropTable("webhooks"); + }, +}; diff --git a/packages/core-webhooks/src/defaults.ts b/packages/core-webhooks/src/defaults.ts new file mode 100644 index 0000000000..098ccc6fab --- /dev/null +++ b/packages/core-webhooks/src/defaults.ts @@ -0,0 +1,18 @@ +export const defaults = { + enabled: process.env.CORE_WEBHOOKS_ENABLED, + database: { + dialect: "sqlite", + storage: `${process.env.CORE_PATH_DATA}/webhooks.sqlite`, + logging: process.env.CORE_DB_LOGGING, + }, + server: { + enabled: process.env.CORE_WEBHOOKS_API_ENABLED, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + pagination: { + limit: 100, + include: ["/api/webhooks"], + }, + }, +}; diff --git a/packages/core-webhooks/src/index.ts b/packages/core-webhooks/src/index.ts new file mode 100644 index 0000000000..109e380a5e --- /dev/null +++ b/packages/core-webhooks/src/index.ts @@ -0,0 +1,37 @@ +import { Container, Logger } from "@arkecosystem/core-interfaces"; +import { database } from "./database"; +import { defaults } from "./defaults"; +import { webhookManager } from "./manager"; +import { startServer } from "./server"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "webhooks", + async register(container: Container.IContainer, options) { + const logger = container.resolvePlugin("logger"); + + if (!options.enabled) { + logger.info("Webhooks are disabled :grey_exclamation:"); + + return; + } + + await database.setUp(options.database); + + await webhookManager.setUp(); + + if (options.server.enabled) { + return startServer(options.server); + } + + logger.info("Webhooks API server is disabled :grey_exclamation:"); + }, + async deregister(container: Container.IContainer, options) { + if (options.server.enabled) { + container.resolvePlugin("logger").info("Stopping Webhook API"); + + return container.resolvePlugin("webhooks").stop(); + } + }, +}; diff --git a/packages/core-webhooks/src/manager.ts b/packages/core-webhooks/src/manager.ts new file mode 100644 index 0000000000..953cc02c2d --- /dev/null +++ b/packages/core-webhooks/src/manager.ts @@ -0,0 +1,87 @@ +import { app } from "@arkecosystem/core-container"; +import { Blockchain, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; +import axios from "axios"; +import * as conditions from "./conditions"; +import { database } from "./database"; + +class WebhookManager { + public config: any; + public logger = app.resolvePlugin("logger"); + + /** + * Set up the webhook app. + * @return {void} + */ + public async setUp() { + const emitter = app.resolvePlugin("event-emitter"); + const blockchain = app.resolvePlugin("blockchain"); + + for (const event of blockchain.getEvents()) { + emitter.on(event, async payload => { + const webhooks = await database.findByEvent(event); + + for (const webhook of this.getMatchingWebhooks(webhooks, payload)) { + try { + const response = await axios.post( + webhook.target, + { + timestamp: +new Date(), + data: payload, + event: webhook.event, + }, + { + headers: { + Authorization: webhook.token, + }, + }, + ); + + this.logger.debug( + `Webhooks Job ${webhook.id} completed! Event [${webhook.event}] has been transmitted to [${ + webhook.target + }] with a status of [${response.status}].`, + ); + } catch (error) { + this.logger.error(`Webhooks Job ${webhook.id} failed: ${error.message}`); + } + } + }); + } + } + + /** + * Get all webhooks. + * @param {Array} webhooks + * @param {Object} payload + * @return {Array} + */ + private getMatchingWebhooks(webhooks, payload) { + const matches = []; + + for (const webhook of webhooks) { + if (!webhook.enabled) { + continue; + } + + if (!webhook.conditions || (Array.isArray(webhook.conditions) && !webhook.conditions.length)) { + matches.push(webhook); + + continue; + } + + for (const condition of webhook.conditions) { + const satisfies = conditions[condition.condition]; + + if (!satisfies(payload[condition.key], condition.value)) { + continue; + } + + matches.push(webhook); + } + } + + return matches; + } +} + +export const webhookManager = new WebhookManager(); diff --git a/packages/core-webhooks/src/server/handler.ts b/packages/core-webhooks/src/server/handler.ts new file mode 100644 index 0000000000..42ad2714ad --- /dev/null +++ b/packages/core-webhooks/src/server/handler.ts @@ -0,0 +1,110 @@ +import Boom from "boom"; +import { randomBytes } from "crypto"; +import { database } from "../database"; +import * as schema from "./schema"; +import * as utils from "./utils"; + +/** + * @type {Object} + */ +const index = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const webhooks = await database.paginate(utils.paginate(request)); + + return utils.toPagination(request, webhooks); + }, +}; + +/** + * @type {Object} + */ +const store = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const token = randomBytes(32).toString("hex"); + + request.payload.token = token.substring(0, 32); + + const webhook = await database.create(request.payload); + webhook.token = token; + + return h.response(utils.respondWithResource(request, webhook)).code(201); + }, + options: { + plugins: { + pagination: { + enabled: false, + }, + }, + validate: schema.store, + }, +}; + +/** + * @type {Object} + */ +const show = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + const webhook = await database.findById(request.params.id); + delete webhook.token; + + return utils.respondWithResource(request, webhook); + }, + options: { + validate: schema.show, + }, +}; + +/** + * @type {Object} + */ +const update = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + await database.update(request.params.id, request.payload); + + return h.response(null).code(204); + }, + options: { + validate: schema.update, + }, +}; + +/** + * @type {Object} + */ +const destroy = { + /** + * @param {Hapi.Request} request + * @param {Hapi.Toolkit} h + * @return {Hapi.Response} + */ + async handler(request, h) { + await database.destroy(request.params.id); + + return h.response(null).code(204); + }, + options: { + validate: schema.destroy, + }, +}; + +export { index, store, show, update, destroy }; diff --git a/packages/core-webhooks/src/server/index.ts b/packages/core-webhooks/src/server/index.ts new file mode 100644 index 0000000000..889d13d9a9 --- /dev/null +++ b/packages/core-webhooks/src/server/index.ts @@ -0,0 +1,54 @@ +import { createServer, mountServer, plugins } from "@arkecosystem/core-http-utils"; +import { registerRoutes } from "./routes"; + +export async function startServer(config) { + const server = await createServer({ + host: config.host, + port: config.port, + routes: { + cors: true, + validate: { + async failAction(request, h, err) { + throw err; + }, + }, + }, + }); + + await server.register({ + plugin: plugins.whitelist, + options: { + whitelist: config.whitelist, + name: "Webhook API", + }, + }); + + await server.register({ + plugin: require("hapi-pagination"), + options: { + meta: { + baseUri: "", + }, + query: { + limit: { + default: config.pagination.limit, + }, + }, + results: { + name: "data", + }, + routes: { + include: config.pagination.include, + exclude: ["*"], + }, + }, + }); + + await server.register({ + plugin: registerRoutes, + routes: { prefix: "/api" }, + options: config, + }); + + return mountServer("Webhook API", server); +} diff --git a/packages/core-webhooks/src/server/routes.ts b/packages/core-webhooks/src/server/routes.ts new file mode 100644 index 0000000000..1e2bb7c4ad --- /dev/null +++ b/packages/core-webhooks/src/server/routes.ts @@ -0,0 +1,35 @@ +import { destroy, index, show, store, update } from "./handler"; + +export const registerRoutes = { + name: "Ark Webhooks API", + version: "0.1.0", + async register(server, options) { + server.route([ + { + method: "GET", + path: "/webhooks", + ...index, + }, + { + method: "POST", + path: "/webhooks", + ...store, + }, + { + method: "GET", + path: "/webhooks/{id}", + ...show, + }, + { + method: "PUT", + path: "/webhooks/{id}", + ...update, + }, + { + method: "DELETE", + path: "/webhooks/{id}", + ...destroy, + }, + ]); + }, +}; diff --git a/packages/core-webhooks/src/server/schema.ts b/packages/core-webhooks/src/server/schema.ts new file mode 100644 index 0000000000..5bae3ad945 --- /dev/null +++ b/packages/core-webhooks/src/server/schema.ts @@ -0,0 +1,76 @@ +import Joi from "joi"; + +const conditions = [ + "between", + "contains", + "eq", + "falsy", + "gt", + "gte", + "lt", + "lte", + "ne", + "not-between", + "regexp", + "truthy", +]; + +const index = { + query: { + page: Joi.number() + .integer() + .positive(), + limit: Joi.number() + .integer() + .positive(), + }, +}; + +const show = { + params: { + id: Joi.string(), + }, +}; + +const store = { + payload: { + event: Joi.string().required(), + target: Joi.string() + .required() + .uri(), + enabled: Joi.boolean().default(true), + conditions: Joi.array().items( + Joi.object({ + key: Joi.string(), + value: Joi.any(), + condition: Joi.string().valid(conditions), + }), + ), + }, +}; + +const update = { + params: { + id: Joi.string(), + }, + payload: { + event: Joi.string(), + target: Joi.string().uri(), + enabled: Joi.boolean(), + conditions: Joi.array().items( + Joi.object({ + key: Joi.string(), + value: Joi.any(), + condition: Joi.string().valid(conditions), + }), + ), + }, +}; + +const destroy = { + params: { + id: Joi.string(), + }, +}; + +export { index, show, store, update, destroy }; diff --git a/packages/core-webhooks/src/server/transformer.ts b/packages/core-webhooks/src/server/transformer.ts new file mode 100644 index 0000000000..801abde24b --- /dev/null +++ b/packages/core-webhooks/src/server/transformer.ts @@ -0,0 +1,10 @@ +export function transform(model) { + return { + id: model.id, + event: model.event, + target: model.target, + token: model.token, + enabled: model.enabled, + conditions: model.conditions, + }; +} diff --git a/packages/core-webhooks/src/server/utils.ts b/packages/core-webhooks/src/server/utils.ts new file mode 100644 index 0000000000..eebd60d875 --- /dev/null +++ b/packages/core-webhooks/src/server/utils.ts @@ -0,0 +1,84 @@ +import Boom from "boom"; +import { transform } from "./transformer"; + +/** + * Transform the given data into a resource. + * @param {Hapi.Request} request + * @param {Object} data + * @return {Object} + */ +const transformResource = (request, data) => transform(data); + +/** + * Transform the given data into a collection. + * @param {Hapi.Request} request + * @param {Object} data + * @return {Array} + */ +const transformCollection = (request, data) => data.map(d => transformResource(request, d)); + +/** + * Create a pagination object for the request. + * @param {Hapi.Request} request + * @return {Object} + */ +const paginate = request => ({ + offset: (request.query.page - 1) * request.query.limit, + limit: request.query.limit, +}); + +/** + * Respond with a resource. + * @param {Hapi.Request} request + * @param {Object} data + * @return {Hapi.Response} + */ +const respondWithResource = (request, data) => (data ? { data: transformResource(request, data) } : Boom.notFound()); + +/** + * Respond with a collection. + * @param {Hapi.Request} request + * @param {Object} data + * @return {Object} + */ +const respondWithCollection = (request, data) => ({ + data: transformCollection(request, data), +}); + +/** + * Alias of "transformResource". + * @param {Hapi.Request} request + * @param {Object} data + * @return {Hapi.Response} + */ +const toResource = (request, data) => transformResource(request, data); + +/** + * Alias of "transformCollection". + * @param {Hapi.Request} request + * @param {Object} data + * @return {Hapi.Response} + */ +const toCollection = (request, data) => transformCollection(request, data); + +/** + * Transform the given data into a pagination. + * @param {Hapi.Request} request + * @param {Object} data + * @return {Hapi.Response} + */ +const toPagination = (request, data) => ({ + results: transformCollection(request, data.rows), + totalCount: data.count, +}); + +export { + transformResource, + transformCollection, + paginate, + respondWithResource, + respondWithCollection, + toResource, + toCollection, + toPagination, +}; diff --git a/packages/core-webhooks/tsconfig.json b/packages/core-webhooks/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-webhooks/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md deleted file mode 100644 index f3d7a18c97..0000000000 --- a/packages/core/CHANGELOG.md +++ /dev/null @@ -1,71 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 2.0.16 - 2018-12-17 - -### Fixed - -- Prevent the list of peers to become too short - -## 2.0.15 - 2018-12-11 - -### Fixed - -- Ensure no local peers are enlisted -- Ensure the IP of the TCP connection is used - -## 2.0.14 - 2018-12-10 - -### Fixed - -- Reset last downloaded block when block is discarded - -## 2.0.13 - 2018-12-07 - -### Fixed - -- Ensure safe integer range for block height lookups via API - -## 2.0.12 - 2018-12-06 - -### Fixed - -- Perform second-signature checks in the `canApply` logic of multi-signatures - -## 2.0.11 - 2018-12-05 - -### Added - -- Store executed migrations in the database - -### Changed - -- Increase cache generation timeout and make it configurable - -## 2.0.1 - 2018-12-05 - -### Changed - -- Improved performance for block and transaction queries by adding more indices on critical columns - -### Fixed - -- Take milestones into account for supply calculations - -## 2.0.0 - 2018-12-03 - -### Changed - -- Dropped node.js 9 as minimum requirement in favour of node.js 10 - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/core/LICENSE b/packages/core/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/core/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/core/README.md b/packages/core/README.md index b8eaebfc97..9b1ca1c190 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -14,11 +14,11 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Kristjan Košič](https://github.com/kristjank) -- [Brian Faust](https://github.com/faustbrian) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [All Contributors](../../../../contributors) +- [All Contributors](../../../../contributors) +- [Alex Barnsley](https://github.com/alexbarnsley) +- [Brian Faust](https://github.com/faustbrian) +- [François-Xavier Thoorens](https://github.com/fix) +- [Kristjan Košič](https://github.com/kristjank) ## License diff --git a/packages/core/__tests__/__support__/app.ts b/packages/core/__tests__/__support__/app.ts new file mode 100644 index 0000000000..5813301bc6 --- /dev/null +++ b/packages/core/__tests__/__support__/app.ts @@ -0,0 +1,11 @@ +import { resolve } from "path"; + +export const opts = { + data: "~/.core", + config: resolve(__dirname, "./config"), + token: "ark", + network: "testnet", + skipPlugins: true, +}; + +export const version = "2.0.0"; diff --git a/packages/core/__tests__/__support__/config/delegates.json b/packages/core/__tests__/__support__/config/delegates.json new file mode 100644 index 0000000000..822250478e --- /dev/null +++ b/packages/core/__tests__/__support__/config/delegates.json @@ -0,0 +1,55 @@ +{ + "secrets": [ + "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", + "venue below waste gather spin cruise title still boost mother flash tuna", + "craft imitate step mixture patch forest volcano business charge around girl confirm", + "fatal hat sail asset chase barrel pluck bag approve coral slab bright", + "flash thank strike stove grain remove match reflect excess present beyond matrix", + "various present shine domain outdoor neck soup diesel limit express genuine tuna", + "hurdle pulse sheriff anchor two hope income pattern hazard bacon book night", + "glow boss party require silk interest pyramid marriage try wisdom snow grab", + "direct palace screen shuffle world fit produce rubber jelly gather river ordinary", + "wall ketchup shed word twist flip knock liar merge rural ill pond", + "measure blue volcano month orphan only cupboard found laugh peasant drama monitor", + "scissors sort pause medal target diesel reveal stock maze party gauge vacant", + "hand anchor hip pyramid taxi vote celery clap tribe damage shrimp brave", + "merge thunder detect stove else bottom favorite doll learn festival basic basic", + "educate attitude rely combine treat balcony west reopen coil west grab depth", + "advance silver advance squeeze load stone middle garden perfect invest field lounge", + "prison tobacco acquire stone dignity palace note decade they current lesson robot", + "team impact stadium year security steak harsh vacant fire pelican until olympic", + "walk intact ice prevent fit trial frog glory monkey once grunt gentle", + "same lens parrot suspect just sunset frown exercise lemon two mistake robust", + "skill insect issue crazy erase okay govern upgrade bounce dress motor athlete", + "peasant alert hard deposit naive follow page fiscal normal awful wedding history", + "resemble abandon same total oppose noise dune order fatal rhythm pink science", + "wide mesh ketchup acquire bright day mountain final below hamster scout drive", + "half weasel poet better rocket fan help left blade soda argue system", + "target sort neutral address language spike measure jaguar glance strong drop zone", + "race total stage trap wool believe twin pudding claim claim eternal miss", + "parade isolate wing vague magic husband acid skin skate path fence rib", + "neither fine dry priority example obtain bread reopen afford coyote milk minor", + "token atom lemon game charge area goose hotel excess endless spice oblige", + "pledge buffalo finish pipe mule popular bind clinic draft salon swamp purpose", + "west hat hold stand unique panther cable extend spell shaft injury reopen", + "van impulse pole install profit excuse give auction expire remain skate input", + "wrist maze potato april survey burden bamboo knee foot carry speak prison", + "three toddler copy owner pencil minimum doctor orange bottom ice detail design", + "ceiling warrior person thing whisper jeans black cricket drift ahead tornado typical", + "obvious mutual tone usual valve credit soccer mention also clown main box", + "valve slot soft green scale menu anxiety live drill legend upgrade chimney", + "twist comfort mule weather print oven cabin seek punch rival prepare sphere", + "say tumble glass argue aware service force caution until grocery hammer fetch", + "idea illegal empty frozen canvas arctic number poet rely track size obscure", + "chalk try large tower shed warfare blade clerk fame second charge tobacco", + "category nice verb fox start able brass climb boss luggage voice whale", + "favorite emotion trumpet visual welcome spend fine lock image review garage opera", + "waste axis humor auction next salmon much margin useful glimpse insect rotate", + "remember rose genuine police guard old flavor parent gain cross twelve first", + "coil tray elder mask circle crush anger electric harbor onion grab will", + "shove airport bus gather radio derive below horse canvas crime tribe adjust", + "retire lend burden cricket able sheriff output grocery empty scorpion flat inquiry", + "agree grain record shift fossil summer hunt mutual net vast behind pilot", + "decide rhythm oyster lady they merry betray jelly coyote solve episode then" + ] +} diff --git a/packages/core/__tests__/__support__/config/genesisBlock.json b/packages/core/__tests__/__support__/config/genesisBlock.json new file mode 100644 index 0000000000..a09a1fa3a0 --- /dev/null +++ b/packages/core/__tests__/__support__/config/genesisBlock.json @@ -0,0 +1,2210 @@ +{ + "version": 0, + "totalAmount": 12500000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + "timestamp": 0, + "numberOfTransactions": 153, + "payloadLength": 35960, + "previousBlock": null, + "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", + "transactions": [ + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", + "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", + "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", + "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", + "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", + "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", + "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", + "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", + "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", + "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", + "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", + "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", + "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", + "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", + "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", + "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", + "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", + "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", + "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", + "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", + "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", + "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", + "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", + "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", + "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", + "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", + "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", + "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", + "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", + "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", + "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", + "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", + "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", + "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", + "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", + "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", + "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", + "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", + "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", + "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", + "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", + "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", + "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", + "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", + "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", + "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", + "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", + "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", + "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", + "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", + "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245100000000000, + "fee": 0, + "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", + "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" + } + }, + "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", + "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", + "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" + } + }, + "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", + "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", + "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" + } + }, + "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", + "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", + "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" + } + }, + "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", + "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", + "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" + } + }, + "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", + "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", + "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" + } + }, + "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", + "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", + "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" + } + }, + "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", + "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", + "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" + } + }, + "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", + "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", + "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" + } + }, + "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", + "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", + "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" + } + }, + "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", + "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", + "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" + } + }, + "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", + "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", + "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" + } + }, + "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", + "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", + "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" + } + }, + "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", + "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", + "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" + } + }, + "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", + "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", + "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" + } + }, + "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", + "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", + "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" + } + }, + "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", + "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", + "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" + } + }, + "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", + "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", + "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" + } + }, + "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", + "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", + "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" + } + }, + "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", + "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", + "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" + } + }, + "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", + "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", + "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" + } + }, + "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", + "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", + "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" + } + }, + "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", + "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", + "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" + } + }, + "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", + "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", + "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" + } + }, + "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", + "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", + "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" + } + }, + "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", + "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", + "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" + } + }, + "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", + "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", + "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" + } + }, + "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", + "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", + "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" + } + }, + "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", + "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", + "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" + } + }, + "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", + "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", + "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" + } + }, + "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", + "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", + "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" + } + }, + "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", + "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", + "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" + } + }, + "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", + "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", + "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" + } + }, + "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", + "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", + "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" + } + }, + "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", + "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", + "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" + } + }, + "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", + "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", + "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" + } + }, + "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", + "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", + "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" + } + }, + "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", + "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", + "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" + } + }, + "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", + "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", + "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" + } + }, + "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", + "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", + "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" + } + }, + "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", + "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", + "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" + } + }, + "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", + "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", + "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" + } + }, + "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", + "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", + "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" + } + }, + "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", + "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", + "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" + } + }, + "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", + "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", + "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" + } + }, + "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", + "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", + "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" + } + }, + "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", + "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", + "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" + } + }, + "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", + "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", + "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" + } + }, + "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", + "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", + "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" + } + }, + "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", + "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", + "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" + } + }, + "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", + "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", + "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" + } + }, + "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", + "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", + "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "timestamp": 0, + "asset": { + "votes": ["+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357"] + }, + "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", + "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", + "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", + "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + "timestamp": 0, + "asset": { + "votes": ["+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80"] + }, + "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", + "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", + "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", + "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + "timestamp": 0, + "asset": { + "votes": ["+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e"] + }, + "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", + "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", + "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", + "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + "timestamp": 0, + "asset": { + "votes": ["+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055"] + }, + "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", + "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", + "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", + "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + "timestamp": 0, + "asset": { + "votes": ["+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2"] + }, + "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", + "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", + "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", + "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + "timestamp": 0, + "asset": { + "votes": ["+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e"] + }, + "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", + "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", + "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", + "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + "timestamp": 0, + "asset": { + "votes": ["+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883"] + }, + "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", + "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", + "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + "timestamp": 0, + "asset": { + "votes": ["+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252"] + }, + "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", + "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", + "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", + "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + "timestamp": 0, + "asset": { + "votes": ["+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4"] + }, + "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", + "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", + "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", + "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + "timestamp": 0, + "asset": { + "votes": ["+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28"] + }, + "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", + "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", + "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", + "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + "timestamp": 0, + "asset": { + "votes": ["+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d"] + }, + "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", + "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", + "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", + "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + "timestamp": 0, + "asset": { + "votes": ["+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294"] + }, + "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", + "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", + "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", + "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + "timestamp": 0, + "asset": { + "votes": ["+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983"] + }, + "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", + "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", + "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", + "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + "timestamp": 0, + "asset": { + "votes": ["+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964"] + }, + "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", + "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", + "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", + "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + "timestamp": 0, + "asset": { + "votes": ["+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5"] + }, + "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", + "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", + "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", + "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + "timestamp": 0, + "asset": { + "votes": ["+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95"] + }, + "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", + "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", + "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", + "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + "timestamp": 0, + "asset": { + "votes": ["+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a"] + }, + "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", + "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", + "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", + "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + "timestamp": 0, + "asset": { + "votes": ["+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689"] + }, + "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", + "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", + "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", + "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "timestamp": 0, + "asset": { + "votes": ["+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a"] + }, + "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", + "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", + "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", + "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + "timestamp": 0, + "asset": { + "votes": ["+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a"] + }, + "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", + "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", + "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", + "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + "timestamp": 0, + "asset": { + "votes": ["+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f"] + }, + "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", + "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", + "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", + "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + "timestamp": 0, + "asset": { + "votes": ["+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751"] + }, + "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", + "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", + "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", + "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + "timestamp": 0, + "asset": { + "votes": ["+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb"] + }, + "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", + "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", + "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", + "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + "timestamp": 0, + "asset": { + "votes": ["+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d"] + }, + "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", + "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", + "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + "timestamp": 0, + "asset": { + "votes": ["+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a"] + }, + "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", + "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", + "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", + "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + "timestamp": 0, + "asset": { + "votes": ["+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe"] + }, + "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", + "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", + "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", + "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + "timestamp": 0, + "asset": { + "votes": ["+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e"] + }, + "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", + "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", + "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", + "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + "timestamp": 0, + "asset": { + "votes": ["+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9"] + }, + "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", + "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", + "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", + "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + "timestamp": 0, + "asset": { + "votes": ["+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca"] + }, + "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", + "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", + "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", + "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + "timestamp": 0, + "asset": { + "votes": ["+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c"] + }, + "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", + "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", + "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", + "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + "timestamp": 0, + "asset": { + "votes": ["+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24"] + }, + "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", + "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", + "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", + "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + "timestamp": 0, + "asset": { + "votes": ["+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f"] + }, + "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", + "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", + "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", + "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + "timestamp": 0, + "asset": { + "votes": ["+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b"] + }, + "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", + "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", + "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", + "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + "timestamp": 0, + "asset": { + "votes": ["+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12"] + }, + "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", + "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", + "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", + "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + "timestamp": 0, + "asset": { + "votes": ["+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904"] + }, + "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", + "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", + "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", + "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + "timestamp": 0, + "asset": { + "votes": ["+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c"] + }, + "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", + "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", + "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + "timestamp": 0, + "asset": { + "votes": ["+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd"] + }, + "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", + "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", + "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + "timestamp": 0, + "asset": { + "votes": ["+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647"] + }, + "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", + "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", + "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", + "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + "timestamp": 0, + "asset": { + "votes": ["+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd"] + }, + "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", + "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", + "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", + "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + "timestamp": 0, + "asset": { + "votes": ["+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564"] + }, + "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", + "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", + "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", + "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + "timestamp": 0, + "asset": { + "votes": ["+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0"] + }, + "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", + "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", + "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", + "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + "timestamp": 0, + "asset": { + "votes": ["+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b"] + }, + "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", + "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", + "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", + "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + "timestamp": 0, + "asset": { + "votes": ["+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc"] + }, + "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", + "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", + "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", + "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + "timestamp": 0, + "asset": { + "votes": ["+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565"] + }, + "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", + "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", + "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", + "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + "timestamp": 0, + "asset": { + "votes": ["+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374"] + }, + "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", + "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", + "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", + "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + "timestamp": 0, + "asset": { + "votes": ["+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93"] + }, + "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", + "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", + "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", + "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + "timestamp": 0, + "asset": { + "votes": ["+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17"] + }, + "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", + "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", + "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", + "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + "timestamp": 0, + "asset": { + "votes": ["+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1"] + }, + "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", + "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", + "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", + "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + "timestamp": 0, + "asset": { + "votes": ["+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d"] + }, + "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", + "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", + "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", + "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + "timestamp": 0, + "asset": { + "votes": ["+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37"] + }, + "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", + "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", + "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", + "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + "timestamp": 0, + "asset": { + "votes": ["+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a"] + }, + "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", + "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", + "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" + } + ], + "height": 1, + "id": "17184958558311101492", + "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" +} diff --git a/packages/core/__tests__/__support__/config/peers.json b/packages/core/__tests__/__support__/config/peers.json new file mode 100644 index 0000000000..fa4e124d8d --- /dev/null +++ b/packages/core/__tests__/__support__/config/peers.json @@ -0,0 +1,8 @@ +{ + "list": [ + { + "ip": "0.0.0.99", + "port": 4000 + } + ] +} diff --git a/packages/core/__tests__/__support__/config/plugins.js b/packages/core/__tests__/__support__/config/plugins.js new file mode 100644 index 0000000000..19429c3bec --- /dev/null +++ b/packages/core/__tests__/__support__/config/plugins.js @@ -0,0 +1,45 @@ +module.exports = { + "@arkecosystem/core-event-emitter": {}, + "@arkecosystem/core-logger-winston": { + transports: { + console: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + dailyRotate: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + }, + }, + "@arkecosystem/core-database-postgres": { + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || "core_development", + user: process.env.CORE_DB_USERNAME || "core", + password: process.env.CORE_DB_PASSWORD || "password", + }, + }, + "@arkecosystem/core-transaction-pool": { + enabled: !process.env.CORE_TRANSACTION_POOL_DISABLED, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + // 100+ years in the future to avoid our hardcoded transactions used in the + // tests to expire immediately + maxTransactionAge: 4036608000, + }, + "@arkecosystem/core-p2p": { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4000, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + "@arkecosystem/core-blockchain": { + fastRebuild: false, + }, + "@arkecosystem/core-forger": { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4000}`], + }, +}; diff --git a/packages/core/__tests__/commands/start-forger.test.ts b/packages/core/__tests__/commands/start-forger.test.ts new file mode 100644 index 0000000000..680a6a4478 --- /dev/null +++ b/packages/core/__tests__/commands/start-forger.test.ts @@ -0,0 +1,19 @@ +import "jest-extended"; +import { startForger, startRelay } from "../../src/commands"; +import { opts, version } from "../__support__/app"; + +describe("Commands - Start Forger", () => { + it("should be OK", async () => { + const relay = await startRelay(opts, version); + const forger = await startForger(opts, version); + + expect(relay.isReady).toBeTrue(); + expect(forger.isReady).toBeTrue(); + + await forger.tearDown(); + await relay.tearDown(); + + expect(forger.isReady).toBeFalse(); + expect(relay.isReady).toBeFalse(); + }); +}); diff --git a/packages/core/__tests__/commands/start-relay-and-forger.test.ts b/packages/core/__tests__/commands/start-relay-and-forger.test.ts new file mode 100644 index 0000000000..9b4001ee68 --- /dev/null +++ b/packages/core/__tests__/commands/start-relay-and-forger.test.ts @@ -0,0 +1,15 @@ +import "jest-extended"; +import { startRelayAndForger } from "../../src/commands"; +import { opts, version } from "../__support__/app"; + +describe("Commands - Start Relay & Forger", () => { + it("should be OK", async () => { + const app = await startRelayAndForger(opts, version); + + expect(app.isReady).toBeTrue(); + + await app.tearDown(); + + expect(app.isReady).toBeFalse(); + }); +}); diff --git a/packages/core/__tests__/commands/start-relay.test.ts b/packages/core/__tests__/commands/start-relay.test.ts new file mode 100644 index 0000000000..f8df498030 --- /dev/null +++ b/packages/core/__tests__/commands/start-relay.test.ts @@ -0,0 +1,16 @@ +import delay from "delay"; +import "jest-extended"; +import { startRelay } from "../../src/commands"; +import { opts, version } from "../__support__/app"; + +describe("Commands - Start Relay", () => { + it("should be OK", async () => { + const app = await startRelay(opts, version); + + expect(app.isReady).toBeTrue(); + + await app.tearDown(); + + expect(app.isReady).toBeFalse(); + }); +}); diff --git a/packages/core/bin/ark b/packages/core/bin/ark deleted file mode 100755 index b7557fd986..0000000000 --- a/packages/core/bin/ark +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env node - -const app = require('commander') -const bip38 = require('bip38') -const fs = require('fs') -const wif = require('wif') -const version = require('../package.json').version - -app.version(version) - -app - .command('start') - .description('start a relay node and the forger') - .option('-d, --data ', 'data directory', '~/.ark') - .option('-c, --config ', 'core config', '~/.ark/config') - .option('-t, --token ', 'token name', 'ark') - .option('-n, --network ', 'token network') - .option('-b, --bip38 ', 'forger bip38') - .option('-p, --password ', 'forger password') - .option('--network-start', 'force genesis network start', false) - .option('--disable-discovery', 'disable any peer discovery') - .option('--skip-discovery', 'skip the initial peer discovery') - .action(async (options) => require('../lib/start-relay-and-forger')(options, version)) - -app - .command('relay') - .description('start a relay node') - .option('-d, --data ', 'data directory', '~/.ark') - .option('-c, --config ', 'network config', '~/.ark/config') - .option('-t, --token ', 'token name', 'ark') - .option('-n, --network ', 'token network') - .option('-r, --remote ', 'remote peer for config') - .option('--network-start', 'force genesis network start', false) - .option('--disable-discovery', 'disable any peer discovery') - .option('--skip-discovery', 'skip the initial peer discovery') - .action(async (options) => require('../lib/start-relay')(options, version)) - -app - .command('forger') - .description('start the forger') - .option('-d, --data ', 'data directory', '~/.ark') - .option('-c, --config ', 'network config', '~/.ark/config') - .option('-t, --token ', 'token name', 'ark') - .option('-n, --network ', 'token network') - .option('-b, --bip38 ', 'forger bip38') - .option('-p, --password ', 'forger password') - .action(async (options) => require('../lib/start-forger')(options, version)) - -app - .command('forger-plain') - .description('set the delegate secret') - .option('-c, --config ', 'core config') - .option('-n, --network ', 'network') - .option('-s, --secret ', 'forger secret') - .action(async (options) => { - const delegatesConfig = `${options.config}/delegates.json` - if (!options.config || !fs.existsSync(delegatesConfig)) { - console.error('Missing or invalid delegates config path') - process.exit(1) - } - const delegates = require(delegatesConfig) - delegates.secrets = [options.secret] - delete delegates.bip38 - - fs.writeFileSync(delegatesConfig, JSON.stringify(delegates, null, 2)) - }) - -app - .command('forger-bip38') - .description('encrypt the delegate passphrase using bip38') - .option('-c, --config ', 'core config') - .option('-t, --token ', 'token name', 'ark') - .option('-n, --network ', 'token network') - .option('-s, --secret ', 'forger secret') - .option('-p, --password ', 'bip38 password') - .action(async (options) => { - const delegatesConfig = `${options.config}/delegates.json` - if (!options.config || !fs.existsSync(delegatesConfig)) { - console.error('Missing or invalid delegates config path') - process.exit(1) - } - const { configManager, crypto } = require('@arkecosystem/crypto') - configManager.setFromPreset(options.token, options.network) - - const keys = crypto.getKeys(options.secret) - const decoded = wif.decode(crypto.keysToWIF(keys)) - - const delegates = require(delegatesConfig) - delegates.bip38 = bip38.encrypt(decoded.privateKey, decoded.compressed, options.password) - delegates.secrets = [] // remove the plain text secrets in favour of bip38 - - fs.writeFileSync(delegatesConfig, JSON.stringify(delegates, null, 2)) - }) - -app - .command('*') - .action(env => { - app.help() - process.exit(0) - }) - -app.parse(process.argv) diff --git a/packages/core/lib/config/devnet/delegates.json b/packages/core/lib/config/devnet/delegates.json deleted file mode 100644 index 096f5472e1..0000000000 --- a/packages/core/lib/config/devnet/delegates.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "secrets": [] -} diff --git a/packages/core/lib/config/devnet/genesisBlock.json b/packages/core/lib/config/devnet/genesisBlock.json deleted file mode 100644 index 5b5e132d15..0000000000 --- a/packages/core/lib/config/devnet/genesisBlock.json +++ /dev/null @@ -1,896 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867", - "timestamp": 0, - "numberOfTransactions": 52, - "payloadLength": 11395, - "previousBlock": null, - "generatorPublicKey": "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", - "transactions": [ - { - "type": 0, - "amount": 12500000000000000, - "fee": 0, - "recipientId": "D6Z26L69gdk9qYmTv5uzk3uGepigtHY4ax", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0208e6835a8f020cfad439c059b89addc1ce21f8cab0af6e6957e22d3720bff8a4", - "signature": "304402203a3f0f80aad4e0561ae975f241f72a074245f1205d676d290d6e5630ed4c027502207b31fee68e64007c380a4b6baccd4db9b496daef5f7894676586e1347ac30a3b", - "id": "3e3817fd0c35bc36674f3874c2953fa3e35877cbcdb44a08bdc6083dbd39d572", - "senderId": "DLK7ts2DpkbeBjFamuFtHLoDAq5upDhCmf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02511f16ffb7b7e9afc12f04f317a11d9644e4be9eb5a5f64673946ad0f6336f34", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "02511f16ffb7b7e9afc12f04f317a11d9644e4be9eb5a5f64673946ad0f6336f34" - } - }, - "signature": "304402205f6acbc1b91787b97a02ce8dd2f511ec8ab8786a9e3ba058173a94e80a1b4d49022044ec8f8e7a14dbb661a3d9803484d220a5038488b99befb43b6a22a5b7c499d4", - "id": "cbff39a30fea596d0cea50c78cfbb23a6d8546ef1487abe7d5023ae949357832", - "senderId": "DL6wmfnA2acPLpBjKS4zPGsSwxkTtGANsK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03697abb61ee85e020a35a1d2701112e7e16477ac9d2eb2e8900a27995edc917a2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "03697abb61ee85e020a35a1d2701112e7e16477ac9d2eb2e8900a27995edc917a2" - } - }, - "signature": "304402204cff61e3c4f0aa15a31f4a84611b7ab555a2b15ebe7012b6cfc99f711842277e022040dd69a0e6ba7e1b8be9d7dc7df64f0f23bf119f01e9babedba3851d65ba3263", - "id": "bebaa71c139fb53d5453833ac9c8f1491bcbf96be4d10ce5ea1bcf5e7f86e07d", - "senderId": "DMCAKuFyjRhZreFtgaBjV43Qtb3EzVUfVz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "027e2269d8a770343223bedc49bab31b3c52fb4c1df6627153e6374ac23e2d878b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "027e2269d8a770343223bedc49bab31b3c52fb4c1df6627153e6374ac23e2d878b" - } - }, - "signature": "304402200f86542051d29cf0c9a2dcaffa1729b7d59e85c9c01a19d00b5cf2d8193c160a02201841255da9bf8fecc6c78b868c7ebb3b450372a4fffeda0b31bdecf344fb096f", - "id": "44a36f5fcc58f2091a25c1346edf2a0a707e5d58c89be2f814f1c658fda7c559", - "senderId": "D5pVkhZbSb4UNXvfmF6j7zdau8yGxfKwSv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03858d4d3b77c7c227f6fe3e18b5807aa476828cb712663dcd79df87e439cc07c5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "03858d4d3b77c7c227f6fe3e18b5807aa476828cb712663dcd79df87e439cc07c5" - } - }, - "signature": "3045022100cdea6cbd578c8380c5ffc4635af5c9cf9a06b8ce8eeeb3a03a93936c60f0b67a022027a8efce7700d9e4192f50091cb5d72b8c18ca867df92d1d9cd40dc3475da4a2", - "id": "e12ade6f3832d1aced7ddb25bb63e06efdc1f99e3eb16c501f7cb790b107a09e", - "senderId": "DAjqLwuWd4t4rEDZ6xpk7Fcyndv4Qcr5WZ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "023918d30ff448ec897e12b77ccd529835c78aee07db1682639320c253cc21a1c7", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "023918d30ff448ec897e12b77ccd529835c78aee07db1682639320c253cc21a1c7" - } - }, - "signature": "304402207b6770015a921fd6ed1d0e0faa06ede9af7e66925a9dd2ddd1553e48179cadf9022044ecaee06f0661abccdff90be1157c5410fbb42ed8e67db413201c61740d7e39", - "id": "2c5969f690dc7d8e0c6720405e172b4ae06ce186f7ba99972bbe7dd587b3b319", - "senderId": "DSMxEhoudwLYVt1jtHDu1dtisa2gS7LeCW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03b12f99375c3b0e4f5f5c7ea74e723f0b84a6f169b47d9105ed2a179f30c82df2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "03b12f99375c3b0e4f5f5c7ea74e723f0b84a6f169b47d9105ed2a179f30c82df2" - } - }, - "signature": "3045022100899b81a7fd3683fe1cb60e8ea070dda891ed22e9ce386538d1f57a79f801e90e02201592f75d7d1dfb15da63c27d190d915fca192e9d69013af7d8087fcc7662a13e", - "id": "5191f5fce08b980f0004f654f0af58eba43a112c25eac53e29778239c7c2b8ee", - "senderId": "DNv1iScT2DJBWzpJd1AFYkTx1xkAZ9XVJk" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c3d1ae1b8fe831218f78cf09d864e60818ebdba4aacc74ecc2bcf2734aadf5ea", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "02c3d1ae1b8fe831218f78cf09d864e60818ebdba4aacc74ecc2bcf2734aadf5ea" - } - }, - "signature": "3045022100b71c3d87f13aafa9e4e7ecb0293daa2f40ac703a8678f58c3a3c04c57845d04802206b0c9e48051b23380c158abc441372f4074b1fb76040fef0a008fd9a9a1a948b", - "id": "269b9bf296615f4cca0812f3d15ef8f0afec02b123088e46008ce4a353def912", - "senderId": "DEHtM61jVo4uJWP23B6mGrb6p9batXCHZs" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02be5acbe305bc5382ebd7998f3f42744c793245f37a645916771ff123fb7b4ef0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "02be5acbe305bc5382ebd7998f3f42744c793245f37a645916771ff123fb7b4ef0" - } - }, - "signature": "304502210094b62a07ee25d1058f5eb2c71d82acf55f62b787cb017e821eb22885f16c81bd022074d2f06ba34771e2fdb65b5d46a8ad69d05e3e2500549c8c9dd978503b8ebf2a", - "id": "588c80d5df11abff1247672f875a9c44c36b45e742187b9071f1a9e04a1ec3b0", - "senderId": "DKXY49bVxzzr3QhLpoYWPvdKcBrKfchE9h" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02e9ef70986ab6de9dbd5e1f430018bb8dea671d30c1e34af5146a48f2b73d551d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02e9ef70986ab6de9dbd5e1f430018bb8dea671d30c1e34af5146a48f2b73d551d" - } - }, - "signature": "3044022038243ef0738d6edaf8313fd51bc0f1482a055880e1a37e0d28f13e7287d3f5f802206e3cc68b90437b8b879a83c490aac772d89c8f15c28011b7409545c9e934d922", - "id": "a11ff1c1ffae49f6cc08f1b36606519f215f3ddaac5dca616f3e0858f33205e3", - "senderId": "DQEAsYqgNQ92wqmH6b1WGwsJ9JWhJtMTbQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "024c149adb250db9d314462e7e3628c8a63d263812d85306a530c3ee1f5ba31618", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "024c149adb250db9d314462e7e3628c8a63d263812d85306a530c3ee1f5ba31618" - } - }, - "signature": "3044022001e9b6ab03157a9ab0b3a882e27576bb496b0a749f2b4c9f6bfb77ae3a18d72502206f4bc9e9f4375dae548c9d480b4b149af35a3625b1aea1e9298494dcd292b935", - "id": "e5034eeb44b0762a884d346a0d8806e0776bf7194973d978e07a2247d457b6c0", - "senderId": "DM38Tc7HUZ5T41swShV2ELVtvjghVgHWLw" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022eedf9f1cdae0cfaae635fe415b6a8f1912bc89bc3880ec41135d62cbbebd3d3", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "022eedf9f1cdae0cfaae635fe415b6a8f1912bc89bc3880ec41135d62cbbebd3d3" - } - }, - "signature": "30450221009bff8a22cdad663c21f587e828cf774c3b399a66fbe9a4161f22a932f0a389bb022045b1ee28e49cc95d280e4d07bc044459381c8d7dfbaed94e1bac2e22ece22f67", - "id": "831e9c84af1e61afb18fc7561681558009b51206e920495b2b708e9f65f261cd", - "senderId": "DReUcXWdCz2QLKzHM9NdZQE7fAwAyPwAmd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02bc4cfee3716fcf191caa51c7bd2205a796b504b9ad5461864681cf1b33912003", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "02bc4cfee3716fcf191caa51c7bd2205a796b504b9ad5461864681cf1b33912003" - } - }, - "signature": "304402203ef34aac4fb45505b2e869e1a1f075a1967bd7f7fdb95362b33bc30862007517022028f65b2409c64c0832f1874f5e7e6173b107bff4f3737fc41c2042cf1fc9370a", - "id": "5d7cbc26b116c9daefe94965b5a64e34fd6a7e42de3619a71d0ba5976976c966", - "senderId": "DLnysb6HbtTcNpff87P5f47qVpFxYAqUSY" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03f3512aa9717b2ca83d371ed3bb2d3ff922969f3ceef92f65c060afa2bc2244fb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03f3512aa9717b2ca83d371ed3bb2d3ff922969f3ceef92f65c060afa2bc2244fb" - } - }, - "signature": "30440220763f33a056e01ee9749e60a17722138dee67b88cc1a54099e30939787ca6890c0220606b4e77d517b9e2d3f2fcf92176f081f3ce35c76ea4dc838bfe5f9122635bc8", - "id": "8ad18dfd85c61f3fbbabf8f3a0363cee55966c2cad784f96406aaeeb8d87e8ac", - "senderId": "DRqv1yELdrVc3z4ViTe9ueWiPm8Dbs5ZV2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02e311d97f92dc879860ec0d63b344239f17149ed1700b279b5ef52d2baaa0226f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "02e311d97f92dc879860ec0d63b344239f17149ed1700b279b5ef52d2baaa0226f" - } - }, - "signature": "304402207f764b224b3baa195c4385ed608ff2ae4516f00a0d26c370fbe8d2da246f31fb02205be484657a0550bbcf334fe20cca1e5d4b709d7288d497fa6f8ebc9dc08e4d91", - "id": "75604d72872f730d7c38b9d73c916e4a532408ea0074850a581f4b28bd62acdf", - "senderId": "D8vwEEvKgMPVvvK2Zwzyb5uHzRadurCcKq" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c57b6a3eb7d01ade51f95c8ae4e8ebeb7ca7b8422ab0fb2a236de5d1a5bc6a1b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "03c57b6a3eb7d01ade51f95c8ae4e8ebeb7ca7b8422ab0fb2a236de5d1a5bc6a1b" - } - }, - "signature": "304402203fabd59dfcfdf9a2789b08b3fa44685f0327e314af34c61a70ae0a857a54cb7302207a87e1d17b1526a16dad101bac823dd6c3075b9a65ea0045e9b60ffa0dba51f8", - "id": "d52d2fa7a2dde4e8f76e9a4d87c9b85100390e85e502f7f53760b062e7c58b2b", - "senderId": "DHg1jYVS23D6GP7RuhckuJsYAr6crH6c3Z" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "020aac4ec02d47d306b394b79d3351c56c1253cd67fe2c1a38ceba59b896d584d1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "020aac4ec02d47d306b394b79d3351c56c1253cd67fe2c1a38ceba59b896d584d1" - } - }, - "signature": "3044022036d24450a81aa6343f4e7911301f8fa1e8a7a4d439ad140035f5033841da95a302206fa9f1721e1fe5985dce006270ae5e25f4bf128b8d007350caa7041252a68356", - "id": "d11fcfd463a40465ed61d8d624f0a4141c7a66629d0fe36d40d1b181abfe7673", - "senderId": "DRgF3PvzeGWndQjET7dZsSmnrc6uAy23ES" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ff046250cc01d4ccb74fcf6ff9b6977f5a67539cd9c461f9d41436677f4035b7", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02ff046250cc01d4ccb74fcf6ff9b6977f5a67539cd9c461f9d41436677f4035b7" - } - }, - "signature": "30440220347cba116760fab827b31ebb2169679cfdfbe6b5c104ea74a2e4e3878bb064e3022015a44c54db1909777d2d4023716de8cc30484b0659d9776400ce9d7ccb84041b", - "id": "4933cee8da17f173db0dbbd4746dde532dbadd50060dda1d571e2ff918593a54", - "senderId": "D9RmfFg3xPMdxwo4AsNA1xbBoM2eY2quBM" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d6ba3d1132d7a74bbe9da3923168aad70146b5646c6d6505bb7f500b211b332c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "03d6ba3d1132d7a74bbe9da3923168aad70146b5646c6d6505bb7f500b211b332c" - } - }, - "signature": "30450221009aacd21661b435f58c40969cbab10600c504fd5d705ee174cb9177e36f893149022049870b13972d9eaa6a3a4335d892ab2f0620c231ce90d0a9cffeaf40f9a3093f", - "id": "8cb3517ff945dad8652d439c6c4db47a040aa41359897f45d6089ad325f4e239", - "senderId": "DFjnYr6USjvtaz9VZv7agd144W77WUiWtN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ef692bb144c368b4844ceca3ffd30fb8c82b97b5b40220473e9009925637e9f9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "03ef692bb144c368b4844ceca3ffd30fb8c82b97b5b40220473e9009925637e9f9" - } - }, - "signature": "304402203ea9c88ba6bbe508e1753b77593be7213b4fe9833ea5b09168a4c85575b5b1a702205cde0fb97c4786d9a87c3afdc3a1ea0557bcb00530f33aaeda67fc544f39e3ef", - "id": "1bc38d97669d9bab1b4f59927d1f752732ac400c80cc3e2972a59c4dc1f59423", - "senderId": "DHaJZBGNrWLzbWbVTc5TnHMJXKeT6comq9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02aabcdb8511f55b6a28593979b726ef55b1f5bbf16a83205c2e2bfc9d8c2909e3", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "02aabcdb8511f55b6a28593979b726ef55b1f5bbf16a83205c2e2bfc9d8c2909e3" - } - }, - "signature": "3045022100ff5a0dca3050e01b69688c27ee08b9954b9424354fd1ba5d9f100d982d938fc402203159fecb536b4a4dcbb262e37df5649f1497ac2a5e58f54f9f2a1a5b2fb981d0", - "id": "8232a75bf0a7e2133276dc9f66c969ab8710105b5128c293d8a51efe5328f99b", - "senderId": "DT1ftBZEuQm49xzqfq1MiniwvLkFtr42KK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03b1e1d0b5e1dbe57109d78119785e8c5985a47e282a048bdf6e8b1fe61b7c5c13", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "03b1e1d0b5e1dbe57109d78119785e8c5985a47e282a048bdf6e8b1fe61b7c5c13" - } - }, - "signature": "3045022100f041225c982e862436317172c593d016e6f1f6d1f5c732d3ba1806a5956015ea022079bac9a025de182905dd19e151f8414051227387e42067089c5017371585fa91", - "id": "c13a52286d075cd6ab637047dd7512514352fe25d32fc721d8cb867fd620454c", - "senderId": "DG1hN8QzbjsMdQhCjpHDQoc7BgBt7WBRum" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030efa0d981385ad10d237588ed67eee4a245ca552673b7f137cc2e9c9dc7143d9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "030efa0d981385ad10d237588ed67eee4a245ca552673b7f137cc2e9c9dc7143d9" - } - }, - "signature": "30440220478da5abd03f51d3a1b2313027624ad38a3af2e832fe4998fc70524a2032d14002200f4ded96178002713f05ef0bd80b2f5a96182ee5f9b2a4508c663c2c6169aa82", - "id": "bc9ab5bde4513df5c7ea88d2b625e2b9dc33681d3b6ac8c2e620d92032f35588", - "senderId": "DSQhC4JyfWaxsZKzF9Kfq5knVifCZA5jLK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0335fec08c867b80e3b71545be195e1b9876b2040d5393fc177b6edca78bde8e42", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "0335fec08c867b80e3b71545be195e1b9876b2040d5393fc177b6edca78bde8e42" - } - }, - "signature": "3044022001a806c7d5a40553fb13a1fde597f53a1904229d3c2ffba8bf9c86c10f436170022058403f447c5d3aa98af0e4fd111c541e33e5d9f271093fa8860ff131ed6eb6a1", - "id": "f585f7fc3b709c91128b1c2c09fb6b8d43ceb6220b85e5e1257715de748eb6d7", - "senderId": "DNKFUB9u37jWrePHMXhe3qWde69JFe3HzZ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02a5522a8cb7bc98f8adb024f92d8455918e18d3280e427e7d73d40a03a1698b96", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02a5522a8cb7bc98f8adb024f92d8455918e18d3280e427e7d73d40a03a1698b96" - } - }, - "signature": "30440220116373ef6690b97809bf2063ff3a617a5ed8ac351978ad2ffe8b2424c8f8727a02206c64f816b524f388c878fbc51a0e1229fe8e0cc774964711581055fe576965d1", - "id": "b07658aea5a70cdb8cb9001625979681d49a8b1a6989e64a5098b959a6f1f76a", - "senderId": "DFUAdEcBMCPfBq3ZjM1DViAp7oLL486zEB" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02951227bb3bc5309aeb96460dbdf945746012810bb4020f35c20feae4eea7e5d4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "02951227bb3bc5309aeb96460dbdf945746012810bb4020f35c20feae4eea7e5d4" - } - }, - "signature": "3045022100fdf80150b0cc8956bfc2cbed753a52343b9b95c858620c3e59aed44630114a9e022041a34e074ae8e36ddd7ec352837395733f93eab9a77f3e4635c6739c54d22048", - "id": "f60145575d1dfaaee2d8c7927c30bae6ba7c82c4381ffef374b7a09388998052", - "senderId": "D7JJ4ZfkJDwDCwuwzhtbCFapBUCWU3HHGP" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0259d9ca7922c277b0e7407a88703bbb98f5da43a335b0eefa6c4642f072acfe79", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "0259d9ca7922c277b0e7407a88703bbb98f5da43a335b0eefa6c4642f072acfe79" - } - }, - "signature": "3045022100e90fd1d6bc5572317f97de693c8186c550c81e5d2ab9314dc56572984d72967102206fcb2d63ef004a9356048ec44a083886f7ff73af5fde973da1275ef694244938", - "id": "7d1409e3032e05f3571439148ed8914ec9d7cd11943bab4170823049dbcda9ba", - "senderId": "DCZFtPKqK5iz294jyhrXPXKGRwQpGru6o5" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "027f997ab2d8874c8f06d12ef78d39e309ed887a1141d2ba0f50aec8abffaffcde", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "027f997ab2d8874c8f06d12ef78d39e309ed887a1141d2ba0f50aec8abffaffcde" - } - }, - "signature": "304402203aada267711da498f7c6e3714d51ae89c0f4dffdcca85740eaca3994cb36b08602206b7c29fbfdae516c5cc677b8932f9cb2112c7f231abfdd040148ddd6cbe930ed", - "id": "056716dada5916f4f8e108a49a93d9b5c4d2da5773e9d47ec81509624a46e638", - "senderId": "DK52aX793nEMRpDvRVB2euBgGXiCpRfmwQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02fdcb7706624aba3c050c19bdd05f74a0c746614462584dfbba3705810ba2d69c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "02fdcb7706624aba3c050c19bdd05f74a0c746614462584dfbba3705810ba2d69c" - } - }, - "signature": "304502210086fe27f8a2527269b59728ddce8cefb41c4b11efb49cedadafbf364ff972beba02204df3a83777933f4cb0941748e148a3f0d2b03b5e1be93ff429b1108ba1791c04", - "id": "583e706ee292251a32526a647576a4467162ebe4dd61fdf4e9bb878083745c7d", - "senderId": "DP8LLTXWNZfafDbetQEWm5uc8YYpNz1wFF" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "0212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad" - } - }, - "signature": "3044022065af2653051345aef10cebf5f98210f228af00768b094eb0f82a5a0765180ca202203c87461e7e40f17273be4638f604b01cae72c47ffe8815917d0dae6f6195709b", - "id": "4041e3a8e3760e40e7b3d0269935b7894df156dbdd08092a9dfab32a00dc0572", - "senderId": "DQUtJHQToFo9Kbgm6R9afGamvXptDzc2mi" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb" - } - }, - "signature": "30450221009c0682b562cb4405cea7e522d7b98b628af4b33c1bfbea354d67351a0d3a066402204e1c060c533c8a8896dbfd5e6219e2a0e7d96c9d40cbbb23bbdf8bc6c0450bb8", - "id": "ffc28e2cddf4494fa4bdebd90e5b8fa41253c1faa02a95eca0d7ba6bd04ca616", - "senderId": "DJmvhhiQFSrEQCq9FUxvcLcpcBjx7K3yLt" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ff842d25fc8eec9e1382e6468188b3fd130ab6246240fc97958ce83d6d147eaf", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "02ff842d25fc8eec9e1382e6468188b3fd130ab6246240fc97958ce83d6d147eaf" - } - }, - "signature": "304402206f3e7f51cea90b6180dee3dbb80a0e7fa5f06ab6266b73ae8f1cc05de077247a022017d5aa06a94eee0c3be3764fb644c02db8a0367010e726c646e75df3e41aecaf", - "id": "062b54b6fc5abdc8e7d57a98c9a8f2d0ddfd42c98c640a6242c9af10fe792d68", - "senderId": "DRDyyHNmfF2vijmA3kmMp2JvmBVMLJTTBr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a2933", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "03a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a2933" - } - }, - "signature": "3045022100e35cca00771ea7b1dfb141681aebfc30a698976203b2429b21d296980f278b6b02203bef1d5571dc1109fbf7eb956e411bce5d11ae06557d232a6fb7c5e8055a489b", - "id": "d1955dd9e4ebafd3c33fc2752ddeebfb5066c0e9c06275e13b3904d2db4f59ae", - "senderId": "DTKn3J3pMjtPLJxZtjvqR5T6DefPzjgGdf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03b906102928cf97c6ddeb59cefb0e1e02105a22ab1acc3b4906214a16d494db0a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "03b906102928cf97c6ddeb59cefb0e1e02105a22ab1acc3b4906214a16d494db0a" - } - }, - "signature": "3045022100901a161168ef69bc7b35610809e892d0aafe2b275f2f03752fe46d54301bd16502203b143cb62708efd18ee11cf5405a05023935ffc867c39b78397dced75aa7b0ab", - "id": "a9a246807ec148536909e633b7422dc90dc8ffe858b3146e0b3d870100ebc488", - "senderId": "DA4RAKRngGDfgEA7L6nQjYVEKcZPgi8EdP" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034ecce71a7690b1c314f71789fb32373afb79fef870fa6ea2843be4c54812b0e5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "034ecce71a7690b1c314f71789fb32373afb79fef870fa6ea2843be4c54812b0e5" - } - }, - "signature": "304402200e5bc71301bef7a018e9715f18cb705d59253ecb9ba90dca9846272f70168b6602206d5cd8f334883480502e565b1602daa4c6168f631d449faae098e773305386f2", - "id": "e15317b81533925b0c39b50feb18b865f7122621a33266f606787a2a6e97bad7", - "senderId": "DLqV4kuQMq1j9nEFoWKEF9jPQQdTh7CNWx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03f89a2f86fe683dbbe4856a43c4c326fb2938ae2647fd334ad33261c7c2dee265", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "03f89a2f86fe683dbbe4856a43c4c326fb2938ae2647fd334ad33261c7c2dee265" - } - }, - "signature": "30440220310fa69482a0cea90eb033c258b1f6f012799f76fbe17abccc36c55fb33b00200220287647844189029eed0f7b30e28759d346e6cd212cf1ed20bbb3457e066f8234", - "id": "b6f546a0017599ca64784ee5fba9fa524fe861ab1a9e54249d3a87c94477f5bd", - "senderId": "DMjBzD8BP4WwzpZ8yMVGHkr1z5jsA2A5Ze" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0349e7e2afb470994a8323e9623a6dab227c69d5f09f1a59991fd92880123ffe75", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "0349e7e2afb470994a8323e9623a6dab227c69d5f09f1a59991fd92880123ffe75" - } - }, - "signature": "3045022100b38c62db607d567ab0e9180dabc42662ad02b1ad86ec962af54085403bb4405702203b8b95a3941e8a825102a55af6c374364efb95f807cb146d318c2255379c132b", - "id": "1df66d5b41a604ffa298cac7a60a3cf69f8c5fb9f6e8da67cd6f8e6fcf6807c0", - "senderId": "DJic2dfPgxaGJeme1tQPpLuvkJMKo6PDfP" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02237692aa37cba4fe67d557973ed610ea175ea44d3f5cf4c3ce8ca7553ca1de17", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "02237692aa37cba4fe67d557973ed610ea175ea44d3f5cf4c3ce8ca7553ca1de17" - } - }, - "signature": "3044022022d6b16ab19faf9d9a86bf52e6cbe3568f2c3be19169657b49cce412b2287d4c02200ba4ec759b7d1e2fdd268111fea5ea49d54f2c5d9c04723e4b4fa64025362dfe", - "id": "3fbf00f4d88c94d736bbff3ef4722183c9b2993a0afc2b992a590b2cf93c5d64", - "senderId": "DQFLoHAkjrY7eqJrdPu2NdS8KgLQJSnP6Y" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ac8d84d81648154f79ba64fbf64cd6ee60f385b2aa52e8eb72bc1374bf55a68c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "02ac8d84d81648154f79ba64fbf64cd6ee60f385b2aa52e8eb72bc1374bf55a68c" - } - }, - "signature": "304402205097c9e4a3e3020f13753bd9749cf7263e8aed0165582f449da057a4f46d14310220699527199ff99af5ab3a81cc13d8100ae8dc85c84ddecbb2211daa7ccaff9361", - "id": "637aa19839b81f70c84ebc0e1611e18a260a55fa55af363873f7c2401a8ac18d", - "senderId": "DDU4aLrxw9VYJzrMTYtRAyDM9fKsqciiYd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026a423b3323de175dd82788c7eab57850c6a37ea6a470308ebadd7007baf8ceb3", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "026a423b3323de175dd82788c7eab57850c6a37ea6a470308ebadd7007baf8ceb3" - } - }, - "signature": "304502210099fb7d275d0e4a42febbec6279f853df99b6d4423df38df484511772e8af7e44022011933c855a3253eb1c68ae7d6c6daef095a93fc55942b77cb502f070d1559bee", - "id": "ef351a3237490b7b09d74f37340aaa8a589adee9ded564974ad230f7ca0e0aa9", - "senderId": "D8xN3Nsa3KfC3H68Ek9xnkfdSwzv8Kkh3q" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02637b15aa50fa95018609a6d7b52b025de807a41b79b164626cee87dd6f61a662", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "02637b15aa50fa95018609a6d7b52b025de807a41b79b164626cee87dd6f61a662" - } - }, - "signature": "3045022100923763d4651f207103a4c8c5d7cb40cc9423b66d03dc155b320fda25b6c24c860220088a9c96d50a8123856be4762284f0390dc0775f05a1bf40c54fe034c74c6cfb", - "id": "d1c6a20ba8d77777b0d3f48bd0c0c0b1dbca53c602a98645ed103ffb7a09d816", - "senderId": "DF2hqXQjZvzowY6wLxgwMeH638Cgfd9pGB" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e5359516", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "0346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e5359516" - } - }, - "signature": "304402202e2550948f1a31becfa76f7fc1698313cd597148ce9530fec24a03bc8b3b28d302203b75f4b837b83602acb796baebd684d8807140a445277728987325ca1d4f32a7", - "id": "0240a4a290d3973ebd34eee830567a6c8250229f5f3d67cfa7400787bfc5f158", - "senderId": "D5L5zXgvqtg7qoGimt5vYhFuf5Ued6iWVr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021b0f58eca7f123428a8647ffe0644a9454c510f066d3864c27d8c7ad8f5a8aa4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "021b0f58eca7f123428a8647ffe0644a9454c510f066d3864c27d8c7ad8f5a8aa4" - } - }, - "signature": "3045022100bd702d74bc7a476bdb8b2eeac513fa57a2a06f7a3726933c0edc13413c1fdeb90220014682b24f98f2ad79fd53913ef4e691c4dafb5e00535c1c220529bcaa6e0998", - "id": "4b4c0d5999f70244ff66b14e77bb6f1ae3279458e784222f9b8a81b3915111ca", - "senderId": "DNpPXpZrRSKsk9aiM2p4g5PiKFHxEFCtqb" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0335238d54c6ee73d79769d4ed43888d9b18e38c3594e656bb5a5544649d4f5ffe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "0335238d54c6ee73d79769d4ed43888d9b18e38c3594e656bb5a5544649d4f5ffe" - } - }, - "signature": "3045022100d7dc0aceb038ca7a910b9337cc8fb325c9523874b4bb79d8491a6d5d413391c1022008c6f2b0bc8ade96163d577241437c6e1a376d36525a3739596cba2077abd1ed", - "id": "dd002a3a4f56a33c33cde898966887009adcf53f4cb84093d6a5670ed43d6248", - "senderId": "DLL6884s6tbNX2Lwbbsi5FNonm8GEvQ6J7" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022e13de675e14a409ce636706c76d42857c673d8dc0dda4e5bfceffdbf86e13c9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "022e13de675e14a409ce636706c76d42857c673d8dc0dda4e5bfceffdbf86e13c9" - } - }, - "signature": "3045022100a57a10ee4e79394818345347cf3a4f6c9e4f039b4fb5586da040ff34a6b96cff022078416a630aa400f9c988f8c43d4a5484d14bce536d91f3a6dede075896d32842", - "id": "c8b11bc8306047f9b368c510dbb6027a7c9b4d6df3c5505079ac334446437fd9", - "senderId": "D7u9gSS3KsykEoRys7DxsRNwHjpYoG8mqS" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "029a20963b506afabb2bd805830a46cef8d59218cd88c0dca9d2a0158045b1c3e0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "029a20963b506afabb2bd805830a46cef8d59218cd88c0dca9d2a0158045b1c3e0" - } - }, - "signature": "30450221009ee57a3ff9a8db841853411ad4068065aed82d35bed8a89f80995e70848f78dc02206a6628a2a8347a91aebcb78e1d575c0a1f8754103909a8a225ce3ac834a84830", - "id": "4f5a69ef4ee644cee1e5f8c0fffba9156d26a3c27ff88531a5c3bc53d77cde41", - "senderId": "DGKgCQ1srb8HZyr47RqQqMvGZ4cDyr4eMo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03f08761f99892996c6771761955ec41ee6cdffadd43171228f5f28f8c76423b3d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "03f08761f99892996c6771761955ec41ee6cdffadd43171228f5f28f8c76423b3d" - } - }, - "signature": "3045022100d65c0d76bf792c61a8c93070cdcfcc56330f1ffb37185774d04f77115c1acbc70220092dfa82e91e4b7f47a2a6d4ee57965d80ddc82fca9758250b22c4271b5ca303", - "id": "e36de13f1ef1c5749f697a04943279d8b398c5758eb30b7c8c685d45cd2da4ac", - "senderId": "DPv1wzB2xJtJQH5PaCMnw1V9mUBugzP8yQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ea97a59522c4cb4bb3420fc94555f6223813d9817dd421bf533b390a7ea140db", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "03ea97a59522c4cb4bb3420fc94555f6223813d9817dd421bf533b390a7ea140db" - } - }, - "signature": "304402200a378960653a3d2f8b7959828cc6591a7e345bccd78bf4467e727841504f6dd302205129a7d4ff5a695ea4c05881824540753d09343ea591f8744d408917ad81465c", - "id": "c1da6ef5cb25b778cd8401127ee15b22e2e95cd37fe6da489e8ab35dd8138eb4", - "senderId": "DMDtE1ueKfVYHs2Kmxb2XepLnzvSM47fwh" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "027710de44279aaa41f8e75d5ae61a085020c414db2dbea02bbec0e0b1f27bcd22", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "027710de44279aaa41f8e75d5ae61a085020c414db2dbea02bbec0e0b1f27bcd22" - } - }, - "signature": "3045022100dc408846105dd8d3849aa24b5f8ed40836f17b1c5c0d8c82977fb278f8bd9d3702200b6c097658fb5b1030f1cdbc8c3c63d402b108b4236b2bc63ddc6c6be4a05633", - "id": "f9ef93a7ff6a96ddfe6b0f0a58f5c5bd9d906e6f7b5f2c8d1372fbbab37ebf74", - "senderId": "DNPBUxxGQUKDPX3XKUXa3pc4GK8yz7L97T" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02677f73453da6073f5cf76db8f65fabc1a3b7aadc7b06027e0df709f14e097790", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "02677f73453da6073f5cf76db8f65fabc1a3b7aadc7b06027e0df709f14e097790" - } - }, - "signature": "3045022100e6a5a91dcd3c70f3beb78eb15432171b000606557c5c13073d155422d931b1460220073b2aaee4cc7f6788b3b1781e881f20cc163ab7f81be9d32977e2eb194d9ffe", - "id": "de4fd6a03861476286161177b6d4e4b421871d8bf393dea05c5daf2de733de56", - "senderId": "DRW3wNMA4ijPfm7KA3XtupDNb5Hb8kL4AE" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c1670", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "02257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c1670" - } - }, - "signature": "30450221008a4abd4350b2069b2552e6b2c26d1d8fffc78914a071d98f1682de78497fbac502204b65e5b30255438e92d1980017c2fd5bbf3436839bf42d933d83a032d5b68f1d", - "id": "93288bd9dda27eaa1a395c0e575802f2c89a088c2166fd49e4dcfc8480d5812e", - "senderId": "D8vKwaX6ksU3mWg7tJDm7v1dbxy4cMo4dh" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03508436f55577f406be58a5e7e59307cea823943c5312d62f4e3f3c63966f6e7c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "03508436f55577f406be58a5e7e59307cea823943c5312d62f4e3f3c63966f6e7c" - } - }, - "signature": "3045022100aa88c4528c44c168fa205cc88b240ad73bde9bd0bf7b6c3607d15cd7dd1a6bfe022024c361cf430531ddf6345fe00c40eeb4f7ebeebd853ecf927f34f465ec87d134", - "id": "7d7418341dabf8406726f30b33d22db8a6e5713a36b87f4d5c2a12e44cae2564", - "senderId": "DKY5eyQUKKYyaCfPp6MUv3Y4FW6EbNF53A" - } - ], - "height": 1, - "id": "13114381566690093367", - "blockSignature": "3044022035694a9b99a9236655c658eb07fc3b02ce5edcc24b76424a7287c54ed3822b0602203621e92defb360490610f763d85e94c2db2807a4bd7756cc8a6a585463ef7bae" -} diff --git a/packages/core/lib/config/devnet/peers.json b/packages/core/lib/config/devnet/peers.json deleted file mode 100644 index b00dd2960f..0000000000 --- a/packages/core/lib/config/devnet/peers.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "minimumVersion": ">=2.0.12", - "minimumNetworkReach": 5, - "globalTimeout": 5000, - "coldStart": 5, - "whiteList": [], - "blackList": [], - "list": [ - { - "ip": "167.114.29.51", - "port": 4002 - }, - { - "ip": "167.114.29.52", - "port": 4002 - }, - { - "ip": "167.114.29.53", - "port": 4002 - }, - { - "ip": "167.114.29.54", - "port": 4002 - }, - { - "ip": "167.114.29.55", - "port": 4002 - } - ], - "sources": [ - "https://raw.githubusercontent.com/ArkEcosystem/peers/master/devnet.json" - ] -} diff --git a/packages/core/lib/config/devnet/plugins.js b/packages/core/lib/config/devnet/plugins.js deleted file mode 100644 index fd4c38865e..0000000000 --- a/packages/core/lib/config/devnet/plugins.js +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || `ark_${process.env.ARK_NETWORK_NAME}`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: !process.env.ARK_TRANSACTION_POOL_DISABLED, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4002, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: !process.env.ARK_API_DISABLED, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4003, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4005, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4002}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-snapshots': {}, -} diff --git a/packages/core/lib/config/mainnet/delegates.json b/packages/core/lib/config/mainnet/delegates.json deleted file mode 100644 index 096f5472e1..0000000000 --- a/packages/core/lib/config/mainnet/delegates.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "secrets": [] -} diff --git a/packages/core/lib/config/mainnet/genesisBlock.json b/packages/core/lib/config/mainnet/genesisBlock.json deleted file mode 100644 index bfc4226214..0000000000 --- a/packages/core/lib/config/mainnet/genesisBlock.json +++ /dev/null @@ -1,18176 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000004, - "totalFee": 0, - "reward": 0, - "payloadHash": "6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988", - "timestamp": 0, - "numberOfTransactions": 1492, - "payloadLength": 313052, - "previousBlock": null, - "generatorPublicKey": "03a4d147a417376742f9ab78c7c3891574d19376aa62e7bbddceaf12e096e79fe0", - "transactions": [ - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AU9BgcsCBDCkzPyY9EZXqiwukYq4Kor4oX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ed57f27cabdb01f5398b30e63e3372735ee428e17e95de675c37586b6d1a5c12022062a0040ed189a4adac6c3d105e05180f7c74e8c68ca9912b3c60286c2226f3fa", - "id": "44d9d0a3093232b9368a24af90577741df8340b93732db23b90d44f6590d3e42", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AeLpRK8rFVtBeyBVqBtdQpWDfLzaiNujKr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022018618cfd5dd1024c0dd7677fdbddcaa6977b57f832eca130583d36480dfa452302202c067556fd93899fb0d18ea28e6f0276a778099cdde3d97d3bb8733dff965a59", - "id": "512f1aa00538b24a3ba55d65519d34cea83d753f5b2cebfd7004d5c0eaa7177a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "ARagsXvdeTHYghaQgJkwbdSkPLZ73qdMkR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022021e056a123b4a6c30e3f30dd68ff56f4cc1a994222cf27ff5b48434947e45f300220424cbc671a54a019cc655d02b2313a324702908a4a05c86bac4ac83029bb01ef", - "id": "8bb3997878a6a359f1418cf25f31c84f660e5e6897ebd6d07549ff6a4374a44d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AT9xWcPQ8hGYuXZ8aWE57VJFohyX1TTLkH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd0ab0bee79152978d8d5835e2d244fa159e4957f48d602c65e35e2383c0d14a022036380dac439784075befef7f7b14734f9ed782e4be5ac7f2f4c49985b08fdce9", - "id": "30cb724924823c689058c25243d1c213b9cdb8c157eff26ee9c89fc1e705fedd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "ATjjXXcGPTum6wogPVGb9pmimpSo4EDDEv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202e4a8dbbc11c3628f7aa9ef92825d84cb662a20e0af724d80475bcaf64416007022063e859ef5e9f9dcb294956e14d0680bad69641d1c254ef0ccc733f25b7814573", - "id": "69e5ced4fddc54dc4b688150e8bbb5bd3cb056252708e0d03401819490bc6125", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AW58iWw1ATvzGHu338WqMYvgUhmvMMsRjF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b1a8dfbf6c37a984acb364311e22336c367c5c76741a29dae4d5f894a496738202203e93441406fe1b23ab6d8807179c2cfe9e4c33bfbc455cc30733b5237af35d60", - "id": "62a85a7d3c9c31eafba7b39b102ff4594c5dce17e5c9ed38ef897ae0970ea613", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AFxqVbsVuDfnfyd9ciRAuGq6waR5GqiC5R", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ff14b9189e4f4d6199737c9b9b00d7572b2e9a5e1d1280b0bcb02b51b233668d02207a9df93b66088faf216bdbd60f438974dd5000cd1dee92a5a391294d717fde00", - "id": "c7b221db20709d99cf25d4a5b75aedc2644c963be994f040c9582c8ca84524d5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AJjkVwkhsvsX6dVKhVxpmhRvz4CyXLEvQ3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100aa43dec0f44ed1ae07fcca1bd4ed5075954cfe0a0bc6ae88f0a2640c9961cd1502205c750b5c957bb92e7dc032bdf01fdd2765cffa5dcdfdaec7201b8a2fd17e56b1", - "id": "647143b65234e1dd78f258cb32ba64008a1d23baea7739c02db82e60ce9147bd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AMG1Y3LP4kZJMAtoPhsnVkpsMTJ8nnCDr3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203519061e2b618b44181bee15f4ae561f710c6601995ee5e38149726366b3d14302200d0414c012b86429eae098b474acfa1c9f29ac4ef06d500eda8a9294c1ce14c8", - "id": "02dc2d050528586dbb9ec9dfe8a4118a21bcb3244d14e61a8cf41337925972a0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AVFkEkCmEg7cuCXoVrfvtH6mKz6XC9XnvV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210088d431420331051cc29ee00cfe3851ac4fed969c9592a81dd0492c0dac83d0ec02200f2efd316f7506abc7c60ad6151b00977206aeacc5a3a538df94fbbb88a1bd70", - "id": "1a3522ccc27e223953d3978b891e4adc219ab41216b06eb956b138c8dd532243", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "ANstQM4LaxfsuKtFMd8vqdGte3CL9s2vMA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b6e401941a798a09d5179752ac67c39af596f95d28aaded45a79cc8b8689bfa5022079ada29c01231e3fc2ad96050204047a7d0a9b56c56f609532bdb3d92b152e91", - "id": "574fbbd0c1f28cc923b03cd00bc9f6867fe246a645e3b261b6b9b6b041491c13", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AJqbav8MPPAe3zdzo1f471gaciQbx1SRe3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e4f4718c13b10300c478ea28fc4a6444f626fcd1fbe6d45f80504abd8f1e5ac022078c0fa5bd5701dc989ee3432a561e659d7a07c22e6a9eb198745d162a3eef958", - "id": "e1d3f456bc81aa88b5b9f790c383384fd24593ab4dd50b308a77a843e0de346a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AbfnTeGFiRM3m8eHcMsrqNvrpUCoCnuSzH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fb740effa4f878b5afd3bf8202cedaa7bc4c76b5a9ec0e1791824008b08c04ab0220340e130184ea186b9c7acf058b9cb62c77fea9cbd954d5e5f0d883da8136d15c", - "id": "85c540e0e42f449a14541c4ab66a667a64a18ffb1dfc2213a143441c6d21069a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AXArbuCVSiRuYBkzCAajboxCfNiw8AyQX1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022073143336b15ab2a51da194873e14a2e08da329a325e570d3d525c764a8e546b402207055a169c2cf9b1da89bdd40b1e9a19c579865cc19e2f5e868343f594ebd87de", - "id": "0d00b60f1a26a05d52432e517e59a6e42b1597ba16547f25d5b4cc5a4d821c03", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "Aa5rKoVusA5xiyh8Git1tJUWZE48ScbCR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ea59db1db3462c4d08ed19920dd694e707181f63dedb48ad62991935f3f5da10022005d5981a84a82f8b7a881e50a07ffac814fa62d1052d43583de50e0ed62498a6", - "id": "bffa21418f3668024901a65e57f484ad45202ac71196e857d30b61c2a300ab6a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AbrgipEvLp1ZHNJ1GcWWAsCLNgSgDG6Lxh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203e5fc71ee49506af5218a4d6dd3870493081984f16234dc53339e6dd411d774302200293cee5f33cc54c22c766f420a0658107339905c6d6b745e88d49be6154be18", - "id": "77456d6e6fe11bd3fb7ec636e115dd6cd105cfbc97f2c9af37513f9f74737c97", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AWLoVgSScNNB2kecswuhbY9tcrtv9kf2iC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210089cb088a883d07418a6be847f45d2fdb1c8c09cc506b5ead7df7df3f54cb503202201b323319c4e8ae723c46886fc51ba311a722e25a0ed87a130ca57e9d6e725dd8", - "id": "79c7c34636d6bef31813438d5db5c2b02bd8a7964ec1654a2e24cc24563f5695", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AbyacFxcWS1JsokdRCx8bFsWP8f48XftmS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc987030f590b970eade8df22b44f13deb89a652837b3cdbd9274a27b9d97f9802204ad6718c262ab2eba80202503ef5cc9704c13b3ebce7e2e5e80489e783e34540", - "id": "8e40e07b53f46d8b42c90b18383ae19d5af69ff657c13f5f6550890eb65939e3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AYTEu82arYgRyvTgi7dbYwjodV7ignYucz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e30c20254d8518201f0a46ec347352159ae81ab2d1a47d582dfb20bbbfbfdb7c0220300c012e88a107f9268e7593f2adebcf7ab72f2d240aad37b58b139d02067f40", - "id": "23241fac8b9f633b6749f0b7788ffaf9ac8997168c55718d7c20eb688856cfbd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AYLTbdiYWHvPvJo5Lh42MEVS4Fnepg5vgc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207c919607f7d4b17b0dd54cd82843f4b4da3f5c5ea92f07a1794f0fd666f065f402207245dbc1d97ed651e14849c84579f11bee9dad72ec7b73dcf0b3d79cb7d68eaa", - "id": "5c78d1808a47333800b9604fc1ab2352bd847ea5fccb9f834825fee74e5aabe0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AGdHR2UZ3MJuaXWejKGoAycztyN8BFQnNG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6f1153d93d37483e21cbad1b6c07bebf95a8d13dca45e8c51008a69be736dd602200f81dbda9b257e6c12314bc3a20c5b1c8ba86e0deecde76bd1606f79e836e008", - "id": "fe843047a6d408de46081217cea6f5f197f6c2aba86bbacab00240f147002501", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AVi8PFHr5jFBFGWSissPFDFXUPABuBgxSa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022002a5daa6a6ac2cba5804e8bdc21769310f76fc6501c61147f9a5d97ff35737ac02202c6886b09e65cd90b3fb5ab5c906bf98512ff823f21cba66ee684c3e55e2058a", - "id": "eba8ca8a4c458bd3e15aa956a15e4b8cdf9336bf7d1050e9ab44bc83482005cb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AVoTzMjHaaWKDuA26ysXGU681N6Q2PQre6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203e6a954d0044affbfb9bf8097fb12fd030fa4d7b9f921a797ddbdac7d64d357002202404106e049c35fba4483451e9522ae46e201c916d50bf6971e5ffe7bc1f0155", - "id": "3c8b8785bb4213cb8df30aee1b061fa2ef0a515b59f05abbb0fc7e1c36f2052d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "APXkAdLbLiLJDC9Ls68Y9a5ws3PcgmUDAo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022022b5f2321cc614962cb4fd9a1eefd128986d39285b59f6e418e459139b07528f022050988e49f7d25d9a44765514d50765a0f43a47b306df56def037be36d9c97d01", - "id": "ab8a92d54c08815a6dcb5561c0dfa0e93b8552eea75e3feac096f5a5e4eff784", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AGZhXHXFUdvd7W4aWs1fYJe6XFUYeSWrd4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a66c0a231cf6f09d6ba27d7a835a4458edac26f7efce8fe1c6b58f80f88cd00e02207f92221ca0873e10a6415e62cdeb4da836cd16336715cc861784762d387a1749", - "id": "ea59d2aca71b241d063cc41061adab57d97369fe0e9e3bda651ada6c712665d2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "Ad8UiXMZPecuNGwCXZTmF8Mysn1TchVVTf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100af001967b21b5715f68047911026eb0cef51d14a8e0563e1633b2bf213c191cd02204b85d55c4b13255b488a661dba416f7208237dc12c114040fe54692f76a7fd57", - "id": "b8b63cd15f19ef5671309e8d571482537d88af6226248c2fa0e5bfc3750606ed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "APQgLh8MaztT1XuWVKb6d4FKM9HMmdmDVB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206bf4937703d0b2fc171b86ed8596ba5ec58e9282e3cc4ac269192ce670478a4002205cd8f627869c5071fb99db462c47655eafdeb1b991beb75bc48f62a56915c137", - "id": "e1a4a5601e46dca9c9e2a5a67655bf976764efe72e82d2cae07c043e46e29928", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "ANUfH6C3Jjp46pDUxWgeV6WUbWCagaVxM1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100acc09397ff3c0b38214df5bead49d458931197b22e28426826d0173594a75a0702202d22e93e84748b52edfc2e420ddbc6887b9cfae3f7b7b3d7781886f085b9618d", - "id": "e47687f406ffdc59012079854958532ae22512e2021990c0c11cede226c64d58", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AXuVpX3tAewNp5YVWvXWv3etxjrMDgaZdK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100df1255aec444d8ce3ac9cd459669412376eb92d281ec0153c7a525b43b2cd7ca022078e22a0c52eace076baf1d89bfd03771ed1b45dfea105451cb9490371be6908b", - "id": "a9301a31302384e0428be63ca0731b93ebd2a2c18534b2ce771525c4a0c1a708", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AdCa1oGLMRQqygEBGGq1AEtSV44CJ5Kbdo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220181358f06e1a2f2d43538dd72e7ec33ab64a39c92a512f2cb08e08fa32c3e2cc02203fddae5a4c9545093c9668c95688ec07b9b7ab1be27eb3efdf0c0acc41b2bc61", - "id": "f10ea8612fdb470c754f6ba5271d0b30c52c12d3a89ddffec0151cbc90e8d6fe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "Actv8ebcwNsbfx8MM5k2AeJidJuLL7PotT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b2c5014c3967c971f57c995bad2385f5d3cdbef34d43304d17be215984f986de0220080cdd2e5a643d7dc8fa9c299c5456d554a2d46713ce3a8901abd94455dd5929", - "id": "2a992486b31f723a33458ca055b878e51f1c13359cc4bda87509fc124d57c6ec", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "ANeLRbMxkSguPMq9CJeMgr8xZxwZ9mbqbx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009fddcf0b5071d43e7cfae4f9241011718d324ae16692ee674d668f09434041bd022050fd7c97e4206c2e07ef98032ed731593ec0cd8209a9debec36f39b153b752c5", - "id": "3e6debb349d1059af502804311bcb2cd37368d67b33845f9b50e4ba296d0aadc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AR5z28tRUnvaBWC14KSBpvNJDgsVCNtagq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220747d72aa2180e80d190ae4940dbffcad648ab743d551ee70ef221439357aa14a0220334be2dd1a004d188539af503467b37dceab37ec55ae5a035801746fa356412b", - "id": "7b97374ee88b881af118a757167c49ee0ae60b0ce88e377a200b015f87441b19", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AKeDRRgG4TdDo4Z2iCzaBZS2UcvjWKMSrC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205882a3aa05216dc8495784c9b842e3066f331014c8b55644804deccc10d696e602200fae518daafe7c9599a3e9a4d1ab8388fd3e5751af5e946c00a0d5d7c062f82e", - "id": "0d1a17ff619a2dfd5b1d2abe09600fd1a9ae1f373ebffb70bba1dacf7a15947a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AbkDPAUDsfQJgvHn5g8AisDm9XqLAtFFai", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022016ea5da49920aec7c20e293270fc5aeb99a7a8e6020daee092ed11cf48b01a3002205d10abf6cd111443e4ed7ae84a9f29791c5441cf99dac64af09611d6dff00dca", - "id": "4888da0a60c337f287a6526c9fc86be8bd85460ac1e48aa39b5d2f5c4a4018f9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AZvjdJSG5V4WnNjPaRbDNfLD72j3rYWqbs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202d089f163508a18c061fc42c70a79583a0081d9afb49dfa36b7f881cf73a48ab022021dfa71c41e3b7303297dcfcf63a180094f0ec58953f52e8b8c424251514f7f6", - "id": "be67562ccb6fc1d6a04791726fbcc111067d5d54076425b91380bc9442b31ea0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AbAeJ4YJU5rxuNtXpDn3E4W5E8UNHyc26a", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022015b2f957cf3fe5f690786bdec5831efe41b59ea82a357bc520500c77ab20d9cf02201ac6773a21d83bf1f0b744576b620838ad984e6954965db199fea922ca5680ec", - "id": "2101c235de152ed1a0bde3f57593362d73149e1cb1e5b4df855718c392ac245c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AY5GwZtG9sFvDzMKAQfAD1Q8EHDh4bW718", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ad645598de2adfa46f68a9c0b849457306a8744627c9d5a085238bcf51599fa022035823c617d15fc35bf7910a88ca45e47c9b3d2aa6c9146f6c1db2b92de0d3ecb", - "id": "f9aa1c9331948314027579cf07368180d49b452bb747f153c0f25c39328d955c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AP8agRcU4WmsYhC72pBqyfLwaDU6NYKUL6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207b203b7f10f7fcd1c29379603c765d37d9c1eadb787885211729b63606fe7b3502202862e0cdc7af6f3f2ae8d48ce56b9a76bd38ef3320bb82f21bef96c6d793eee9", - "id": "f6445a905a38bf0454327fc4cd9976bbd971994b48a55906ed3c123efc0f5370", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AaFxHxsjYyYCsZtpQwwYGvYESogJ2SHxe5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206f4c7616b756110ad060c31c2e4402219f77bd266bbf2c0bdcb4e8969cf59cb4022028355a38d68cf842b88b67ca9d9e261ca5adf017e84b2b5bf04e7adb6062285d", - "id": "4ccaadad7365662dcda0d68a13a18ee08174e2b0ce3638de5d45586cefe1fec7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AHmBqWJgxZfaC2azkyASdrbCxF6csE7Qxx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220088ea11151b787f4aac1c5bed478a83ed2b31a013942ee76f6c8b5a45dd1fb3d022068d99ce4db49d383fe1987682a459620153e616de651b45f092839b7afa4b29a", - "id": "600b4e8a38b45624d23f8f6a11424cd20a9ed956fa970cd317b126221c2c870b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AZengw5WND4WLC8JKz6xUDFwLz3yCKPpTC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022001c34abe513df6f732d459e995d5ab3ac6ce064213ba79f502472fbd23d2a41b0220799e96bdbe031aa06aa9fd14cb25b49b71244298cc04f5835fd3c947bcacdb61", - "id": "fca76de221fc1f70b642f30c4e5ea024f5ff886fbbe2f51f4ac7fac4bce94629", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AeLV4NUMsPJW1nvHquBWPFVKCWirs1pshf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d37946b7e677220e9333aa5d428e8b82a8553d2b3e0c1adbd8551a75112b54ee0220465fdfad076cdde91b5642305ee4c237c4093dcc210b9445c374660b1a11ae40", - "id": "6680f73be6c0d1f96e187440bf3575eed655477e15d8f1da85388453f5c9390a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AJrXd5u3y6FH5HktH2jgkLQHjgD9ZztMtk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a97bd4beb5444b5c2dea233a7dfc11c5ccb705a88799a7386f188a159477ef56022011875cadccf32228d693ebf24b6a1acbd574b615dabb2763e4a29b75591dc75e", - "id": "0520f4b24257ba8ad735613737ac0f0f2bd593ab5c52edcac746f4bbc8c635e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "Ad6y8ae35QWkrtiLBpiXRR5Cj2KK2u3EGX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b6065b0dab807b36139d101154d5e16c145fcd9ee807c06e46ba60e172efedcd022030e641f2c5ee29da508ceab6649593a6cb872d89d4661b3fb2b79f48b20c4ec9", - "id": "04e36364fb8007bfe7f85914c22cbe4bad262c12631ec4f339858d82213adc7c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AQaKx7UU8857b4tJij1jb7aURUzd3GDyKt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220540f725157e6fa9e1d5a7e495504b5007d9d554ad8f0c275e0e45a04028cf27f0220660a555068c7402672443beffc2bcaef8a85f06f9ed38ab558ede7a21e265bac", - "id": "3dbb56502f32939958b41aeaf84e9cbd4483a85b23ab32f979a3c1f0d683db10", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AGb36aHfdxvqbMqoDCnm6wVkKKtqkE3zAh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022063d7b93cfedf0daa9255bb1ab9a3955367c5bc8941b386f548fb01db85507bb902200cdde437236daa8fc1b396fd7de9294a9cdcbfdbd22df347bd635bd66581704d", - "id": "1c99e21a5232ccecf47da6b530eba9ceef87f02baa91cb333fcb3b8ca18edccb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "ARDkQVS4DbJnErrptyWSWdJD8gtaPEyRH8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c1b92b570f6abf42650414541ddf44693c61835816883bc2de0c4e7997926640022015e109e9f622844d8930a8e497ea2af2fae7089a4a110ff2d3c6fca46b19a70e", - "id": "fda4e22bce9444a6bc7d952c0cab822a5408ff74a19aa8e6d47b61513e061bd3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AW91q3n1QYTn3caBm7KR35zexcJU7PgMtZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009010a3346835bd198bd011a5a3cac7ef94685c96acd668ee92be386e9b267fe2022078a272a9552454e80ff83dd2a01b3621fce5818d71fbd6430e0848ba85cd2c32", - "id": "52cb2975b2dec5cd21beac470055a254a84169e51b1a72387757a340509a5049", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AYaYHEKaJvLLWX2NgM8VXk15zSDxV8vCgn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100894832a480b4ce89972badf315ba89a21f9c200cf83dc402dfac475107197852022036158362b58228e69defeeed4035e9491f20e38b8435a4b8afd86718906ec42e", - "id": "1b693cf3a23efe80385fb0f8f2b7e1ec123af4f5b99795a154f7dd1aca505950", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 0, - "fee": 0, - "recipientId": "AGNMmJ5upuU38ucaG3TUsG1ESaQDExSMo4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e2d35dbf837de046e351c2bdc8a7061edca5af3cf01d8c47ed468225a85f688e02202cd057264c2d6f17a4b0ab9d054de7e6db6349eb1ffa2beecda58960ce80b4d8", - "id": "509e2950f47ac036f4ca1545f3f3e4dee77db3756682175d292558b920948579", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 128537390, - "fee": 0, - "recipientId": "AapqpT6xF7q1Enu44UkL75c76uNuWmRWkB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ba8eec5c24356ccc89437cfb53440d3db1929fdae0133a4744a4fc61ee1c768502202775dca5d3ac6c2c3b6d5b72e58a975cbdbdfeacf31f26d46486a420fa12bf9b", - "id": "6300b6f026c1e1657f7d7328bcbe58e5e1c6a8e56eb5f1cb1402bd1225786c90", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470150572, - "fee": 0, - "recipientId": "ARc9Qz4v9wNbneBSNk5s4M1RKXwoMzLiug", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220084a67416a41da67ff634eab70f88443a7f3d531976c84b310a07cb0a6ced39102201f26c0d886785aaac15d5efc79b9545bf8da36ec4b4a491a2d33a937a97bb9d0", - "id": "33c5449bf830f309809f67aee50bafdfbabc3fe284aed0d5a84059baec213843", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1939749374, - "fee": 0, - "recipientId": "AcE7Xh9FTaMB9pUKQgST1BHfXnpmganLAN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a3515ef9744947b6b3cfcc3c6bbc6ccc05b7c158ecb639b0a46b0c5d38132b3b02202d2c20ebd19a0f60da85961a6dfcdcc905858e11556a0fbe3f37a36ea286703d", - "id": "5359b1e1068625f47432ba3a1ac12fda7411225f9be163d7248c25a92080c496", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2223350287, - "fee": 0, - "recipientId": "AcGWNuiBErRUP9MeXGW3yUQrhQTNKh4keP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207d9fc26887b0a6ebc7877d0f96cd49fb6f51e09c5f7086c8030740bd19c7f8200220229ac976a89a3f1b061d7f833c916fc76da131629d0373e20f6a10680128c246", - "id": "68e9ebe8e03c06a773223752296acaacc47503411398d75ce9aea8d1809900a1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2635948642, - "fee": 0, - "recipientId": "ASHDLexaM8z53tcrddDC9qNrG48KiykCFj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220390fd742fd9ad375f06b2dd4be9fd28976facd9ab58a3b497f9c53627c54903e02207a13f2cf4b2e35b512d9f25344d7cbaffa7533a841b444be4131ef3ccbb9a707", - "id": "140dad637fb5e85d9bb48885be2ce2caf63599982325fceef875f5438dba6379", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3319628053, - "fee": 0, - "recipientId": "AKoFbGWi8ZpD1FKLtWbs4PFdR4H5L9mNqj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fbaf95f5cf4ed909feefc9b0d3d82a27c6281837320174fb9c1cc65c6dfc4b3c0220571f50b5ba6c1b9effff224d5ea08a1dd79753d07487694897f5b053cc7bac56", - "id": "fc4460c527373e2cae1ef706aeb846d0d011f5fe3a15d5b29727ec6d4a65b378", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3500000000, - "fee": 0, - "recipientId": "AcT6rrsUh2T23TXVgfXxXLQKmpRbECs4N4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ce9a0287b36c323e4dfbd845a2d0bd30cfeadbaaa4314c5055d3f4194d05aa1402207aeba8bf92955383a8bb95ce75628a258c5ce379e3bb482787b66db194bc24c6", - "id": "255952a161258cd10a8e70bb03be46b8005101f05ad0ad453e50bf8b5f1c39c1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3500000000, - "fee": 0, - "recipientId": "AYxobqMdZyUFvvX9o3ihE4iMnZWCzQBhgs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a8ba213f88717697168ef6647a89ea7d7d1a7bb56c8b82a27f4019cb351f13b502207afb90964461534dff853f2bd8a55e36fe6660de37b0864f65e4220c45cff2b5", - "id": "4a761497352e5687757421e2cad81fd7881c45395fafd33fa5d4266d98d3b40e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3606920711, - "fee": 0, - "recipientId": "AMepHWLBpbShRDbwLcEJhyHRatqGAMDa6r", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a35fee7952af116cb4ec7b4881f9fe5056371287ee26034206d964171c76f05d022053b2d4cf6bd03c89db073a25270ce48fda9b1c98a6cf19136291b912040d8917", - "id": "5cbe722d6e53c668c603db420a577c8a88f2c57c838141d7b0d336ecf4d67541", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4373745201, - "fee": 0, - "recipientId": "AXZCfAqHYyFUpYxYgNyw6W9XVngp25rGkq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d430485cd384e4a58efddc72ce62e203a5ce80eb29699fb293b7d378521c932d02201e0d8cd73d3c98ca4973fe45ca2d161443f045125c971304864f9dc4e7d47645", - "id": "4f29a835860ea8533c479ecaac35bf14af9c12689a4d40ad8f4b07e55efe9a91", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4410451715, - "fee": 0, - "recipientId": "ATyCrCUvZN7hJfkYWbCXgKfEZp7PB7ez6V", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e90a709462f9654e698f131cee2fe1ae82852e12909909c32bb4ad8883e5e764022065c75a2511725d489a625ce0f4321119c43d2df7831fa748d788c879cea94eca", - "id": "16f6d59d7b48c528332ba917f7710f5af66dcc647a0c129165da7508aa93e826", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4410479871, - "fee": 0, - "recipientId": "ASrrGt6HcDBYe6hTqX2p4cD1AjNfiC3vnv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f42315460ff894d560857f7b863e22b8e9b14bde5a86ccd34c4189e37e83a8ed022036ad285ab692485715db059896f7d394382dc6ae8386627d6bfde5771a01279e", - "id": "6497901d7e0d33d9ebc7b510100ada1146f9be06e960528d789c4d62f1198855", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5445780417, - "fee": 0, - "recipientId": "APRgb6yUmpvtaCyPmLuSzXPucgPiJ6FRpJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cef35ed420e2dc8c5f0de5911474f0b22c0b5f0eebf9cfb453f6832892b47fe502207f52c64e0bcb8fabcfd641a8c0a680d6f6a80ce47df726e276f620131f681c70", - "id": "1ba4319ef107661772377dc6587a13c08493dc0a02499eea9f69de567fce243c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5549484283, - "fee": 0, - "recipientId": "AQqQZPw1ZNhRqmbYfVisYc6muRF1TrUk32", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100aad3e80ae4d92622e819f38872ef85c0edb22300dfbc3fe8ec67ce570c0fa45a022076ce462d4b9cc5ae6340eae28219b1949ca4e5d70eb01fb70c92d759513402b6", - "id": "ddd800bcdce22996cf22036b8de5903e7f5719c5aa9142e4aea0a79b25837df7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5618558679, - "fee": 0, - "recipientId": "AXd5pgP55H9Xd9i6ErnzM3mWpZ8hZpYaCN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b5860683085463cbb8f828e4749c08b31e1c8a0cb5c5be8166414c7d38da33100220053a0efa7541a6a63cdb752bf3ca7eff289d4314c01c1215ee86dddd27d0c68d", - "id": "12bc04a7470e5929cadeaaa4f1ac786861eaecac4f6792b38b5bd6a8596b6395", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5677245896, - "fee": 0, - "recipientId": "AKURdwRzPFAS4XhPUtBAqKrTrLu9LPqFbj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fcef07fa5b582bf19686f0df769c110a74e2503fa653475b82f7f5e6faa3be0b0220647b219bd0b2163b6fa363b62bdfef06f2df6006325b878455f806dfd44c09e1", - "id": "2436864a65bb862ba864aaf5e6334bba91a7674a032abd2ad9f2a3895d08309b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6558699223, - "fee": 0, - "recipientId": "AaNyZo6YitELcMW9SyWXiG1FVTUmrK39Zc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022078ce2baec0753ba02c05dff2efe59dbb628cd279307c7bd7d884776f2c15f35f022007f3cfe57778813c908496b147ada7c6d3531111cee9fb56053d09099a4b878d", - "id": "64277b25a02735c525a4f11417772288ac3e71f0fd75c8e00d6f40e6a2af8cb6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6900000000, - "fee": 0, - "recipientId": "AY3adwSjUDSKysWv8Ym4UaGYeqDx9V5a1v", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200cd7794247be234bb63d915a2c777372a89d0762c3c5299178bccec15b6be0ce022048130b001b7d24136435c148838e1caa6b176b98a0fa052314e408f26f81db35", - "id": "644fdeb9e3e870bf0e6558234075ecd9be5addff36f9fec81c28aa7c8ff57a86", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350752858, - "fee": 0, - "recipientId": "AY9k6pyVCWzkCNdnfges2yjGJVJyhLdYw3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220216dd68a632f1f0846cfb628921df1ff06772d478185d05c20986ba09efacc8a02207ea5c3932b37e98b491041ad0169554e3145e06a03088ca5c4e92a6fb184c6d4", - "id": "00cdc908312961a8350276e109985ee2dc325a984d92d8ad64d60dafae5e1ef6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350752858, - "fee": 0, - "recipientId": "AV6aU7fieAyQQhMpjAdiUPF8w1SMujpVo5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f9e222aee99a87a56193fc9661fcb15754e43c41d3a37a1104c063b8e754d37202200c31da497f5e8e84069c9265d816d5ea8c80591836ea68ce94b4cfad308c619a", - "id": "71788c698bc0997546b1c9c2da21cdc2f391a3a295193c61582cb5788571c5c4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8174205069, - "fee": 0, - "recipientId": "AWhKaR8aPiTxfMaMypQqt2LtfDEUGpB1kz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ce4bac79228cd04ad09239a51a423a4c0e3e0aa48feecb5de9d3ad9402b6678b02205acaba891280ef0fe4c1ea18ed56d6c0693f5a0d31ae9fd00e266e3dddb0c5d5", - "id": "586cf12649c475cf490b6f7ad6bda018032da30e87bd257ebe2b222391e932ff", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8335753741, - "fee": 0, - "recipientId": "AW1BDJsPkEV6AUfZtk5js9RKqHYfjEnuBH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009c055053f467f1571cc395463e23f7225580fbf5814d17756a7a421eb812c709022011f5a333a164b685998de4fbd5625c55a35c4a35e31643f8653b74f84d488836", - "id": "dba64b2e7c7f94a2afc083b73fe947c069070b534e442cd5958361ef4b22880d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8793099579, - "fee": 0, - "recipientId": "AH2QuDHZFX3rkLFkFTBrRshjz9VAaDVu2z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022075820ff7058679540c808d1fceea1033fef3c80fdbdc1f7409ad40e9ee22249b02207a60b839bb427497387c71965871304b2418afcc7aef71495b3bed16bbb87445", - "id": "fd79cc4dced6ab4862faf2c0b5cde19b3ecda72ac2453558001cee1e5f90af09", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8930739717, - "fee": 0, - "recipientId": "ATiYQXzewAtwbKtNb8n5uoRpATXvASGu9C", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022043721eada610fccc21dfcc12eca7604854d55ff996a1aee8ef1b6ed6dcfc9c7c02205c1b63a8dc3317a90c41366477b12e6bbb4b26aaf1951546b956b2a019849633", - "id": "4b309d61a46af825c19faec1e116c949e0e4958bed87afe0ef3c81336e269169", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9285850665, - "fee": 0, - "recipientId": "AL2DsLKiUjXFKBbByaLP1RsdUMeNyj8Pre", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207310e88bc29ed44d42c60cc2bfd8cd0cf41751a7c44a72d47f0ef597a312efc0022074f06ab9ddb0dd66b6095f8324a65d8e57841db18df7569427e3001222bfffd1", - "id": "fc59c5754fa17a47bb34b642ee71b99fff47e1cca8d877788490a6401c1f969b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10062477006, - "fee": 0, - "recipientId": "AQjGegCDm4ig1o2hAmxBwWYAta74ZvS8f8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022035956acc21b16b19f446509f805fcda6ec6aa5bb09803e9367e3459b05de6a630220310de9e98092f88787e9ccc503bf67439dcb47e4516eeac015ca5064c6c8230e", - "id": "96db83cae120bcee57cc27436207abb76202272b558e5fd7579fcda5f5f9f61d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10090126421, - "fee": 0, - "recipientId": "AXhCBxMufzWdxQoGQi3ZPS1SQokqHr7SQ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202cda06e19276c0d76680c0fd5177c8e143da4863cd47f2ab4a9f9634abf0b28402205e278ca769cdafefbe74862d94df273382ba379ac0efd90c306d817951d75703", - "id": "f5a2f41b66cb6ddcbc7d68cefea3f5b884f18c2e981eeb8731280dad92d781e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10400000000, - "fee": 0, - "recipientId": "AbDSp8xeAfoUR6mauGYE5P3JTDXXnWYmjJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220684bdff8dbcd3dbee5bd9d98d630db30d6e285f82f06349cd4c85500d4e20fe4022049809890d215e9041fedaa8996dcacf2b59cfa949ba44d5b19bfc0aa3d993f77", - "id": "6bcee0de136de52f7fa27f7641d3fd661bdc17069f874c189b09593936b9511a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10469786602, - "fee": 0, - "recipientId": "ATQACwzUT92xq8ftk83nm9gc8cDELLk1wt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203261caf9bd95edc131e4c18f7c0fd985d477314f70b85fea340501934f6968f602202c0c225421471e7ca9ba8e1bf3fa38ee5ab35187ceb5f272c6eaa36b2fcfa127", - "id": "8a8a7657c453a4eceeba12235add5d02a27dcfbd27a82238e586e673ba2d8dc2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10792241696, - "fee": 0, - "recipientId": "AaD6oZFiPZaWDgAz3NGdjASA4Lcw1n1yFB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f80771b2256f6e642bb27369d356716861ac83bc0d779870055e53a362bed01402201c5e896aeedc8acbe608ec78f14b87c623069b627d5470d835f07db98dd3394b", - "id": "194ee2091f2dfe779eaa985ae67489ef6f4caef181911050d2d3615292eb9345", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10860889308, - "fee": 0, - "recipientId": "AQr31AJnzbHCTxtVgbguzDEL5VLHUqpjhb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ffaa693ebc94e479852ec7d47a90b96f82eebaf2c00b73adfee062ec8f764851022039b50274a148a43a23f7744c30cb00a370ebbd5d39a2e8aff5d91465f3b3a95e", - "id": "177f350bb7801ca83c881fa56256c9f9d63dd8b78d508e67ee74d8455852eae8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 11300031743, - "fee": 0, - "recipientId": "AKgJL6ko3BvdJXKdRDuZ4f9kAdvmNRiq34", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d2012faa0994f60a9e9ab7049c703e223de18e4b89fc71b2204fe73b17af9708022024c5294f0424a68afc1d2975f7d300e35a4440b937f49684ceeeeb64277cc0cd", - "id": "b513d351901d269acf8389af328ed20dd0146542f83b80cb4c3ba8f8a0fb9bce", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12028504677, - "fee": 0, - "recipientId": "APg3XHNRy3pxHmg9jnbb6iSqq67dEyeup6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022018b80f68abdc7e6940dca597bb8d24c3a6150c3b6cedfa85f13f3e73aeefb720022047f45325bb01e0d6b9f300a80b0b5b15d5f9f453b646dd84371cc23ed63186b6", - "id": "e19f84fd9f142d50860f6725a79d8c1a14b90e19301ca793ba4a1d13bed8ab22", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12416758078, - "fee": 0, - "recipientId": "AJZA2GSpqAAjxCBsQS6ASjryGwFvEDz4wY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022012701d828ce4db33b2f03f4dc42720189cb3e0aa82f48ff8e0284b3a4deaa0b902207a1685bd283e1c51c42002a8041c31ee518307c4b86c7f22c78acdd863cbb4a7", - "id": "3c75b71a126df25c97986d07402e8b153919ed29dd8e86ef7b5d232dc4c27141", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13626793145, - "fee": 0, - "recipientId": "AHUzDz27ouUBRnBB8MVYEAjaEkjzfYxAZy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207dccfdaccd0afa9d7fe39da6737f1fa1588cfaa83011f9913e46f9e0bc2d60f5022030b46c49b11c5513d7041963c76394f6a643f60fc9f76af2549ea72758d0d33e", - "id": "ff635c6f6988222c2dfe24ebecd0ce33f5c9310dda198657343c16d9c2c73163", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13900000000, - "fee": 0, - "recipientId": "Aezpc3WQ487fMYKeoH5CxF3X1BQpG3Z1Tv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ee21909eedef0a8e7c587263c5a6d8430dc487a1b96ed2d5c662e69c9cc6cc610220341bac16dd02f854fc19f9f3af37489c0b4f4035bf309bc6ef81f22997a3b814", - "id": "06f9cae8f3f46a0f3fe4f1b53efe34cc8c502bd1fbbb2fd15d52d8d0cef3846d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13900000000, - "fee": 0, - "recipientId": "AUhaVctz4y38gYtk8kAH5iiHsUZJyiV9QK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cc8a312b243e021212b170071f32ce4174013460e275166c9f17020e769de2c202206b71d09fdc88f4ac32677c19ccaa21408045e1756ac5d7ab4b94352dd21ed038", - "id": "e5a8d56567089b515a7878383ea67dea1690fb4624e8870f566848a28fd2467e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14407475602, - "fee": 0, - "recipientId": "AN5fzWHTJm6LuJjU2iLqKyS2HGrz7n1awJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6203ac00161224464cc3c4ec7df00bf57446cbc9b19d96b4895df4a550bc126022059ec4181988c1f19ae9e790098d17bafffb2668d11b4aa2dd6581abf4516de0a", - "id": "817ab520c018dbf31f24790d8ca23e5f5c1f6e598cf204c58427289dd9c7299a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14554490659, - "fee": 0, - "recipientId": "AWQzdRCybJwnWsJcB4Ux1ZWCcudmuZj29L", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f61c81d36b176998d3c00b6490c0ecc79726693b04f7f32bdd0522076602e99e02204d8bffead0b7adc26cb5245c5a8de9db29909a4982bc1d371db83e69e005864f", - "id": "397f2509d05348aa49de4a15f4d3adec5379ef12521541ba3388d2c73d45ed35", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14561654186, - "fee": 0, - "recipientId": "AdBh5du8W41f6JXJxFVmHE5x97cRG9kojq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b15dbd1e14d3a4ec461cf52edf840144bb0094e09e2a9d1b91f9a9669f6b929f022038ae77df2e975e856a57f656bd04f4fd8b08b11166503de53affdf50da0891f5", - "id": "39d14c1d51ac1509fc9dab4be8b3b40f540c12b11392fc7fb45ed208f0696ca7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14701505716, - "fee": 0, - "recipientId": "AaoM7ppRsH58K2ABYH1YxSyBKW7z3FrDpY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049720a06d232f1232cd8f09e17128721f32bac33a8b210baa322ff6bd813f5520220402aa73d01e414a5953260b79946172ecb53e02326c9baef65c0d6a80e71afe4", - "id": "6d93427a7be1230f2faed5c635893d0f7e376b9bf13fec2ebbd7d6d314bc7e7c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AM16EJe7VJpQtFPkoYLyatgNDJmaoNW1EK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ab3dded9d9a320de89ad11a137ec45cdc6caa558b1ed312c2cfab6a5e73028df022050adffc944953f798ddb791b9f233e1503856f6c16867e06cb91fc18f503685b", - "id": "a9e8e4e49962cffc045abba3b6eaaa77f2955daa6c3b2bb62497f861e26c5b04", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AUPFE3QxTSpq61H8T5pumPcZNBtxbPSCvr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc62bb643a1344403854746bbae7bffc44228db69528c485a1bac9b44051160302207628f7a3ca9b230a77dfa13714572f0f28d421356288fce43d695753077173a2", - "id": "c4ced95ee40260f04bb935b7709c4a19c969b77311dcc75537165c30f43793a4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "ALeKWmZh3XGjdgSboRyFU5p1k1BQpuriwt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203abcccd1b15a9857f111db5dfbc65135ae361ff3548a94cdcf9d18f38f1ccc4d022064f4dfbb1a13e532ea2d629ba4a4d5cc85622c18e6f5fea4c5cca9de6e8aeca9", - "id": "110689874c1ab40dd0cdb8af84630a1b89074c4f751e662105017f5e351e1841", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "Ac9tpzJ5h72V7rCobsaLxeC2Y4aHmki9EK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200a67c0321177682ad46b56c287807a6c6fb3c68cb35e7fea4325c26b1ff3c3b0022038d12784c4b342b950f43dfc5592d57703c33e331e867a33fc0bf5d6243406e3", - "id": "3293e055b85c9c3a34f2099261032891a326cbd39541a80dab24cc4c5efff8c8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AZ8hMVpGVT1hyHt8nonk5ip8fKFCST7KZ8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e1d4503779cf4c4e9e90579b2de7eea38a344ae1d1a5be81961309118b13ee7202207b977b82d13e913e0f4f9f37ac713b8deb3ea5b752cd3495af4a00a99c62f853", - "id": "b1d086b1b1bdfb25160c121232d62f2246e0f2125f557f7657439def1a5b473b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AXxBftbe7ARvVnaLTpXyY6bSWSnL1b7iQw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022057b0ec2903cf6d7741db49ae7ed7e6243e2bda73f878c5ff93e78b9e9941169402201c199cf06a81408eaafa26c8dc6195f243fbfd193076df3ee362e502b555ba8c", - "id": "5555d7f9855671019c47805a4aa14c3054fd4aef01fa161cb7d7e3fc79388b0a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AcB6nKH6ueGiN1ygXurgkdXUVTNGPrAiVA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b9a86d98d13a708b647d00ca9a28732af3707ed3ea34b173e66a20340666eb9802204de9b88e185c87055b454f146b8472d6513883ff065ebb251c155e59ac56ce28", - "id": "3922852d56b26b389bb1cbcfaf3235d2721a9e65e72185946dcbb46e09de2889", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AZJByMD3mzvS1ux4emnxrJbYAf2mEsS8bu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e7c70ded41dc8ff70b306e5fe5a01fcbf38057d2a38875e9618deecbd5e1bef40220031687e7a7286f4856e056fb86a600c5fe06cfd05d1373ad451a8e33c3d7ffb6", - "id": "dd619abb201619f8e6361f54aff5d95ab8a45f1b75c866e86a68b4319094bb01", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "ARVv9D5LedrdyjyuLPM45ALNowdWZCu9Ww", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022029337e12c30c5a1f3ea0b17a53dcc263bbb88343f140765e5dd5b7661cb7249802206c50a6520a2b47313a614d9d657199b5bd22f6a58c10374bde951078ab66bdf4", - "id": "271e64895989fc542cee02a54399c1e8dfab53f596658a095d7f3818f9d147c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AJ6Swgm7ABDGZd7vdBuAv3xHL64MGSFZ1d", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220640d9872265a1f6b0649c593e9850bb77023c1122e0483e03924cc44d8421cab022063368396d31392bd0fce5b5aeb2d0a745a4462ee3b47bea7499a8bfdbab3e218", - "id": "b1c51b691bd763cf532b060f04238af305a3965f2755d3b8295c1145c2c78acd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AbiXRAWJQvtxiEMzVZm45Jj7dTYF6amMx8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008a0d3a40ba64688ce776449872561a1887d272b885ed9dc77464f5e0e9044ae5022062db52bcbf422b12bebdaffbf4f207f0240b803478764aade7c5440b0efdf413", - "id": "4616809d23f8caeac00a90f33547db0d3c31b6d21dc0c6d1223a2f2b6f8acb9b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AQd294jzG4M7snWPK4chtyrKDTGBN3xU37", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220073c9db4fff5e1b5d59f7b36a29d250ccd0c352d8e30f42d641c0aecb9fcbb780220304f3f1237fbfdd6feb055a4d193a2a24f3d78d36891f86c752059704cb87d84", - "id": "8356eea142b2f8dc858aaf9ab5038dbfc8f71f6a6fae25eb0f9a1e7b0c9c02b8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AJ4WmzahkzX1mzQGQ2ASfXUwVNsTr2nK8c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100beb99fe85584c1db0e164c49ad211ef99e09b62fecb4ab5bd388b82f3069a3b602206657572320712fa459da101ea422229843f65f7c63cd0f2ec86d49aec3895cb4", - "id": "d3e6dae3d4d3bcad431182bcac5dff365e0ef615c1bc44be29776b9b62eaa847", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "ANZyNXsDsx8pCWwodv4VBp5vQotCgFSLpQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c1b2099ba3d7dcc366b013be5a09fce01599bb707d25c8ca4b8112c1fa65eb1a02203468b56d9ed1247cf21bfa67fd83307eb6c78741f51f24d5722033249193300d", - "id": "eb1c6fbc04b75fdb056432946f90715f02c4a1f3cbba7a1da1e11cf923d9c26f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AGZ466UoZ8rmMwYqCobkJieDn5WnmuNJBT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220115463a85eedde299e2181968838add9326a9f5f11a066e2de1063372d96d2b102203299c9a0998b75353b474a383f6a8ee5636964ded2265284924a1810dc8a2e6b", - "id": "42362155399528b008cb1945e8271cda6ab8232ac38c1a9f1ba092722162d5ef", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AQK3Ew5GGnHTyrFyt4fZRY6xobNoiWAa9S", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220232b18ce1bb8fec9f105de89b07be45b4851032fd57adb11227c7b408fe4910002204fc98352522425910dedc1a10876da390acdf2737a3efeef36afc0b77c26c114", - "id": "01899d20bf6bb2163d05c2dfb1800fc9abfd8fda8c708b313dd38c44eed60e27", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AKLLCyWEnnsXPLxQ1S91J76WXL2YsgCxo8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009b8269539e07b9055f9cab99b49218c0535124d99a366cba09a674a31371e82902203b6b6cf090facfbda718b53f181e046bab31040ed1a6bcdc702c519b2fec40df", - "id": "053d16a85becee53a301904de426d478499b1de157a9f15db98f91c4783173f1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AZ36zcRJmdUJ64NvN1M2T9pzv7dLAWtp6U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008a664c0db5cdf291bb0da5dd05b22a539f8b538286650c2f7dc91def82ecf204022035033583b9bf16e4b0e3db65b4125c567e0591a8c69b5739ee158b55f8c842c9", - "id": "968c02469edccfcc11797a12463cef24aa7ae281aa648854fbff46b6634b5279", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15000000000, - "fee": 0, - "recipientId": "AYhCgzBpHXC9kgRCu9FUyRpjtqvfqNSr4o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f207cdc66a586a04eb9e7a5c777c4ea37434b73dde96b05a0c7ac666064d69c7022058eb07e5f8adac444c5eb113d25d09da6fa42e9f7a3a5ae778f479c953039610", - "id": "67b0005a47824abec015c2799d99ddb2036bbf932a36cf05db7575fbaee92098", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15155129039, - "fee": 0, - "recipientId": "AN5x6WfeFwtvyqDvpaN6C1MusAvSCzZU45", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f2577c3278f2aae85a9cbce9b581c9c094d2f33b2e8173ff6b4091a12008da9a0220753f0064d21a0b871807911194832c3c255060f3a6dd7730fcd6538de884392b", - "id": "c2f08ecf56771833549e92030bc839761e353fd88330cd7e414f9e5e33869b99", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15155129039, - "fee": 0, - "recipientId": "AcgtP5SQDHp9gjDwe4KPVVE83JLGwWGvUG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210096dd68b49e599249c0d35342a8375ef543fa62670c6f67602e2415db4b788cb302202cc2edc7b1b318c4c127acc7fdcf34c7a5385d078a683cb6f10593272091a6a0", - "id": "887cc5bf0335496a821123b14efb9b9e989645f90911c1f7fdb9f44924c89f52", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15155129039, - "fee": 0, - "recipientId": "AU61SM9NBrBfiCVyJ3wYMZ73GhvGPcQhkj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022017fa1b374e87bb1dcc55ed07a0dbd64efa5a148d5b1f3604b6890dc43ff6022402204a0e788d401f1223b27b4c2e0cc5631cbc92d1301827a53bd2d7a7ca411d41dd", - "id": "3dc4b91d426b88fff8d78258c20991afa63c247572ba06ea469ae2a4887a4aa6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15155129039, - "fee": 0, - "recipientId": "AJN82CKCtJ912F93y6mHEmbWS7ny1He3c3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022042cced4fe76ea745f6e41b70a696a800c2172eaecb24c77845b35355f882dde402200e7a5e5cdcb4411a92bae306df4c01a3ff8ee5d53b6972518dbb1f50f768ee0c", - "id": "445db40bc02d41ea28aba75cc2069cb212793b0c202fe5346feb1ae304288c35", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15458231620, - "fee": 0, - "recipientId": "AebPWjx5BvurHauVa1Qj6nJD1h6h7hrnAm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eb671a7f10f305a65bf38632f205cc28f850d304e24d49cdcd19d7ceecd20b830220658bee7489d4555361f2d330fd31f59e146c489070c2522dbfb2009a3ccc5209", - "id": "0a6de5b1e8dfbccd33051fa1bcdba56d8f8ba2beef3c5ff7310620695ed8f7ed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15912885491, - "fee": 0, - "recipientId": "AZTp75YcToEXwnZU51Meb5etPteCx72DCp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100831c7fabcbfc8e4f0d7d093d2f80f4dc964bbbb7139d1f438ee539cb3e5266ab02207a4c930391f10a571b7047df2059de1aafc255da9225425cc09f6008e8b93889", - "id": "6b144c56b46f2d418538d2af9ba9890847b3073cb6f0d91c0dd4406402361cb8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15912885491, - "fee": 0, - "recipientId": "ANFaKygQ8EoZFiyvWi2SaRT8WbfwXpW21R", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205d825095fba54447404abf0c7721ebde7ec9d1bafc56bd4673eaa2146dd0c24d022029586e4fd33e94280ce99760eeeb4f12c51e2b068df63cdadc090dc5de09a741", - "id": "74f7c9d800d11facf0e9fd059069e0a05eaddefddc35e684c77f8bc9c6502d45", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15912885491, - "fee": 0, - "recipientId": "AVbpuFDTAyRmt1BRakzWTJyuokTRpBbeqo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f70e2674210db0cd7ab970fd8b58cb86da605ff850284058d8d6803668950ff902202669e91f8004f9ad85b19312539eb5b1a393d9ed101659980656fca3a7f2b978", - "id": "d828823d3b2287d38edca76dd4e83a88557b2b8da43475cd6197ce16c906834d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 16670641943, - "fee": 0, - "recipientId": "AG31y1h83VJt7MUvv4zu3ergGFqfpUpNkw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022050e9976c6dade3e5c5e365ff0f4ffb1990ff83e6613015559439c18e62395ab20220083ae7dd6567213a1ea0d762c24af46f8845080bd554f57e675b501c30c11ebb", - "id": "85df60d54bca2146dc642dd2a2bde7ec60c2b00ab7868b3487c3e4cfec742385", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17079830426, - "fee": 0, - "recipientId": "ANaUP88ggMTde3BhtTXvm8N3UwSLADG54e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207e0f782747a7020e5f2b0c72ce0bf366bf43ce2b8016c937d376adfd583aadfd02204607186b59392cf5d7899a8f020fb7298d974dadac28f3beebe82cc58b2843d3", - "id": "4a78a7be1bb24b6b44a6d8d20a5be158edb30f7e33028703a01488194439628e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17300000000, - "fee": 0, - "recipientId": "ARUNkZzsiJa8YECfHbTxiCGv9vrFLZcdKN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d143d7c5f2d41212209c85a3f762dff3321fbf3e766b6746a9483e5136f62ab402203117b6c3c8fdeb69dd8779319c3652ee0390e3ff8771cab0fcf18512e885fd18", - "id": "ebc1f86a8dfcb7ae369e124628a80298b868939e410a2bb5262c467b43a21b3e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17300000000, - "fee": 0, - "recipientId": "AepyttoE4qxeaX3Q6tmhvJmhWR4e59UgNN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220100a5efb6722cb9e96ebd5bc5af40854bc765e7b68c5d42b56d7b92e48c32ecf0220428e3ef95633e89d1b125fbc8c75ffeaf093971b70c553722232e464965ac6ae", - "id": "3b6bb91064213ed8006dfbb57a7aa9fc9b92acd1ff152625bb13c84d4a1c9fdf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17300000000, - "fee": 0, - "recipientId": "AK1DoQ2TzXwZFpqRuuLRkuMLtxvnwYA6BN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206d8e23fd7873b9b3b176c2f81ca68bb8f6a39e86f48e01afd58f1d72b2f370170220160f43117a81dea41d0bd16bca567744b30a0df789d4cdecf1b4969f0de5a16d", - "id": "f77518bfec4286fd33fb36f0c6bfddd86fb51a4394034d80f12616b9c839eca7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17300000000, - "fee": 0, - "recipientId": "Af2dxbRzquQbsEDdc6YyK38EYoa86bLaS7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022072d8f98d2aa1dc1ea479aca3d4b3232de3619674c0b484b4f821394ffb91663802201ef857234756b2c4dc090b32375a399c9b9981c616d4106ce35f18b55e7883a2", - "id": "c05281457bb1438b3a8dd85840f6aa8a4e87089f8f41e3243ca146f1ca406836", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17300000000, - "fee": 0, - "recipientId": "AdgRuyVNXHvKYyqfTqHNN6FL8nQhFuybe9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203fcc0e870991a56bb59b4d5e3a8be7efa5a07361a34c19802e4b7310bf2a79660220281eee0aa154c8ceb369173ba099b1cf0a46117576dd77d9d30e0bbe7cdc55b3", - "id": "bd02783756ed0a83080748833bd610847e7a54be76c138b07b417bbd09589040", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17300000000, - "fee": 0, - "recipientId": "AJZcbQZWg6yk2nZvxpuenxNEg5vdNfP7d5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204f2170f03f42e1a6013ee9ab7f585e420480fc72309e219aa536de5a1eeaeeb602207b5c6838c72cb39c48357df2419b569982eda8275bc4662c066faa7b390dc4cd", - "id": "3742052eb3b87c2e55476c66c85fb7d6f71206ef8c3c94c737a382576f142746", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17428398395, - "fee": 0, - "recipientId": "APGnKfbcBc7pKjbSWd4dqYbC6zAS9UQn6u", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a9b09e31f0cdae58a7f91e18a967e12436dcbb5dc3e56304e4803d942cbbad7002204d398385e1776bb0ed9f1cf6325cdccfb8bdb64a78287e4d96d18d5754bab1cd", - "id": "f7bbbad0d7ef708f2df7af6116907e8b89d34348d1786d22687f6e680e70b918", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17428398395, - "fee": 0, - "recipientId": "AMBQecZh541er6XkgwPgZxk7fNLWUDwjXZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f78352db270ad2e970789ced7fb13b976f20d45905cf4374d9ecb7a7b89a072d0220634c8330932815d3c89aad1f22d74f4305f9a0209279c3b5b5e849a573b763ac", - "id": "849ada089f0422ce85f82cf22cb73ea146af98d1166ecaaab0f772873f854bed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17428398395, - "fee": 0, - "recipientId": "AZHYssEeVUGz2WkDiWWkjefoSRcmNtKcxj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206cbf14e3cb952df65c1688fd79bb6dd657325b21dc9831ea4545617bd15acfe902202e828910f28cc99a91b1e28c8000b53c5389fdec3920c502ab7c2116c11d111c", - "id": "de2af52762d16398e677a58eab98730dd6ea3fa036b148fe8c701da96501938f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17428398395, - "fee": 0, - "recipientId": "AefDEneWp8sNsesZ4aQkGk91vKWCpMm23n", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022023939f4888ab00f3e2c258fcb8671a70a8b1c25a2eb52d610e1efd91ca5f813902206bf4c893b636139551282d0274f6155df35e46688974c5d9225dc1e3c7ba2230", - "id": "dfe1495aae4a5c62976804ff5ecf1e993416996f4cba9bf1df0ab0795ef5005b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17428398395, - "fee": 0, - "recipientId": "AciqQathcu91PZkK7wiKqRp61Cddop7YwY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201b8ecb50cb16def362f572efe3385be176d0ffcac0aefe6f9f378d5e899e445d022062907eb7d5381840cfcd021a1c9283dc4047a61e76df88e9cecd7852ea63e3e4", - "id": "e86a19a03fadada17fa359e563c25ebec320915ce818f28bbdb3dbe7a15545e7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17432660696, - "fee": 0, - "recipientId": "AJTnGpoECgBAwkcWJFgxNAqq6BNNwQP1hm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022045f5da273f2e2f227b3d422d418e3cbf3816958282d21131f3ec2844c9cd76920220591b8a380ee924fd2550a1e52a425fe3c7f185448347c119d2f0d515e15135ee", - "id": "aedb2f45e57c01965371534c52adbb99629ba581c788f7597da354fd72a0b885", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 18380568436, - "fee": 0, - "recipientId": "AUWioz7GLb9wrTXGMLVMGa6X4HytaUz1mt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202dbb34ce6baa94e4996bf50310955636c5082e96db5314804af02e4c446846810220333215217bd86424975be8033217fb1d74b5897ba9cbcac196efa9006765ea6a", - "id": "bc0efb1b00e63694849e719ee3552176f4dbeaeb943910ad34b681b8250c0280", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 18726879545, - "fee": 0, - "recipientId": "AXJ2kuVeuwa2MMtc9XQqe5VBfxKgt8ZAsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210097fe86ad5302cdc3a189eb42fc873abb875e7f3c4f47b4b5e581e04be5a37a0902206e8a1fa9cdf78cd7d69d0e12059709f4c2301fc6235585db9dd8d638e01a3a3d", - "id": "a4cbe3e424ff70728cfce9f31324b8115012bc47293d480f62b05228969396dd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 18826504375, - "fee": 0, - "recipientId": "AaPfmEsY8yCH9UhUdHaWr29UCruPbQC8EK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fc0dabf71ef6e74f376b5c646ceaaeb09f65c47322096e0d130e69463bd0a32f02202b52c16528b3106348834e75e68137fff3908755171c8ef89ae44e480da6d774", - "id": "79e4c62a158b1d95852406021ecc176c2eae643012071320c741c472be24ef25", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 19758347933, - "fee": 0, - "recipientId": "ASWwdLfs6zYsAnQB2MEVq2KVhbtsuuWELk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100af87163f52e7bd36dd86f728b18360df16d9d9f499acb1709a0e365080ead6730220016e4040fe172f041da4ea90eae432d28f8fe4f49326ff7178f087eeafd52945", - "id": "b60e9a8a4229478192e1bf5df84df85e009a8517b5add54ea0e779550571515f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20185909534, - "fee": 0, - "recipientId": "AXYVmQKznY2qtzpryr19zTSjTbSkfHd8aH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022062ddc327d35dc5a02d06d6a91f9303fc340829d731a2ff8c71e868482c99d70402206c4bafa555098c358b0b5e68823b103401ecad4a05a65e5a49176ba0e435d09d", - "id": "60d339709c06fce066b14b06536b0afe9d7d8f0cd346da5763240fa937d2b7e6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20235049486, - "fee": 0, - "recipientId": "AeR3975uH4kfYydrwgLQgo5pWkmJUww2nm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e06a134e0545f41c5f3a6f7f54881ae07c5e7791d23a7c9786c3015fa5fd4a60220422553d947eee3af1faf66e6e80119511651d5a2589d39c34d7cee4daba94ca3", - "id": "8242c9f18a6fa8535be6511f8b75cf6cbbc8da6cb962f8f9a0beeb697a95ecf0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20301994953, - "fee": 0, - "recipientId": "APSfK5XyawPuumG2me4ovkwDPKCSx257gW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220789829516e896e6f204699d632176feb9a721d69bc3b72392ed1a1a7b787efea02203f736d9ffcf9ff0b899d7ea648d1b74e1e938a4fe26cf3c8c6f556b0fb4a4bb7", - "id": "e0ea1ebc9da7c1a67fcee0b06203e7df1bc6ad83444f4dfff4fc37ae58f41c2d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20800000000, - "fee": 0, - "recipientId": "ARwCmALF48ZWiY9cCBWqhCV6PkFbp2Bre9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210098dcf23f4386de84fc4d75258d22469d2bd67017fea3417396393437d831628b022064427eb68be7fbfb7d034c682b2e5a3c0a740594da40843dafd4fc8fe1593528", - "id": "9c76e2b23ef60016ec4e1e54fae90bdf727172be152125e41fce7a69fb04f73d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20800000000, - "fee": 0, - "recipientId": "AUGQoqoSkCGdT9gYPNsuFumhksQBDjFLeS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022071580d7f06e5a33fe8e140208441455af5cd013853c94fd983c457e34685960b02201d7c1134e7c3e408b2dfdd61916e8b1fcbce9fa3e5539ddbb00ec47c424bc510", - "id": "8f444d8c999f8cb2a73f6b48be6f497b0230b4956ce62bc3c2c646367ae79d79", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20800000000, - "fee": 0, - "recipientId": "AQayPyz6AWMwY8RPTCPeVhxkr8hNuez3Qi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022045fc1b2a0389840a6e8495f0bab89579c2927936bbdc1237f56b2159ddbb152802207f2216ae4e65f848f96a5da8db61a64c7b0ff16a973783d1a6dc93afbabade13", - "id": "805e65bf89a54b7bf5043f510d74c9bc89a8fbdea7fa741a68bf4427bab5caa6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20800000000, - "fee": 0, - "recipientId": "AGahFaiYodwt82Qi12rt5j29p2C2MeTLBh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100897790cf4bd81566f48c2ade97bed334735704f19a67f8f9ca75f53c60756ad30220468cf778cb00ad16e4921c2377a06c2ea762f87ca41fc55e7864fe1cf18b841d", - "id": "133692fccaea85c45398542c5d4c8ac7971d4882764b60daf649f81a3204c49f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20800000000, - "fee": 0, - "recipientId": "AUPkqi1hRw1Xk95PU9DmKn489PztyNjRYh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200fd59320b0f0962b330c012ce0ebb55791d59ac26ceefbe0f89b140ffab7e5ea02201ace6578816b4751dc693f4894e04c25ca654e9f962d7f7ea710cf55a759df13", - "id": "faf85b52fd458c9be80059cd065cb328853cd66ebe35bc2f0c76160e271cdbbe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20800000000, - "fee": 0, - "recipientId": "APuwwoc1Btsz33vZNtuQDHNznD8G8CHcNY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206633a9f528e3430e1ac997eb95252c483c94308a97462f9d8d8b6175e04efc9802204cec670befc7b7e2445aea1ad7af8d300a0fdcfe5f8e185df47bf5bf2918b210", - "id": "886bf5c983e9f90993a7fbb5a50064a6c9b32db2408d09561951578a12db73c1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 21622003904, - "fee": 0, - "recipientId": "AavfBaCqpww1LarHdp74ayFp3TSVZ9QHdu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206329d87b5ab1bf32142a5688d09638ee7b80b79109350e42fca433dd4de0170702206d27e82dedb5ec747f7e16eae5c35408ad6600c0a847c130c615c7cf2875ecac", - "id": "3ac9d7d0173837c2079cfe16d4079d1f960ccbd320ecdfba026819239c4659a2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 21819296752, - "fee": 0, - "recipientId": "ARfsXLuowXfLk3pWNrJprdbn7F25hwMRYi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207289896d987705b7a0be5edcbe197f61cdf28efe3a0844bcfd70f1f1e36f3bfe02203e49e3d9e01428c39a8e5966851fe94d7b798b4c3c6690be3a098f1ecd16db17", - "id": "ccfc33a398e393b8abcdc28d431fb5a3b4753b15544d58864d7a405b828ce957", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22277220240, - "fee": 0, - "recipientId": "AGaMNkFFH7n6bXwKbXXEdfuDCN2JC4JMEY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e619395ae8cfc6dadcea3a709bb27a014c8836092bfea565ba8fa40c5fcb42002200b46a3a4503971b4238fd849b15a40a927945babd50e7107e205d9fb40bd4110", - "id": "ee656950500341fe56fbcb6b720b120a5e6ad4eb5eb75a2b89651b0526cbb782", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22426664055, - "fee": 0, - "recipientId": "AYL4yJL3TfEafvWh5abik9mjrNGDS2N5ZT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b94073a33031731ec6ca84bebbb2e63e45d4d0c509b885684b7315d769b7cb7b02202e0662f659c239b2a69583fde44eb38e8650eaf91a9edfce4e9c602106fb33df", - "id": "b5e89bf8f85d527e891b8ca3ee0f2a2e24a1daf63e50e0ade2d791aa52e03b06", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22493303746, - "fee": 0, - "recipientId": "Ac4V3AcXoBTb3gDXkBofGd996DySEZ8w68", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bb49539eec3c2ddd506812379d6d9a126f3cb252fce6e918d72920dc389db7f40220135ab2cb8c335ac8e2b7fb563f9b3cf5ed4a3189e4ff9008871b21765def468d", - "id": "f418f20dd6f1c0826b102833e01fee05ac5eccfd3c69e70c6d9694c9885fef2b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22606906279, - "fee": 0, - "recipientId": "ATksXZghzmn9RkctvKrpwPEctjEFG2s6Fv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206ca341544ec2f0047a0f1001838d1aa42c0d94b49257034e037258bf09d34a2e022021c6958774ed74bbc8ad0fc4fefcbbf3711bfa0c0b47369fad575001fd3d7df6", - "id": "42e3bda812617277293b0efb08d308dc5494e47c5823d088e1c0520d2082ff57", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22720508834, - "fee": 0, - "recipientId": "AKy9y4KXZ7XhtWmiodsb3mJSE2HH3H2VtS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207d201e258c9244f724e94f5764513a16a42dc2aeddd50da09db88b6fac5919350220437202432b51c67ed7538ae1af5ee3445093d5fe85ab500923b23c866a4e12d9", - "id": "54a8922aceaf439f3efc4ebbcddcc60adfff7170351c57fb38a3a23ef2b6e1ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22720508834, - "fee": 0, - "recipientId": "AdQTTtT1HGKsAuGzs1mgqBnwXHtMVCp6MJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100df406f87bf59816e855ba7749af423879a850e20863ea5bf5e0ff96972aaefce02201a50f1f35dedc824860e5785dbcd6b49eb09510df251b5be71d4ec1fca16e546", - "id": "7f97907d31a72371925ee8ddd94f6c9460f2d7bd837533245c2aa41b9fbcab20", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 23338898720, - "fee": 0, - "recipientId": "AJeeb4cmTjh2oKsSUt6b1Cted554SrkCuE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201500734913f9fad425d61e07ed00838e1c1e8171020c25dced36849b81be9da402200cf05fe867256dfb7467009a78e47ffc001eb80835413b4c558a465cc08c048b", - "id": "888fe214344f8d288fd0107747d8bcfc1cdd1baf596c75a1aa493b2e086861fe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 23976819323, - "fee": 0, - "recipientId": "AXtJWNWJctDDFkwuYC3J8CNBGNJorqs5KS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220678d7e0489942b5faae8fd0cf73542706f700bb4a9ae7a7037f1f13a64b74401022073f32ad4ca5b3a8ecbf843d645876656ff46382057586a85909c8454261d829e", - "id": "d7c0a0b53885f500999c7efde8b6f2dddd186247bf94f0f9b4a1864630d90dc5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 24200000000, - "fee": 0, - "recipientId": "AYet9i4fohv9fYye39z6TVkTkdAeb7g2Gm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204382461f91941863a5256166d0202c684e4fb1ba39ac43eb7cb432c8b0a90a0002205ad3056fdf794935c28e94bab65673c01e51154624cf695331687f0a3974b420", - "id": "a14fddbb3784cf2bf6491f244fc9830559bc8b3fd355decee085db44c9953b5f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 24200000000, - "fee": 0, - "recipientId": "AS3b7yzfnNX97TLzWm4ez9GiAfkbzs8WTa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022075bfdbe554fb74d09142042bcde603b6390903a8baa2eae9995193f87bcabad2022048a005a2d88496da6de06cf027ec78de4563267a8de12c0f258580d1e2d44638", - "id": "74b83c00431943cdf0a97d5f801eed6045c1f57e10d0c07c5437954b4b811efe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 24748325720, - "fee": 0, - "recipientId": "AGUdj5MtJ4HnmcAFtdd8GscPBGwRC4hzYK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008600ceac976e10082007b40015e56d19d6924f888e6e620ddf8ae114bc877b5502207cc0afad875ec422d817fbcd6a067ffce41c013d6d971083b52389ee567726d5", - "id": "a64ef1a8982ad70a13440e5a901066ae8bbab1b1015113bd53a48c14e886d2b9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 24859867470, - "fee": 0, - "recipientId": "AVu5QHc9C727s3wXJACZyvFRc9XJSiHkHs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ee316c58495cc4729ad60d451d129d7a4f98574e764f8cf5bda95050e1a8c79a02204ba8d962c6128d528dfeebe64156e7ea6aed5b4c98f468265d5886066c350711", - "id": "81e9ca6c942e14621b6c0de8ca5deb8200114cc8dff364ad52e541663095090e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25052610401, - "fee": 0, - "recipientId": "ANmAMut1Egf7zK4DBHbaki6tv7dzgsEzBt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205f1aa6c3bc84dc585915658e7e03bdca6b05307a86df928cac71cb523be7510b02206cdad96a72947eab1a24acd55869a8888c12252583e3319ce11ad2de65f64857", - "id": "ea5534e532dfed08d41e0c0668e5b1d3e1aaf70831ff61c6afc65562580a40bc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25157514204, - "fee": 0, - "recipientId": "AUncJXmGFQDzHE2XWPWEqitaQg5Azw7Rux", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220354217fb3a9f69b30e0c74b86c3474260e276e4553c0dccbaffa5d2eb1101c5702200b9dfcb85de1773d239fa99288959577030eba8f3908a4cf2611d52c5f5e58dd", - "id": "bb609aaa336872e1a90049e64e08b329ba0e9b1b155b39555660f13e900cc5e6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25248444979, - "fee": 0, - "recipientId": "AZ5MTXTuHnMtZsgpZJ2zM9B8gWojkpen4A", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100892b7674c9045e60026206aed6c6d8ca9fea571c02fb63a3d852e26de4d9aa1002200f4c29438eb6a7ec035105771370937732a0e076fb1bae458d42f6b864784d0a", - "id": "9e7cc0e06c0f91878772d1f3b3ef00fa270da3087442baaba6e2ecdfb1e788b3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25763719365, - "fee": 0, - "recipientId": "APxxDqpuPch7PfMZ7611osF5bZYEyx5UMB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dafcc214d6f27318979493288f12f50e61a1398bc4276ff9e559dc2d00a2e61b02206b173725cbca509a7d26ae49ab1b063540c8d75843c887ca1e0139029a26d395", - "id": "b838f9fefc43e5780733198956cb88817b6b57ada2f8b9351ddeb38a1b1b4a13", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25763719365, - "fee": 0, - "recipientId": "AJdxs7TGgcizNuXVMYgd9JaXpuyKe6fF5g", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008402e0f1908c2a7a75474b3262ec6a6b492cb8d63d9ac0fefd5e7935f91cd0bf02202e6225f7b03abe5f580bc1c7d7a2a33070fb139fa22122fa075a70d912fc7749", - "id": "e93052fe6fe08457995307ee8c843d5293f0d62d3a644b4613af01057f7b4182", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25763719365, - "fee": 0, - "recipientId": "ARjehcSFYaDpcuEnZ1iPquKMnq7i3sW8Yd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201b094974dd39f06be93b8fc08eac96a942eba37a5ee4aed9b38b4c4d5e85414502203470bb9167f1a5121ae1909a3336caab615fd947a9ba464ef4cdc0ec5efd6182", - "id": "f9b86470f90ca95e6ec3dba7dcf43a638992d501595dc2b311354b01420267f8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25763719365, - "fee": 0, - "recipientId": "Aar18iMBogeroCZw8S4ELPzeLDuvXoRdH8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f884dc287f4e68750c622c5cbcfa02efd10ef032e95e3dd5163fe0709f331f7002200b8fb3fb1c0a21467618f265d012a2d0094d62b6b95c5a36e77b1129d295033e", - "id": "7dce050a4abc7ae6ada53d03e78ee7e42774ea2dab60a084cc2f2b2a3f77cd4e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 26000000000, - "fee": 0, - "recipientId": "AXbPyMxJTmQ2iN7qoswFQoxAfZWW3wZj6t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c343b8c44441d845cd4a56ace540f15ea2445b00a22a350a70f4694052480f2602203110b618b42594b5d8e148f53cf613b55d2eb83685120e40eb9b8f2ca2ee36ed", - "id": "78688e06570c4b2ee4017450f89cf8531cfe1ec31a5422887e84ba2a6a21427e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 26000000000, - "fee": 0, - "recipientId": "AJbQvAY7LCed3KXVa1qxJiySXMKWRrLZ5X", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022045b6f2b658a709eb8b9ec4f9d15839cfe270ee0be5f96e6550a6395e0f887c2c02201194477e971ebaa8b7b8dc4f281f2cf1157960905618602c060b35ccc29d8c0f", - "id": "1604c3e03314a5a83519482df25366aa40aa3bcb3738d7eeb42c177bd1a38b08", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27559966014, - "fee": 0, - "recipientId": "AXBwNDce45Bw5upoPTup7NUmdhn8NP6D12", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c3d6ccd2349a07f0a94d99292863b4c40fd1fdcb541dd15554131b6f03b5c427022028e5387876964b386a74c01ce79c9435ae71b6690bc65284f6a88b6e313498ad", - "id": "cb5a0d3fd3c4edc2414b8036df50caf75b9b8c25b1bb4b645b368291c8506cda", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27700000000, - "fee": 0, - "recipientId": "ASXb7xv6zz5N3JVKzhvfLrmZHGGVETBvSw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ca228022c4c0932b3b548d1c3262894183c12f65c56981b54365360acd175db4022037711473cdbfec5110a4f4467d2f4f847cf8e1424a70bd31ed391c11440e4cbf", - "id": "1d466dc4a218dce9c63f753870cdd645e994c9af33ae0777596bece6fd95bdb5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27700000000, - "fee": 0, - "recipientId": "AJMMbNCj4gMpfne8rg9QezvdBF9zxPfysh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202ddc599782c761e60e98b40880fe924a162495a916e30b4fb7e119b5a0ed0a8f02207429cbe9b1e529682b78c2e30e8269673845621d809b9b0393b7386e396e0378", - "id": "a0c287269d928a06bad77dc7366afe8579308331b10f2fc67b0c249edd5652ba", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27700000000, - "fee": 0, - "recipientId": "ATj3133FZRJqKK49nScR4yZ87S6BAWdtZm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220690493de80d6ae99cf3108b18cbb9ba0b18d9c283f65d9dcb8158df40623e6df0220376c3e63badab403ed5bfbb298622a9449b8f40aa93f797b4678fb03a0615570", - "id": "7ea9ac0687d6c936dc61a9c6a8c131412cb973869a7073b7a679508d91243917", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27700000000, - "fee": 0, - "recipientId": "APunbT9dgTkTpqpUAqxHHW1GvQ6pNzaTHL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc128ca47b50617210c4efe3991eea82f3cfeeafce7e3ee464acc43c6962fb4b0220434e20c12c72b9e6f1cca4b0f79f5f8023cf054bddbc4e654cd195bed26ad923", - "id": "7fd55eb9e7a1576beb9060840cc704c3db6c2022c904d6ed4f6fd42bd2b94b96", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27989653191, - "fee": 0, - "recipientId": "APTFQ7bZpw8n13J5v5sbgZ96YpgqbdYsNj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022045ff469954bab872b82b892d9cc5422ab3284fbcb4976af1fe34abb3882fb18b02204ec503dd4f4491143fcd84a875b6ec8fa7aca68b2e8f6240cf5cefbc281538be", - "id": "6941d8c3cb4a3690680b6af7656f683e3f4d7dc77a8ffed8ca64af9ec501134c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 28721448453, - "fee": 0, - "recipientId": "APjsYt53nwje4wv8sBUJkuJPgHYfkJrvbr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a5fb78077c9bd963201b4b73f26d1f57f9ff804d0e56080d7d5b11365b97218602205fcb7dd7009b4fc091cb21d8fb32c3b20ba8feee9eb95f19038caebaaa1b3de9", - "id": "653f5e251e1fa91014fc5d5fdb5160f1aab6408bb8e62baeaa69733a9d11b847", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 28730083421, - "fee": 0, - "recipientId": "ANPpsAJ5zUvsFSH89FpiuKn3HSE3F8MrvD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203d66184dab85d76f32d99f406814204822417081b933227d247eb99aa5ca69420220470e38f361a771362db029ef353e384f002982dc5a40bcfd846c0db84d413576", - "id": "24df83ccb61485f2694a460efbb8ab77b38d0587adc4e48855ba4c496195a2e5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 29339140141, - "fee": 0, - "recipientId": "AWwHvryaqeQv7SsZVysNaG2xHQD65romAU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022037e16fba790a42b0e56a0e7b353fb740eb499278cc8b9e638209ba06885e27f7022039d8876608f1afc24e7a27400844b4855e6f6b05f7fd2eb45f682760efcd5442", - "id": "54ec066a0475f3d3bb34b431aa58747a246a1e8513c6d295669f0738ad5208e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 29885914464, - "fee": 0, - "recipientId": "AKWZFvNtkFcyp9qCd1qoaA1XHTCo6yumHz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203dc53e2d991bc80bfe7ef58bb5836b1016fac072bb04dd8ea307c675fb5fa86702201e4ca6d94cd78e647c9f1796bfe6855852a1b08d24033e31861cf4769da707e7", - "id": "882cb0fe98bfb862e7c1b0c89fa209cc1611691a92290ead9bd049b38fc69dfb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "ANPjQL9f5qA6hiVgntKsdre4hRnt4kVXqb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220641e29b2546a81aa442e6b13f7c6ca49cb3fd6ea836a3dcff16f9b8ac88b30d602206804d133dd4ab791569c95cb6db82d9e9ae9e8774ebb14be8de6a9ba15308bbf", - "id": "a1e0e700abedc3d7ad878be06f16cd2d565074462d81a731976a57411d1c4d35", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AbGe4yigaf5FgBd791PKMeRkysFeRdNUJ6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ac923911201411e738cc3d738b588fcefc6809d1cabe40042fd145624958f32c02201f3f39bdb47e5ed315cb3d88efd53d7f7582370261ec2956d1eadcfa33e1d560", - "id": "99992456df9d1108f11876975230e1f47bbce3defecb092aefcde7d015863426", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AGWNji5TSq8tXMXK2JftSbUuytzkkUszKX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202ed1f7fadf1c77665beac7333f23ea2afe3968219944d538a894fd5763f7cd320220637dbe52b04f82ca4f340695b9b77e939d6abefbfc10bd928781abdfe0770b59", - "id": "f11dcbbd114f6d70f1b90c5a54bc4dbabebe788ae0aab1473b9464d48686f0c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AZTfaXxi8ZEvtpvZnEbdJZaajFXqCf1B16", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ba7d814fe5391cedaf1eae38c14557e7f7842b0bb618a2043aebf040c10c23dc022002780b3165d68dad87c0dbfe646ca1e11852cc09e8f57443787b3fb4ac636b1a", - "id": "1ac2891f76ab45cce0d5297e9d896deb831ae644968c4b7aacd3c22ac3e2cc80", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AbEXWz7aphvULaWnTs2LMt8PTnuvFM1tcQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e5b9e4859b7541fd99124fae2704d5ccb01d9fd424168d931dd8ce7feae9e55b02205faed9d0716e2abea81cf4748db0189c7cf802706087f0570add7399eb51465d", - "id": "53ef0aaa67a4d02eebff7a550715542501d59849dff9ecd83b1be58956403276", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "ATHcSoKCMZzo4KW8ukygbkeJjQ3Hhdm1oC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cda20fa0f3a07fdce9d0b17acbd67703863c40311ded26bca2411ab6ea2f03b002200fd03ec6c9c62e33aecec9f704b7bc5a409cb727cbe2b43e7a9ff7ac894b7e85", - "id": "6038460b4fb005389c6df2e4e1513468063fe6ef332af5471e7e47390e09f7ba", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AKrAqqmZ4SfjPqd9UqtbMoecpHR9tCgmWh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204d13a7b0c8f6267c5134f40c9c03af1e75eced936834f100607ec8785b58b9fc02205f99a3e0133114f48a42f0eab3e1cf9546b2e71799244e31f12470eef16a5af7", - "id": "4727130dec5138ce4720b7c347fdba334c80d638549992d58e62d8ac6bac5e56", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AJNdz4hjGwRxrt8CJSBzsqzTHgyBUmEkp9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e0218eb720635af56f74b4c3f531ab1146764cf082d9c934978c3cbf52a3c65e022041f23226a14f86a8893cee1ef08bbc8521df699ceb6e3925dee5ed947b06d70c", - "id": "9ae2cae32a3895d6fa5f6b7f6b36b21416a6fe14c4782e386d5ebc0831d9985b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "ANWw5vn73Hjh6pLHztyWjYvMiNxYynkbKn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009a7f5002efea36c366ec5b7afe37b22e12a72d59b2ff43626ecbedeca83a14e802206ea4127d8d15eb80c28905d5047c16128a23ec64e9f25262345de8066e0a31d5", - "id": "3735e0e88737e8ab56057b4383d091ca5f0779177d5aa95da30cf0d8a81aa96f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AcgKpQ3zVCN4St8seZaob41Brk7zpBbyF4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220455306c09b4bcf68b3d503c3399d266ccfcd7355014e1edfb600e17ec67e4141022055e7128f5c2caa7b564a08b0dce9492448aaad0e4cd093b603e61d245b27fc28", - "id": "d4c0238a3d04e69ca8e2ee8ec3b8da3a41679ae8d131a1012c2cb300c4a437b7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AdT9uerGBa6ivvJ75gGJaDYjXyPXfGwJBF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022063f7821d3d906617f9d60474e54259e408ee9f88c724ce5b40455eee8701042202203d0ca800bfc80788ed4888351174f6e1524b7ea63a41f222d770f305f97a35f6", - "id": "aa97fc714c3d8c6b5f5fb76c351b81e4e0fcd263b90597a72e94d55cffc14f2d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AQ96VLizRnB1RtTb4voazDaMLB3VYKhoiW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203c248de6577db8045e71f536de61b935a3c6a2dfbf9ab80f92d810a31fa34b9f02202484f612bc171400b2b0471b6f5820be3ba8291031740e2cde5547948bd25f65", - "id": "371d63607d0af6c067a50dfeb58e56737e2636f4b0b0ff98aecaa44507a73f16", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AZEYQFFZ31ybrZ7gcnt7YtJb11o72HcVCE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202cad95342229afd4cff162e4f6166020be810f803ae87247e609ac6b476be6650220643568fb6eab880dcef52e35a6ae597da18d849ab7f77e4847216817dbdf2066", - "id": "8d4bcd6bbe8c0d21408c5c956e963c7edbe906e8d51618d15946356ed05f84d3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AQrgHm3rfWGVXDhoPBSz4vXRYSJnezg5VE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204abad6d12a702ad5bfe5d86738bb12e5fb77ccd3dc963f385f9fbc60369406f602207e473b2bbcd7176b9d1b8d3ac9429b651c8afa5e4f1394d97114923017734b2f", - "id": "c16763bfed71cf294553f0f3d6b3b0b3ca6d133d73d09c4b918b6e5cee11a895", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "ANBMsW2NRR5QUp8yhQNJc8BW6yrL7iRW8W", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220344f021c3a169186dd6ae0cdb48330b6224da92758de49503fff882d1b71b2c7022048f81b28b9a8ff3c07a1879fb2b3f0f31489f9409061df4cab21bc8403053f34", - "id": "3a359c2cc190a82229e1366b45d4c882bca83958d84063bbdf513d69f8da9bfd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AWHBi3iRAFasvWqRPrhBHYXvayzn6VNans", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210095a887589f959790c4c01da0adca893b9f288a8fc6ef32de25d19a4501f2fa770220267683c7e421d85c5d368c0416c0f067110ad2b39085a57b396300df68446827", - "id": "3deed8497e891cd9c8c52cb779542e4e0c14bc1434418558e20cb2d2e7a26f4b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AKNx6tMWjp3wkCVmMKc6Lf7evoVQaVW2RR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008a303a51a8eac6a3b47ac696cd9d561133dd3b08392060a20baced5d3d7659e5022032c11371eb5e80d42325caa9933e9cb218c03b17584bc20c8aae6045687c1a45", - "id": "8fe4b99d4943a9107010ec38e159f8208e1242087efa376d7345cfc8798fcf57", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AX9teDU5aZqYBJUiGXFmkxFoVARFHpSZv1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009c77ca7fd50bbbe8244d6cf87d31fee1502d9cd53bc2719b4977b163601656cc022049cc61fe0848c5cc67bcd401339da0edf6490d59766f1eae6acf071f59d11bff", - "id": "960f6fd34b2aee0ec704559f85cb7889acfc8c5a3b635593d4660a5c4e84077e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AbB54oR2UpH7Jh9beZbHAMU7i5FAZVqEPL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205ac4b25ffcee117effac747f983502385778170bf4d9397aa36f0b68b660817502204d73b9266257237d35a1de1afed230c6512c7edb749e92cf978679aa9525e131", - "id": "a841cd49a92d5ecb39a5439a958d9d1d02622188a73b71800b45631d15ed14b9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AdDxYew7tm1TmxC6ASHJhzS1qgtoVKLEPc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa7ad99b7c0c61a861d253948568f93ed985cd832ccc49bd68b5fff69d7d0a3202205d3b4717783ed8db230108f8802644ca9b63e5edc60652d743e3792574cd0a18", - "id": "1eae0aa7ebb5ec9e7e421e704ead21748c4d211d899006b8e0c4df6c0dd54018", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AXsSBj89cjVAu5Sc9gLiDDwoRhASaVPWpA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201a00e76784ec8a63958aec45ea6677c6af0bed2c0fc698ddf5a62911459af0ea02200efae75686a8e3615b62fcb698148229837f5ff32c2cbc2d289447124bb2b5bd", - "id": "193e570b80302b146e5c034c941e1817f34979f322c1c8fe47bef69c8b129b94", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AKUncjgDtuG8pJk7YQrFkwBxujN8SLp1By", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203f9a8eb03c96849a9327c877eae8bc07d3c8e469ef3ecb7bc92f3b10ff76e77d02206b2d2396c5f18be1a68689649acc583c7a827eddf3064d16ebb095a3cb6d4be6", - "id": "4aea1fd822d1a22ae2edefbf779ca2ffb6f8b49a0971e2f43addf9607933d513", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AVbcaqopSmsUjmXDvW288cGUTYq9YSmWRR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c26e6789b1f61fc93d15a669de8c5a8dd420f68fb8e2424bb5c40c4a15fd1abd022007007a2055610948879608dbfeb831ffdac2535e26614453bd029b93820c648c", - "id": "de983ec6b437575acf2c16bb9da752f6a901ece688be7bad75e24591baab23c1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "ANhuDMbZ8GPX6MNLNrn1wC13yKTKL4PjL3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022057f327c74d0539fb43790a2315867fba5f2e8a3f1faa261852c146bcd26e570302206d543cb594a980b408f5bd8fa2c45251e7d4eb005b2e55950cebf36c92c4438a", - "id": "a869df9fc61b6e1693527a3981d465f6f46d96a318a09d9a92c9888145e88a7d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "ALg7ozgLSN9wLUGu94LwMhPhFzyWBxQobR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a609672480799e9034457b36d6c8f78c1cb253af8a055ac82117e30a3582f96f02207616bc3a6de1cca1762a3dac65dd48022da00e6800c39b44ae53e0d26a322fd1", - "id": "7f7b0295b52a45c399379f18ee56ab09638718fb9346c5bc1963d11cef412afc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AcF5LeL5ooyt1wja5Mj52gU8X92TVtG318", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022019f8c0dc34957e29ad1e55901f0ab865b38a7e0e018ed69b8793253a5d207e8002205901d7f3d081664da8469ed8e6bd59b3041ac1a8455f629f5508b50dfc2f62ca", - "id": "dae4406da93d88d9398fb59cdb154503104687b2e3a0754b169d17807a634900", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AZ3qjcSmk3vacEiV3GdcK3x18hAut72yAE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dc74c59b710ef47a0568517dfe242dd8df2bd1305155600e7b0d39113b34a9e602207abe3ef33953e3aee86dd4f1c8909edf6ccbb2a3eb181a0221665dc07e1e0023", - "id": "0d3cb578f886acc03177c1708528692590db30c8ca270b5032b5fcd86ed75033", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AYdMCtcQjbEAUKWLb7f5NEkkBx9SmTJzcU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100af1a06c135f43cd0c67c499d1ce310170ead6e61b87f5d673a3d1eee424ae1cb022065b02e0dba32c5f235d9a707cf279a8178d5e5b0baa34572327c9dab785500aa", - "id": "ca2382312af236ea7f5ecec7f860db5ca528b71a4030736eb824db55ea976167", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30000000000, - "fee": 0, - "recipientId": "AGRJC9bwD6npvCuYBZywtho8dBD3RAzikr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d060d1ae9b22357dd0fb6062abc88eb2ae7e47eebb7af7bc21440495acd3803202207e33d96cfb29e5cc40e45b53eca85b5891ee1b4122ef041e579df50464b8b053", - "id": "40a6b024984183f0809d08d199fb2d7c1742995adea05f1fe51a2fdbbd969842", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30310258076, - "fee": 0, - "recipientId": "AU2s8RgEdGWJYXjSSh8M5zkoJjdguh41Tk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be9cd07b90814b2f4cb2a868f08dea6eddc297e79ad7d48c3a1230d078454edb02200171f1277e17ab3978a16fa260903bcad9e66b8667e632cc856d09ea9359a898", - "id": "8d3fe39309daed03ed0e02d4b1808d54a18b71108ad3316898a9109981f0b840", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30644619264, - "fee": 0, - "recipientId": "AQFEMBFnxk8j3ShwNErwoqSK977DidXAAK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100929d3ed3bf40d9aab26360d553f1016f7fad051af511b4111b45ba22571d2b4a02204600751a7a3dd8d0b9e5ae93b6c014facf6191b52a64963a893ce4ad6d74cb36", - "id": "49b13cd9ea10a35ee422e6cca7044ca7a4e4201f397cbb8fd8627d1ef641ca5c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30990774050, - "fee": 0, - "recipientId": "ASVZwCuU1a7qgUKSdiZzRkuRHJpLp9Fp4F", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c352876accf4af2e7395b682bb88e69b896166e3ba25ecb62de62dec09891c5102204bf978db3488674cedc28d875a218309195f3cf7fd2ff1e35b28ed9dadd60995", - "id": "dba6677347d67b9a1030a3f883d978a72c40870414b9dd9a6fafd888195d96cb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31371117110, - "fee": 0, - "recipientId": "AXnjrqJujCyYt9rMC5E6NmnbuoN8N8LtR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205a2ed6c565adbcea14620c4872181c55dd5df09cfdb0ea96242a6a8a16fd446e022019cd448d1b41aa454ac2cfc476eb4d8d95b3283489fed359d7228d87f97f4192", - "id": "f03293bcc0c837c7d0741081cd123574c51a55f7e58f430aab867047ed0ddf0a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31808712368, - "fee": 0, - "recipientId": "AWecYiRcWfVFEqdU9Ph6j7F7YwZp2kuowr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220126963651521701323eb3bc6d78d7bf598d7b12896138f91d0daaaa0cb2a37d4022027009cf91eb481191fb7058204489db792fe49cc2925f2ded59e06f7740f2cfa", - "id": "4b77e88f7746845ee2b045357fb2f1cdc9d23608c50a5587c6414b3a0c4df8a7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31825770981, - "fee": 0, - "recipientId": "APATpfYkUfDEhUnoq2icXy7ECqSumPRK4C", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d20fa45969cdf4217de7c8977675794023ceb702d0986785fc7d14200a836c2202201db190f80f13322618d3d5c27d07b36f78c0a876428550ba1fe3cc6e5a2be8dd", - "id": "d6dd825e9819c4bfa896bb33b25ccd26c0d6fdf07dd60567c8048b57a6046cb0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32300000000, - "fee": 0, - "recipientId": "AWcBdMSL6DFzxf4zcbouKnXqT3xnsaxi4c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a69a449089437fb0878f351b9abb5770b9e84041516b1fa92deb06dc648627a302200b30af92639075735109ddd39d1247a57e9ee7cccba8760f0e18da868f264d84", - "id": "b3971af4cbd2bf84c9766dfd4124993189e2e445bf5c0dff235aa49f4942a2fb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32490327633, - "fee": 0, - "recipientId": "AWq1wsJLPzAY4uiVTvH14F14YNZ2PozaUQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f05a79d7bcd98a58c56630a91eaa25f14e80fd07bc9c70277ded30bf32dcdcf9022067761b7133837da252a7f12c54a5e59596a5bae74292751f4f71722d1d0aa3d6", - "id": "bb073805ed067d4e2efb8882453433f52d0fd6f44235299416f012d73688ec4f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32583527433, - "fee": 0, - "recipientId": "APruVpipmehKJWeienqh2jbAdVC9o3x2Yn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c421a27e857b78d81cfd6d12a5b5feb0fea128bfc43a7dd874c014040c91a29f02201df13abe3451a8039bef22d2e801cc09bc6f4d42f85bf4e62caaeb34131a5e7d", - "id": "439c60dc29d33a9d2e800a5b4ba1c1d195f580fbc46c4183cd4dfe1eaaf9dd27", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32900000000, - "fee": 0, - "recipientId": "ANiKA7nwrdSW432oB4Xufeb4MyyTk9QeLb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207a79a82616ea4c83b9bbfd52f8a53ea0ee6ea8bcbbad23a0aae1b74e5eb133390220179678e1b59b8e435fd19e68541d890136f85656f31ad1513169df3b947ea481", - "id": "e645add44af4985a0c44d63a7d33d6bbc87d8d0ded51a9074d8796a2af61a720", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32900000000, - "fee": 0, - "recipientId": "AQvTNyqxVEHLVZfpVFttfMankBSGLyAfWh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cfc612e6c035e4ba731bc662abd91c151c0256379c975b2b0b301e4990f99c36022028380c0fcec416d7cae09ff6ad936087ecb43f162e7edb98b6773891003a494e", - "id": "d17aada75d6a2e7566d34ea4d19a4f044e7ec8a77954de872911f5e603d0ce2b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32900000000, - "fee": 0, - "recipientId": "AYnzEccw2MrcTUT4yXRp7sHQGe6G7x7UkD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc272d36bad69aef950ac0551b96dd488bc9a83cb4025fac78c57ca286d42a30022003e275f6f1f923c61d6c3a45141274ca2b32f396d9f15a37ecdf8e6299df9254", - "id": "685f94850350f66b039fd33e550f0c2d1114c9f2fd7b7f0f723f9a8dae08f4be", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 33114472223, - "fee": 0, - "recipientId": "AVzMmzLTmugSxbwqWPqxxSQA4QtXVcz5Le", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220699428272cbe81ce84c5556bdb47e05285e8f2a533c5e1f0fafdfb2ff47fe92802204b0c4c4ae35dedca243f3a322e82bba73d80802f01d8d584ce5abb75154db06a", - "id": "1a1efccbdf78b25765e859d14e2d623afb1fc2acffc1fcd8d2679c0aa1fb697a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 33341283885, - "fee": 0, - "recipientId": "AY5Q9bdBx2NnXsQ4L6Zw5XShNR5gL6ExPu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220398e0ab17a01dee9508b61e2a42fdccd6f0e26ad76168dd5f78b3bf8ed5d0a5502202fe39e30d85935a83060a11be934999874065d837f18e65ce96041b502a4388b", - "id": "eae73dbe65e5699583a3db3933114222b264c45ef69c6ad26dfa517fbb028665", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 33341283885, - "fee": 0, - "recipientId": "Ac2vcL7D9SLoQKwm5K8G6bbFQWgAXNbN9o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206826a73682fbfebf2b6e78341e339ca1265bbdc3a2a70ea36edecb3d50adb2d9022064a2a77effebcea2f7e8fa6de6249b1e205e518e0efeca9000826f5a493d46ed", - "id": "16158b459a0c1b2953d98feb4d5b43df41ded0f72f3d8f7b159d47f5fddb1c2e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 33341283885, - "fee": 0, - "recipientId": "AYNtaJsLCbs88Dqv2Cc9SUEhRn3gMi6y7B", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009c00f35dfbde9a28230439c618354b5bb60defa091c8795c455f8a3519dff48c02204dbaa156e94921647d58d271e1e94cbff2fbca92263f225b46fbafbd65aa67b5", - "id": "7c0a5ab8ab2aa880dc4688437645dc9d825e030fdac4df2a25d5a8e97f4b5c70", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 33412512992, - "fee": 0, - "recipientId": "ALa8NaNCRdYYfS5sx81qzvoFEVsA7H4VQw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ed45f09cf19eb0819dce65b619923909e116fe75588a96d6d2e792857a1a887e022050ce6e62dd65fa58e4bae5d09c191290c669eaecbca5dc38813d1b043f8f565c", - "id": "bab26302a9ea1d99d9ae0a90ed17f44c66f1174dc8ff11f7e7b79ed7b2b49492", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34492604732, - "fee": 0, - "recipientId": "AUJRMxuAJjUFEbS7sed3otS7K4R89caZx1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009e20c799169e14bee9d76da2ca433b8d98295f8593bcedbced345a3ce189b4e602205fef5c8b8225b20744c385f1bd2994a0a824849e2b65592fd5246d625cb6a28c", - "id": "6cb0b2faa48c09b48dfa6dd0391d3309be705c4704d55f72d8a39347da0cc6cf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AHWoDbD86gdtvHfgRyKtxo7jHS92hLAnPL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022043bac64f1f5a5b986fae5cf88d182ca4698d667a0989fb2b89e731282487021f02206b8d22d28a13bce3d61e513c6aa2ab9ed8aba0370ea38305c86df90796cca026", - "id": "fdb9291d29c3ba236ff008224764227942056a4d28f9c12c7ca7e22b4c4c9f56", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AbJv6ehN7bETURRPJtTgJ5LfJcmjnVCjjY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e01a874648848f53e3c58f382b04c4d8372dfe14d5e715a0aafa483c4c1593e0022024d196708b95991b30f9a735aa5bdc951ce80455fd30b32265fb1ae8e20c4376", - "id": "f10183e8291eec17c2308b2731b25e0378a2753ac11024c6b967c04138491900", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AMGy7FcNBntN6FY9ouWS62dEmiNW5SNDSs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc4ceeb3be6f423d167c08e8609e5224a43fe2692675c4cccb7ef35d9b5e286502205b9639e02cabb93e9d1262b8291022e87c8fb26e104ad46f38ba1ee4eecd8b16", - "id": "e72f27cd791fe40da52aa33d4534c6cb4061080e57a1f00ac5c0ca2e0f8e0adb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "ATgMvSP8FjG7mtDN84NKvxCJkHP63S2EMp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206556edbb04478bf8492500e284ba73afa570a1ede51add1d0798e70f349e728802202846f2751bc43dd0c0541327e1f20e5e97149b2bb29ebf75d2c17890fdc751d4", - "id": "21d1a6e3205127c1c2008279631cde827bf82ef542a9706f255f0a52b0aea1f3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AHs1p6BVoscsszE917Zhvd9BPWrQbaUqcq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a3e2779d7b46931554138df6b0cef64b7f087541eff83c07358e497e136ea23b02200477eb4b805e61433a164c1d0dac248aa2301bb20f774477bcb81c455b5ea709", - "id": "6e804117d3933b6731b936984bcd33a42319d096d5ee8d5784e1686658c72af9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AHqN9DLFs6S26HcG3zNL93VfofkFSoMXQL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022058b2c046dc42102008402d2aa1cedd8417f39663048e943227682186c8a5861b022062914b3c529429ed2e0612cf2dbf0e66baebd5ce3ee70679ec723862d8bbbed4", - "id": "d0d692c0b8ea05ba6adf6848c94c642a824840fe2b541e16c7fdc0acb8ab280d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AaAewYY6LEDiXVmMcsoZTbkvq2X1DQKoCF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd56b02b4933f9ad0f998c48d4305fc64c047d80fde18883c4c38561d787023702207f6c412361e281154d0868530a6bbe3261040d367087c401b9ad8a7915ff6a17", - "id": "72b77c936dfc9f4f38a825d79ae0360a351753a8aa7e89286edacece4ccd59fb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "Ac5rmbW5oQnjn51KeQeYPi8k8cB34kZFUY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210084307422d072f2c09c3ba15404fe9bcd44932e92eb2ed608c60d707b960b69700220186c1250f67c8805c39661d121b8051aa983e0962cb34e046372641830dc7dea", - "id": "7fe1f32c8cb0217b9ef57edc5cf687a7fb07c1b1b82a6e9d6422e1ceec2643ae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AR4BUMG1TKCcZi7rWMaNQ6HQkp6wikmknF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f32acf1ce8f09eeef89d9b42c32b76dfc95da77375d96d22eabbaf837a318fbc0220059b6de004ce6fa0a2ce3f41ed053cd0d8f735888707bcb6f5163e6c05e762e3", - "id": "28148ffe3c46ac548cf5ca10fd40575acafc8cbd43f01e97cde9b5d385974b29", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AZDhZu4Bxs1Y4SgLc24PansSLP6gXZ29to", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f4963f319382de0ba03c3c0856b9a667a00c82a90de308f00581299aa649b30d02205f7023de13f3958320606b1db434cf40c5294759c01a21691070c90bc2b02bcd", - "id": "d7b7d4815d3617e96fc2f47bbd7424a8b841e2dd5acfa42fd96ac52cd91d2e90", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AVG7PZt7mWxsbHEuJHF8xjqDLCxhPXsH6V", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a6698eead4fc3e21e659f6705356bf1c1cbf11865a735ea83e60eac5488ce1c8022002c23b6bfe049f8a0e9dae2f7eddc50558b51898459bcb2310b4376ef29f1a21", - "id": "e0d87972b7727a514955e01278aa1803bd33f18170554c410d0c851f7c0e7340", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AWiJZUDjG9KKM5QnKhz1Gnb1C3WQB23Xyc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220563392e63a4df3c65909808b2a4b69371e082af2e6f0a9d2c3fe8131b87d514902201657b05f6789b16a5cc4f83dacfd0479f222bbfe17ac97374f19238259566c81", - "id": "34aec54141d983219b001b20901745720f3c38d32cfc736b82697e11bd0848aa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AXDoHhX3gwTapS24HEU72dgjsc8TFEre1w", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204d7f9490bb14995339dd36f4f36a34fcf37726f1ff6a821731a2e788e62c9e1f02204804a2d78f61b94ca546c21285de091fcb3779c2af7aeb7e62c483556c221849", - "id": "152802a6a0822b0d9082c0bfd4109dce3dde9efb491cbf09d76e071911f62083", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AQ1n5gCzkyT9DfgJVBEMdApdKsBhXS2ZjE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206b5c708a8413bb94024010c291633528f6dcf3f86d06112312239bed0e488b5d022068113fcfc5dc3f522005841ea7972f92c75f57ac19f58fd186371d7548ed8ce0", - "id": "b0a303fff3f8d312ee52989778d5473173c0010bfffecf773d89e3db1a3c316c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AdJ7BZneub7qzyB8qPHg1FS3ViisAfXP55", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e09b911019131814eff411b87aec770b43f90b15dee330ef901ae18f57178f90220381febfa6e3cb1e869ca682c3efce6c5c07f5002a234475a1724a7a3fbc6788b", - "id": "05e2795800c06da22617f5ecb702c5c178c610a9c1ccc8dfc8a5310f80c169c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AXMgtmxcMTMTZkMWJJyZgFNuaP4jco9RZL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100db75661cda0f02805787729e123ea7fd914448e56b5de0598c187d193a1fdc3002202823a45c4a674e70468185ca667f45fd2769c4a763c915e4e4c0975a672950a7", - "id": "e7f397170f9ec10b9e1605974c37d1ab68794956b6687c03e96ecae5057d12e0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AQFYCZf3XpzZyjo4BtAQ7PzD72Xhp7y7vb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d3cc40176b487dafeffa3c068dec44b0e94e079d015811c0866e3eb96a77a2d6022051d678f7e7410d3b27a9c86e2829d31277462b37381eca23febd1d195b21cd1f", - "id": "56806a7b8d18fff61d209e977234bbafe94329804f06ce5dacb626c91e5efb7f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AZqUtuBczriacfQkM1iWaiobSGX4GN8KwP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009897cfdf79fd8c69a0508292f557dd46b4ee751be8e158d0bce8338c73fd86b2022044dc1e89fc5eaa374c0703862cddec760a8f692718768b221ca561116af6eccd", - "id": "c9e886dbe8d9344cde99eceec3f927ad6f8ac08525b18daa8d2a72ddc2a0bddb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34600000000, - "fee": 0, - "recipientId": "AW8RbZZiYe8FbDGNS77MGWEE16Zu8AxxLz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207bc3c68c391fe49ed8c7f06d35db75a739ab9c8d97cefd2e108f7b9bc5f164bb022040ffe76e6f3bcc39f993df7405860880ece802b28e56336a17b40a91701587ce", - "id": "53f0027d4810bd37571d6a25fe9a212890210ec4ee42f700394ccd8a1672c8a2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34856796789, - "fee": 0, - "recipientId": "AXk9foYqX2ExUBK9Rtdirj2XTZ2vReCmox", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049aa0eb602199bde5b2163a99fb098af4687a8ce249923d7007f5155a4219764022076e659cb2877771720de2d695b7df6c981221010217e885f42fa7d0c09d329cd", - "id": "fc2c5586c3e6aa5bc6d08272c06caebce5592b9f9bea2657707c3ced4c008aaa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34856796789, - "fee": 0, - "recipientId": "AYJyrRtDNZPw3srKy34buKUFJ8vwCxAuzc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220125967fe3764625cc68030e8111c0499a0c5102195f94aa38499802de03f267a02206a97bb532d9da550410499a3952ab4a6e01939ab247c17227792e67fc25fba03", - "id": "b4cfd3f8d50c8bec11bf73e8bda2e8c78de932ee460911e0b8ff69c49ed03a08", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 36400000000, - "fee": 0, - "recipientId": "AJtBjGqiABsp5bUduT6YaK9L9E4EVZXhjH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210087a0a6cfd2e046140baf9899ee9734ebfdfeb5fa86da06d6b354a7035ae3bfd602207de557dfade43b2b1f79b57c19133df0c0c0f6fe5b72f6b34fd0a5b5de987a8f", - "id": "379d4537f2de2f2cd6d9dcdad6c922ff45d538571712ceb2eca4ebfb676d06e7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 36400000000, - "fee": 0, - "recipientId": "ASE9D8yvqKef22qP7gAqKyxYqvNymPPm9a", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dbdf2e7a976583aa29d4bf6e0d46f3919aa5bf281af7d2a655db076cd73aa1a702203d2a6c322152f63bcfa2db3f535f40b397bdccbd69a13557430bad54c3410cdf", - "id": "0fcf2fa0265d5c4bbdb6a0a0bde602f3342fa59c479af9c761e8032876ca7a34", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 37488839577, - "fee": 0, - "recipientId": "AT2FkCcRYAttXPz47FGpdgURLmza1cDctK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207cc3222d47958a7db0ccf22b6a82724ab14774498c3015c1efe72a4eaa81f0c002204c6d395c17568ee5f5831170da2ed918f5bdb637d2bec9711173b444b09a250b", - "id": "8a905c558371ad7a59243e489fd76d5ca42e522c8a04d3d8908a73445d6ecea9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 37771522137, - "fee": 0, - "recipientId": "ARZQdfzNT9Bd7ym6p7VF4KTjnD1XyoBFdx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205c90fa2f53194d4d662c53420e4ff1408aac499327e46ab09c5bdb4bcb367eaf02200c71e40ed64aaf058061a224a54d21d1da4719816a4ad90a73b2d86a2795d3f6", - "id": "0f8a2e414407bed22c8866f8f3ad446bd932f24089c15c3122677c7f5ddbf4af", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 38100000000, - "fee": 0, - "recipientId": "AQpo8EbxDh5FA4zATjVRzEFfLcnmUnzMAv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201de54ef50c50d19a8c86aaaccd7ce55790faf0a58031f608f50929b0ea79e8550220533214087b327632aad01474d1abd56f24793bcddd2366f7cc8346cb75b72985", - "id": "079d07f46a09bde98d55c78ecb6c5205e5209ab32d27c4307dc5448552a25bbb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 38645579049, - "fee": 0, - "recipientId": "AarTZVSMDRD4k2YPRRs4vNpARZMEa6MtHL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e437c1b3391179c2c73f309351525aca426a52a050f84cb92463fb37c0f0da65022075660f2e0dfe215911ca518b55954cccd0e184f2d87eb3b7f4500a3e782a7b4b", - "id": "a16879ce73bbfd163115c767c3ffaba4dcf8d1cd2bbdacece77ef15c1a777ab8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 39403335501, - "fee": 0, - "recipientId": "AZdt1o9xiUGNbKtxX4GUjA2Qf8rrepeY8J", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022100c6415f478336f7eb59ada8084bb4656eff9e22a97670afaa4b7aeab9cdd38d40021f62e0546fdb8b200a526c79c67c1240fbd528f6cb332804713109b921b4a458", - "id": "6da4c777b0511056f1d5420621dd8b70b8ca6ab5fdc1a6df342938513e01b2c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 39470224178, - "fee": 0, - "recipientId": "AV7fCGPQZi7tcKaKYDKf9yGJRiaTpicRTK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e0bbe7320e5972688505a4793e0e9cab39914b53a9ec5fba0ea6e179cd8f94340220737cb6e960f6c2af403f8a80b7a553b6baab74e517f3bf9de245621b1ca9cfac", - "id": "bd2280fa9b6d30ec1530bd9e5601de25f3dd3e0344472a2b4cc7c32818e8037d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 39620933325, - "fee": 0, - "recipientId": "APLS8VURVhTsQ8gYRh69cNbUCETjL89jik", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220548e77cba610645e3b5caa5fe0de930be30662ae976b837252b3fde06bd2f93202201bc93d5dd1710ed48d95cae56f0842df1acbf6da100d4d4d4a56a43b84a504df", - "id": "6bda585f91fe13005e7c2ae9e317e55486f3e6cd0d83d0a64636c409ee8962b3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 39676127823, - "fee": 0, - "recipientId": "APfd2W7pvN7889NjkxygPYuaeAg1XNvKSo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f99b2ab861887bd456fe83981eb9bf17c655bd9fb3db399c57735001b8913c6102206166bff4926c83511798121ddbbe68e37ce8ef14baf47009c55385110bd470e0", - "id": "46aee861d13cbfa1e058f518e14f1accab43bc628b5aa1e5fcc5f28b38219a03", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 39676127823, - "fee": 0, - "recipientId": "AaLnoAq8B6ZQ9gWMNSc6EHa1PKKkNrPVFV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b9dd2cd37c9f1838e81325c4ec8d738c9554cd658df1588bdfd568d4ef92e1a9022026e3e132290b7c040220c7a6acd09aa98f4f10cc203f632fe8beadb4f9e1b036", - "id": "d40b29ae4603902272be5e09112e1e5291d49ef8a05ac9e756974c92ae50a677", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 40208679058, - "fee": 0, - "recipientId": "AXVNkc31rXQchNorSwfpweu54jjPCuJkWu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022078da406531b60613184867d8ab424d2de871bba0651bdfeb0fd90f57c50db48b02205ce247f5c318340534a0ab4abcf37102c09072085b78649b7dbfdc166e79151a", - "id": "d3c4f8396ff1a4ee221b03282321bb8e45ce23efb00d61b8e1f9c76039d2c744", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "Ab2j8GC1y4QtgT7hYv7pA2g1tiiC4W2Hmb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fab3f4e4d7b764645b4dc54ac55b3856fe95e32f5be4e1bcfa105823a9cb5ed20220053d7c446db556eba9cf85d8e6229360a28dc0cbf46d894481e29269e96ad74e", - "id": "cc6a17753f16a9bc0bfd922b5c39e54e2e8bd6c3daf60f0cb84752af997bcea0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "Abbn712o4ZVx9WVhC3vy36KuW8nRQrKR7i", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022027d1dbc4e47f15a2acbf82f28391d94205ce0ca67fc6bcd88aa0661afb4cae550220717bc9f5525ea5f38d8a17ec4b265b56a58f0caa523071e584d5bc1505b9f525", - "id": "79fc75d4fef939b3967e65fac8103cbdb9dd6b7e60c08323d735611d4f4a1db4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "ANMGxQS3nccdwd4E9VCkUPx7kqCoNgTPBv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203360d6ca61b6a128e399878bcff373b8ffbec4812df391d118eb972f8fa51cd8022057eadf2624d266fe0a054b103302832345189074c05502f9d9efd4969fb6678a", - "id": "000e2a9426529b65db56224bb9ada512a48395bdc14d22eefcc64ea1342b8e45", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "AWPKFKMqjSFjMzzimsKMgQhyzJF9JBbu6s", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c8f1def0dd762083c23c58977202e0fb138ac22f987ecfc71f915425e3492827022054d427bbadc47b5aba27f3856cb00960a9c07ede09e438ab29928e3ed0096e26", - "id": "617a1168653fd2b8c17ca88d0186949cb8a155ffce2a20727b9940254b82f2e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "AcQR4ymd1ezhUe82AuXZdWTooroxdt1cWF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f92c3391e8c66ee21f1f7a12b056e04056603ab6364055295e5bb9eca0862f5022058b6be51c4ae062433934609bf3d0b3f7c8e5aac5af9d72c0ab375abc89a6167", - "id": "948c73321a84c807cd7381d595361e64dfef9b87e957da57ff809f666b76f0e6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "AQ4Bp3zg3oCoS1Kb2grC3DMVtkRCzzgsgz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022057a78d857515ab4028b94cf3bc3cc0bf378213f226fd1160d18c17f298dcd5830220574d2d14ec7972b165c138bbca3f01dd51b34499d2ea5516381572ae3a08ac84", - "id": "4b1cbd8fbd593f29f8fa05b2aaa44c0e0130748b17f09b0c43d65d3e643a4c3c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "AHgivfsrR3CX3xQFoCQmui2DAvUZY2CNyZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a76da3d4c56cf815cd0fc51160e7a78408c144bfce412d19ee1cc58665e8e44f0220348632438eb2902e6af28bf0a5c074dad45e9060a44b011eae325fa52a70177e", - "id": "dcb2ea9333efa45f3d66d4a3fc489ddc3853f998a902785da39f4ea2ca1520af", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41600000000, - "fee": 0, - "recipientId": "APcJ3M8vmMQD7gU5ADj2sDgcf1N6FhJ1P8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b530f23d6cfaf93c26ab19c19b06d4424bde9d28d9a26383603706e15a9c2273022064b7a2879828a23310603e34907761104bb7f3a3df81bfce674ff11ae2fbb664", - "id": "69a05ee826cc2ba50c2c034cb980ae47812567b7307814444b72b8e70f98b43e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 42131258728, - "fee": 0, - "recipientId": "AUHWHwFyrqGvBcNXUjvUwM3hadXg8Ao29t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bdc6bbfd224056cce2382d92e4a81821720bff470853dbb0afd886afd149cc4e02204b98db8db62174711cd168c11900262a210fbdcf3a9a6f37095e174b54ef5891", - "id": "31ef234dacbdde03bd42a16ec10113e15d07ec0a7b7be5ba53c0cea80667fec8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 42255389394, - "fee": 0, - "recipientId": "Ab71JZB8rZ8kpYVn85Tm5Ra2MPP9dP9zrg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204adf5271946845e06db2bd764012260c81fe65fd53cc5153d197bfe49ec4c6b6022064637659ab09da617552f820fa155c0c8e927f6da4e64041ce1d1dc982dee5b5", - "id": "160d40dc3bf0931f17ba61708817191e818b5e3342c23de337ae4e90c865a371", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 42416655243, - "fee": 0, - "recipientId": "AQjvy3Lr3AnW7QzeEtDzaL2XvAZGNmA6od", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d073a33310126298b08ecca7abf4f9af03086b76a62975c13e335e77880ce4d02200e928de4e5726b37f282d9e483ddcb7d4fed27c73cfc0261d8bff9f382606768", - "id": "cb63ced56cddfa2952302fe8878c721bf74dae1ccb014309d602caa2b1cd3787", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 42700000000, - "fee": 0, - "recipientId": "AbKA7AXhr2Hz4N3o4uLgxCVw9q445xygRs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022061a00c4b115ae2f72d6c7a66d6bbc6bff9843239b65de88a214036c98bf2ecd9022059f612d1060bea12812fda983962c7909793151b0c33b57ea1c0b87cb979e588", - "id": "7e741a32b14ce1d4364ae4f93ba123042d430dbd76be81b0705a69921a07596e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 42700000000, - "fee": 0, - "recipientId": "AKRPP27m6RocDmDwUeSn9FPMZAdq7XZKeb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204976b03c753c921b6ffcaf08b742e53f3944dd99f7604e9491128c2cd5bb06070220735eec837093c4b48d14dd6d91d02a0433e86ab5156070361245cf7204b7648a", - "id": "5aa56c54f898b59c422a478990d978382aa5ae7168e87afb3a693db7e5c4d897", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43300000000, - "fee": 0, - "recipientId": "AboW3NE4S2atxnDiB5fE4FNq6ewXQX1jNM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200bd507090d4afe13d3cfda6d6a3b8cd84d07f501c1d0b4dcab622d836f6f2baa0220597014fae0b01e2f26bd560804a8d2980df2695a0b277945140c2f7f3641d072", - "id": "d8768e339e6bfdc242cf3c395d3b0f11d4b4796977179cabb06c2ec3a758c301", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43300000000, - "fee": 0, - "recipientId": "AYSgtUb1hE3WTkw7pQ2U1AxYmTXB6hSdAY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220548744d15a0172cad94e4a2da25632d1074c78de9b5ab01299ce319b7e9554eb0220252400f1bce65a39a0060a64a5cb44aeda15fe33e01ca7b3524f2dd41ef517e5", - "id": "af598dc5b1ec4ca254f60e4abfdddda7a400afd42eca30863b7565899fb73396", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43300000000, - "fee": 0, - "recipientId": "AJDZqWaKSug4kPadzqKYDzm4MHGxhhUYiE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210083e24d007dfe080315146299f74d8129fcadabd3feefee65133cb26b4a7205f102202b0378b31f5810c7aeb96d4c1784d1cb098a36df52f8f8e10d16fc41370f7c69", - "id": "1c2e9b68f93e64f431c95d1aa03edfe301d01d5e5061bf303ca3510462f7dec0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43300000000, - "fee": 0, - "recipientId": "ALDJhQh5YLMeT2TNf1viyoqJFDTdVxBpdr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205a6741a4f2d76c61a93c300c5dc9542ffe7822c1d2eaf4d63e7f5dacaf7405d40220675355e51b2da2eebad0e1409be2b674415ed903fbda6257dde37e1851ce4fcc", - "id": "4522f2e824f76a2ca38b8f9bcb589d32394eeeeb44521c27a7d29b410d27ba2b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43300000000, - "fee": 0, - "recipientId": "ANZed5qKqwcaj9Vy1emaYETWUeTVTuZE6q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022002d24d963c7d96285437680fa90647d6380df7fa3f84fc3b26ebe80d6d5b4ab70220292fd00bed98449c72e246a94a5265324c16bb8357d4d5863cbd9bf95d7ce1c8", - "id": "72ceb8e73f321a870319b090a0840e83bd8257d86500ad1c23fd3df4e9f76288", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43768613037, - "fee": 0, - "recipientId": "AVpuvWkhYahCaRt3HP27X42W5sLQp1par8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202bf9aaf14081129fade556087d9c51d31b2ca79f316df943834364dc655e13a502200fee586bf2a8edf195f939b9b850bbec71eea69a04f4bacd592e38dd69c21910", - "id": "4f0a3f9b10798151efb56686d7920dd3447d824d0ff85e805923f6e429f35dbd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44117933985, - "fee": 0, - "recipientId": "ARKK5gYqzp8ZBQu6Lb2XCrhyFN7DW2u71y", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100982ecaaa74b2b64bc24eafeaa49951093212380f9784b4cdba3f39b4c6d80e84022054da5701fc8b6222fd56ce68c1c36f125b588e1b4a18fa2512674497c553d807", - "id": "f75cdbe651257ba7454fae6d967686c399a041698c666c25db6f31641a7dc9e3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44286661880, - "fee": 0, - "recipientId": "AN87Bx6HtTjA6NpajYfTLrqSwKHzx5d5hW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd9e1fad2be125b433ad14c77655b8f461db35bdcd68e2cc776c22c887908e46022013e50085edbe22d2feda04815b035ff06fc6a744822150ef8ea7f67c893f0c66", - "id": "9a01c2222033973b17d6014c970e033280053eedac7c1fa9092b2d104a72caa1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44333481457, - "fee": 0, - "recipientId": "ASbW8T3Xx3bDZQYGincxMhgspZFZNrobJD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009fcdb37789a54a1ca4134fa8a5931cb5ecff33a6928695d68a903d088fb086b502200e7c474cbb7d1f6245c569d3f2fd7468a7993bde91bccd9802c59ce0d2b56ddf", - "id": "48de47db070de621b582c945d2eee43674ce2ec38b48d7fa80294d20e857cdf8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44556079374, - "fee": 0, - "recipientId": "ALBVdpLvZofQPLdHungdurd4iF2vHtoFbS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210098e6b691e6c5c75fe5288d2a5cf0cd0b79694a03e782d9aa982a6df7fe206db202206df4cfb87cc5fe8fc61c5b95c16c947e98671390dbac8b74d5d9acc02fd6513f", - "id": "bb34fd7dc178bbc4ccf41b5ffe7a9b8d8bae117f006006bc710d76f36a83d5f2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44571697840, - "fee": 0, - "recipientId": "AHXaXhX5ybwNZ7VDkJzJMSD81J6m225xNS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ef3571ee051193e032dbee17d7b1baf58ea08de031521582416e7965407babe9022055b390e25f42ac5240921d89210f4a9c7176bb922941cc85f92b9dc7e12a1491", - "id": "6834021b254515836168aaaba7bad2ea5ba943f2f6ebab2ed65873342886e147", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AatvqNHtaZ3bEbegvVZKPoXUebZkNQgJG8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bdd197b4f1265984c8298d2780746f2e9c3bc375a2e316108fde2334d025e06702201c836e38d63e67910c20791d9fc4813de475b83ef954778ed5a309aa7691ba3f", - "id": "f3fba4cd40e1f99acd6f5fddf5d7fea6758053db2c37e0408976a4294068bc92", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AYzwRwPZiXuvP33QBjKC8aGyhtbQJad56t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f87ba4784290c46da18275953842aa98133bc70d7b4e71d8ca12282f6c3aface02200ea514cffdaaed678ee27e9915546bd78735363976da8036347a99353373c981", - "id": "e81faed2cca7cbb2f9d059b2092adee98010bdd0be99cbd5a2fa3dd6cbfa2b50", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AFrMQ99PEVFX2fF6FHiczGKjXwetkGxYCy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210089b572936a6429869c1824ff5c093f32519eda2f184c25e488044564fd4f773d0220300200b1497d89248eb06940f0ad30972666dac7971767e1e3d72290a2017457", - "id": "d50bbb062053072e469d59ac609cfa67f1b42db5bf94f50f00002a89253b09bd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AZ5xjWNo8XA6xCbWvVeR5gWKF8Yh51ccpY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201c7336f68570529c6964cb333047edffb7208a41b20946e3f1e33a6fd8c2808102200df8378a745cfebe85881fe2145d8b6dc54bf002e7232e966f10f852f2ac4e92", - "id": "0e671acd9b32bb08daec418f01e9a2b92533928190485a2444ce5ed10798903c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AUR1TuACuFHerTUB6nkvzfr8LYKw4D1DeA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205f37144ad23e3cb508c39225b2aa5316c64a64c4dbd8d9f683c931f986862e1302202a6891bc8c03f50f697d5229fe5d9e968c731f54ee2661b4d87c4178be3bc2bc", - "id": "1e0c135240fec675f246e042f8d3677621cf69715a0f8ce8c0480c8c7e276d94", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AJhTg2R2MWsrzWfn2VMSa7YgthV9ciSAaz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b8c307b1cd5f04e3190470d66972ab3887286424834389efa4c0f14b29d5dbad022053bacfe7cb19002963ce7999a9b324e7b5beee348b827cdd071514c4f3a94fce", - "id": "625634fef958d669ba75d5feedd43da8930ee4f33cc192ae99803f9643d9d44d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AawK835dZLs9E5C9KXLsFEVmK6re5E2fwp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f97b2eb6908446bebde6634b3b79d43eab3d425f02660216f1c2c753195d6aab02201c428ab520196041a462194e906afe6bb49ef7e4fd97955dd44bf4b2cb3c498d", - "id": "71ed3ebc697935d79efb1b1b0ec2f45a67a0e784c57fbff3965ce8ac73a8cdf3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "ALyaoK69C8NT8yiKJdx2bPnPpggTkuJdFG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f69603b3b32ed21e6b90d70b8053aea3c45c1c2e2c204bd5261246b97f43da2f022040756dae02033d7959f870bd1e05145f017aa7f9e8d8bde5ea9db4458eca7e14", - "id": "a38b475f390abd826db23697d8f3a11229ef4ff5e5d9a5486dfc2475cf9b2594", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AdrWr45gkfS17raTKgACtMwC1oeem29fLn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022058cdcaabd8201113db1e2babddf080db1958c9072b1039fa245c7dd83e92caa7022070ad9408de0f11f8a39cb43d2533a8b6bf079c7237250288e4eb96dedc3806b1", - "id": "f0865d7b5fbb45687410d8abd0e4a491320fcd2803a80edc40d3a300ae0eb05f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "ATLyQaK3RtKHa6eGCPZRkVrKjEMcKM2NNE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022005e5bdc9a2eaf86059bc4d631765da8da416e108e6d2e07f2aae071e14fcf87d0220085c0961f9ca20cf2e40278cb27e3cc0de609d080599d4547905dc37bdbc60ed", - "id": "89fe6d2b213226f45a138ea256496d79ad5144353b0f5bd95315c34324674ce4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AdMty9JNMNJ4eSoUZqwnE1pzRBZQW85Dmr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201aeb9867943ae98594b74f68a44e2194eda83b52a7c01850f2da145af34554e80220615f683129fc5e3b109067e5bbae547d6c80581c2653b7bedd7d986bc19384c4", - "id": "6550d4dff8ecc93472441bde8d4b87b174719d14b9778f9f25e826cb58d57d08", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "APEkkUCURK2SFA1rXq6nrqYyPVW13rsxtq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022056e356a79e482348494a82271fa5c6ea1f1ff3857fc2429d248eb5c4ab859734022000b20a70ff281ae78a4ad63af9646c92e11b23a0108c6d646e0b2680f7a8cb53", - "id": "2640992456466adfef85dc33db1f0ad1bf731209a988ce0c3af7b9e2f0a15e36", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AYiQeJcxPMdGqdvKVHzLQvmAPcgK1JNuwX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022072936a9c44c3c78e1da607c23346ae71a7dd55294a9f1dccc7ca7b995075b96e022022ceeb6db9237674388251c1acce2a1a6eb5665aeb8a42b9772853ab68284e90", - "id": "e288716799ad69535338745567163e9607bd2500e78f2fc1dd90b4deaa64cf7a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AQAeWRKR5mR124KMqCH44BciU6otGmbofj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fb9aabdf5f55bfe910272ad8ce308f03f5db3dafd8c9d2a9007e784e0ba8b12e02203843f7b9e84f7a2a37a23843605dc6e1943b3ec3be0a7330d075024be0338f8c", - "id": "b7a4725339a9517ce667d69b50279c93c53e432ac8029cf1d1990033ce446479", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AYHJ3GQkHzy9f3ndLg3CsEBvqSrAr9WicB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a609c9f810820be67db6b2ad25e582721da81ae29b1fbe95acc1e6ec9eae4f5502206cfd243d311190870177ddced286a50f7df71e3ed12bcf2544ccb61184441675", - "id": "6e68a86ad3f141ce115341c4167a0b8c8039671f7b8d593a9007c299a1742ab8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "ATsDqjHX5hGuURrPfnDNmQ3jpB4q2aMcmK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd13b731e92673d5ae20fbe28c9dafd55b4de120a8b8654b9db323c4ffce89bc02207df49ea18df0e5fd24e72ade2bfc2ea417f53d5a268364eda48a8af850a972fa", - "id": "f6432660a1f9291b952bcc0518abca3f0fc2a48cb553922f94ab82d5127d83de", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AbWMPtepNQdtZKbNFfkbEvBki3gLxgzpWZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203fc4e5dcf19aec5c7cd014ec4aade1a177bcf1e8ba4c1ed8ba6636e6091d62fb02201935e26746309781e360a48577ef5ffcd82d41c6540943448d1bdfe44c804b7a", - "id": "1c67042ad2c0ea1436ffca3f18bdb68e192dad5cc1af9506db9187880bfcff58", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AcA3ke2piEk5tUbLcaaeU25tZckcYaVRo5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220057351f830067dd2f2a6c554307d64f2cb01c4a41b4bcb56d92bd02353f2a00302205562002faae96f1915237f5ce1574cba8bb17f2cbe4cebcc4eb25f21b39018d7", - "id": "8abc18519bc37580a21b2b5d36bfe15eb4d6f63610fca4db2dee20f6a25417b2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 45000000000, - "fee": 0, - "recipientId": "AWMJezaNhsvRxDPMtEqNdYHt6iAMMCRroQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206f1e30ed77982601cd7186278023615f0f9c19984ea459629a7d4ebcc4f31e9402207109ecb76a21e88e3dde1c9b1c996195def546b3e6c5affb44838c2fd6342a0e", - "id": "5977367c87cc269564188bb26cd03226dc5d182f7191ec3dc7948f6f21f71d13", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 46006407173, - "fee": 0, - "recipientId": "AMjF2vBKEdDs292kBAsPB2HpAYzCqwUmEC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b58f5b2879ce4eb45501c18fc605821dc979e3e7efa7f204c85f5ff02b6623b602206f2b4c40f9e7ba54dab669024d3a5a53bbf35ad4bb815c3811a565e6d5325691", - "id": "faa89d3d52bebd59d0abe61d555e187c1b8c98c3446bc566a036a217c6fe5c7a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 46288347832, - "fee": 0, - "recipientId": "ATsdHdM4ZWFUXSmAS1MxvTJn4wEt75fyBu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cdad60bffc4d6b6e137f894becf8b7d67d2084a47df750d40590a133e02cd68d02205c99a8012fb42099ea7f26c45181a47e53a02602093a0fda29681ae77dd9e6e6", - "id": "87c1e1fdb88c4e153d3e3e85a767b0e76a29f7da8a88d299b79451abcead9365", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 46416322043, - "fee": 0, - "recipientId": "AZsbKy8LmWUf44H8XqGkbGA3mVdyoNsPc4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ea15ca497460bb9d9ef34b644d107abf984cb3ca26e6c31b16509d7b9346119002206b732e6eaf0a6d14f43ed5d889726a5998d5a190895d4aeeb355cbc2c48ae2d2", - "id": "8aa8e83047ae4bc31b0775949ec3e9a393888ce39b463d91be26a9e5393fe7fc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 46947045881, - "fee": 0, - "recipientId": "AG7uDVNphx9PerxNvELAPrHREsnDC4zNyN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220697f3cf632d4a4e8a3a424be3e15cf7e9b8af40f484dbd862fa32fd0e7bded8e0220061d59d40d3f63cb496b1b1030cdbfc87a9e06e71c63fb9b05764fac08df8dd0", - "id": "e351f47deb7122873b04a91a6051db3812abc8fbe84c907533e6f68a224ad68f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AGkeeXQM5NjSMk5FwGs4wsVET2TojDJRgh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210092e1610b6e99829aa9a149a92fb3117ea73ee1f0c7c2b7cea099b55769870de6022017ec1216d66d1c8f7546d9c5e2db23b95f033313d2aa8e1e4f9762cc74036dc4", - "id": "4d07eac2ace1a2cd2579dcd8b0de07d6dde9c3145a2d09ab7580f8ded1531def", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AHGo9dHkARtM2E2kqb8oVi9VMGZduFNFGT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b022f6e3c9b494d8c11e12c2014cb078876469c3ce2cf74ce1d2448cb7f813190220012990c9b719f8e80e69108a6e9e0501d7094cf47a0daa1df32f08c2cf29130b", - "id": "143256c98cf7c47331065de6db5e8e328680aa78a35a9b7c4038055a6a416961", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AVZugZBjYSTPC9ik4rB6RknCnjzWPBdP84", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205d010fa7f4e16cdd80da7f18052eb197794173f661abd51d5b8d3a377a88d853022021f3f75f96a21cd8e3c694b1635a286febb72427c1d7983d52d6dc20a79c3576", - "id": "058cd4a632a73218954a3b9dd18a5b21395ea99bffe9504be2c111e7b5df1628", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AVp8x3mBHzs7fpvPfpUyeE8WMPdtX5CK63", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c0b5dd428920c1f5148e1282ff66c13b845990b6bb31f497381dd060e8c6c0d50220756750f9565df70437787347c15c60cb5d71677b21c9ce6a7c8bb5e9b786b4df", - "id": "6a3a8a2adc4c7c5cee98d56b69f581b32dddf7da323eddec1bcff013841bded5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AbgCof5QkxSrpZNJGYsiepZuAEwPxhxT2T", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022063f69a740c7ea719eb624fcb71fee6b497a159b101f0f3b8f06e90791b7f508b0220710ebc92317ab726b2824684d51ae758059efa7c16b7ec78d38d45a4e7d6124e", - "id": "e5458cc7aff8e0bd8e2804a68822a783c48db482295997d53939305186376ea0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AJZtruVwKAPj1rv3ahcF3Z9GTXGcN9oc7K", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204849a42fc02fb6473c4a79a0c2c9ad9facda13ad294495b9b2786b38745b7f3c022070dbe71e4555efe7fa244a3cca10bf07744bbd0ff280b83676a2a2e188f44825", - "id": "3ee0be7406fb23f3ba38c0eb96698b168c043afeaa6fd918949e0ec7c90cc3d6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AJgvboi25qWyEgoZrqqWo39kPGaPVbrbki", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203c936c24a2e3490bfa4743b369ed09f12ed8dccefa55836551ba3c666febb58a0220586eac237dc31060f71bd86a893b78a578b76f2970b8f57ff31c31132b9f1008", - "id": "14de30e7e1bfc74127cca71e59bfc74a03b9b2f714837b4812a7e149cfef20d8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AU5SUAATEwfHjAtQDkGFXVPL8Vh4k6sNtN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049a821648ea42ce2c2d1d02c5c97d7a8964060bdf2e4028954528dd843faa00e02203e800a96beedff49f634ed9da667ff419bc8c9c582bccd0297c31006683056a6", - "id": "f41fed485ef003ccbd228edaec88d8087731e0005616a600dcdf94c67e72f0a6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47400000000, - "fee": 0, - "recipientId": "AT6sBJKYptsn7csAsQSzrKuHeDNv7GkTSv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200b60f4bf416eef8e104234eaeb92ebe80647bd5afa15953cb446fa06da0b169c02204dad39f650f29de7c6867453c4121a868a2e24b12443d5c7722c4b2465443015", - "id": "17a5a7826eb0676195d33e12b63705cf50846a4db052199cc330156a67607c55", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48367953807, - "fee": 0, - "recipientId": "APcpfWW8rMcGe9gmiFmZZ6qfeYBtwneDP8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100879fa90ae6b2d4c759e4ce307f0a1cd87980bce956d431fda9003f00ceffa6a4022054c7765423a5f512d1631d3ffa077d717d943298e5fb8cde08908a0a7c9b8267", - "id": "4a6b80c0f1feff81fd4450558c0da1528879ac2db42362ac7fe3b65f4fa52201", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "ALKpxaTevuYjZYSXvTYav47Nj8jQaWSNqf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201c5a1709172473523d605e4a21c02f3350df6bd9cc3c0951f20e8e2d13151f95022047b2993d4fe88e1e7d079de95f342202c45813aa0f1a6d17289b71022c03d512", - "id": "fc415685bb6fd5876d5ad3c492ce4696004aefe8995874b6b1f1dd0d5571ec52", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AH2ZufhZhdDsHWsY6qBbrdp9AwH1rF1kes", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207d9dba6ca6da8f7e49f6d2654d0a02cf36beaee6da12fe5e5470582e4d9ea28102202de0debfc82f03488b867493316c18949aba7ce6788b8e6b583b26f7d9335792", - "id": "22a3ad155d12e9679a0af1d5e8384417828eec2b9fc36cbae2785464b12b372d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AG2RijifYGo36FXJN2Noxq6hM1JEvX4Xxh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cc3629e0cfb25047f615976b40ebaec5d3048f34eec4739e4dfbff8537122a9d022034f893414c942792e86a25e64494c4fa92e72d2252236a2506d26ec1e6d64d41", - "id": "ce7f905a90772e79f6e7b54ecbff92625073fae404abce4af59244ac361b411a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AZcoh7wHpN3Ex7ijGMrdMRTF77i4sMEgiP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ce4ea4644dce81416d60ad4de4ce33c7c389b6c6cb53f06cc3bd2c5f60a08e7302200dce4ca71921b485fc37eec0326bceae9c7345af73e16036a195d024c97644e5", - "id": "46016076914aa928d354260f80e77a6cee67c75a0fc754eb8c84c0278c8c5562", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AYnAUcC8xE7EXKouH467mTCiubcpbgQj1A", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201697eb4f8560bb074f208bc71be81f1308a275967f9af4931066f61c9f56912602204bd8f4577f71c2f5133295cf6d9f9cc54deb0d8c370fd8e1c100cbc8a192ec62", - "id": "37b6ab528f70a015c24f1d9532cee172e1ac7e24167b199de6afe74bec419b9e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AUYW8cN8SXecVeBLmTZrr4XGPL4MEbGz4o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206a44108ed5a88f05d1de5932518d0c4f473ff9b85952ec7f168b7f013ccea9ac02205c0cdb66e19f9c4766eeae55d554af8e18954f88c008621eeaf4bbaada341df4", - "id": "9b7ce4722d40f509d67a01d676ea46ab007b7c3250e70cf450c0d4152c861356", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AHoMsGebVZJMkwd722t3SNQtz8kxbsK2j3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a6ccd3bc952f8e17fe373625a22692d035ce466241cdb96e5412246c4288bd6602200c55d0122b0e09a7b98df0940538ab3308937637b03640ece423228eaf724964", - "id": "abb9425699d86129a675ebe33f6021ccfe0423fb2ee919030cd44856e29ae0d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "ARRMnpJsXLabK1nWxQqm3R1WKiY9ZyZxtn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008b4c4f04e3234f263978b73e510bff42e7eecbccc61bdddc82c906434bd2665f022061480eeade9e85307b62121241c7dfad53168a851d5a3e141c460bac0c78fb80", - "id": "6c644f623c504fd6449dec1ece024fa5026b86d13896bd6466912ae6f8487011", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AV6ffXNFhte61rxqLoCuWVTs3ufuJ7Z8jW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200db348a71ec10ef1fe1498daf3c292411ce01e1f524cfbdbee6f2a9e86959ef002204bbbbf2e508f461a134a6acc333ab4c817c9e2c791baca36d1f235a30320b11d", - "id": "cdc3bf330b78fc143c82058b0674465da2b2e840270869d05a42c2d3eac7ac7b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "APhVjG9pJj2YpG3A7rh9gApT2oFf1HHdbA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009609b469e7ced5253be188cc3ccf069e155894e715ca8ce9b402c024ae62577a02204b6887fa2ff4881d0e539d66ddd8649783bf4aea3cae68aa5bacb0cdd1e5a106", - "id": "bafdf9d4628983cf25b0f411f0c957ebe6565d01dc043d5f79877d6503749b62", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AVa7ZKfPSMcnZn5b43m9Z17dLYAkKPepSC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fbc0bd9719f34d1e39ab2c3eba2ae9b20434d4ed9550d745eba0a176c45abfae0220261f68db9cdbbd41ab3df574ffa1a7e5d07d94917bf3455f0348f2a8c86f33a4", - "id": "cae63006ef7dbc6d4bbb8c0f8b2f0c1821292a53c3720046d2f3e5aa9b18df74", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "APJyeTaF5E619fNpruHNDppitroDorHt5a", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203a37ab7e59a3bebbcf9b1d55aa629f5eda60d60330c43ba2ba1c23c43783ac7702202e75368241dbcdf204a3d9e415f8fe755fd8e60c8c45454de3104e9212a9a56a", - "id": "22399be7427eedf993b9bda222c461f4daac136d9ae48988f073bd31d34d9c45", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AbWDXPoVRcHmUQ3KW4ZWeuD2mrPYfhodtr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b245491f63892ba07fa18e5c6f0d3dd50c141bf978094934d32aca1efd8c6bbe0220625ad4d01d27ecc58c643353396093c12c9ee5f617e8a7bab48f590b4e0e92d9", - "id": "e7b6647114488172d1c18263e301818829f4ad9185cae8bed3622cfcc2c001d2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AUeBAB7qtntRpSt6GtXZXcVNeVkhuS2RRm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206013142ca9cf760ad5f77a3f946e8cd39a254ba8d4b2424489e01897edd3cabc022050457ebaa39e2b84e773ffee063526ead54194a86281dc21492e3fb0188ece93", - "id": "b9d8c7f12cd758e451a70df541351b3f19dd2f2c5516f7a62629e04227d1d4e8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AMKTHeQDsTHWw3QeSRLAW2kWQnBrfrqKHV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6fae8c2f2fe7da5e0690c8b030e7ce4d7f49ed238d182c0bcd554e3c8daf1660220065e1ec76e1db8fc24fc360e04ed3a04782c04e29a9248597aa0974a67825937", - "id": "afaa4c75e302dfef1e9d36c63929decf4dfd7d8d1e8ec8f980bfb6459767ff76", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AJT7v8APQuYDV3uDJWsAFGBze9XDKpXdNt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022043653b6aacf91675ba1ea798f0ef37e7fb265811ae4a52130959e57d4d90091f02202b046afa7f108d0d1e2a377246e9aa295531e053c359385c1406eae8ca9dc4ab", - "id": "3917c81dff0713611f02f1427a92b63d26058577ec5f16eade5bc5f313f8861a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AGuBeT5rVgCETkMTTAUc94Yzx788X9fiqH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f17d6e2720d978efea176cbc85f9e5c472882df3f6886172c5fc94b8c207b1f5022049fd7c95a425608177ca111b78b6666829d6644a03c324d033de3d003f2a70dc", - "id": "6094c25254da89c6e8d08fb7c97e255448331fd054f3aeb2a7a239f4cab5c43b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 48500000000, - "fee": 0, - "recipientId": "AVvE9v9QyJvBPZK2kgMDwJxDMu1UEYvYRx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022019dbbb234bdb03d4652206e72bf2294f4d5e18112b86e1e52e29d0419369e55f02202be9a176cec5b00ad3cb40900080100b8c3b15a0827ae5132e4004f22f820895", - "id": "06df438d747a1cad820cde398e0198333f4389c12faeba195c0abb7fd4d92350", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 49717819332, - "fee": 0, - "recipientId": "AKFLHqUXHQSV8qchdjbm1jUYPptVatZyQy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100929159e14809ac7f07724a097962ed91909fae24bf045e51c74f5f212e2a4654022053ad7d90be5cd253e8fe175267b45453d5af2738f6478d611869ec5e4b124e46", - "id": "c57dfb4a2801c1ad9868dd2e14eca2115928f013ee8713e068ab670171e66abd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 50011925828, - "fee": 0, - "recipientId": "AYTJNTk88MySw2YRJxb24XQrHr8fYe3SFf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207a665bd64a6becf19aaaa99468984ce4e5e74cdea92ab4ff565d8d202319ba7c02201aad01d9484f0ec40e02fac399570338ff66cb5b8a84748ba1cffa0bef41fd3a", - "id": "89079a73bd4806bcf928c52bec954e11ec3de186a4caf4d1bc8f21a430373c88", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 51012164344, - "fee": 0, - "recipientId": "ASm2ZsSj9W7jmYCDdLi8Eh9RwLQALkH1T9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cecb5a11966ec1d395eb2eead1ebf4d9c41f7a2e65d94bece07d8175fdb20125022062b79536de24c87d93ace4791643d62e2af941cba4aa457b776bc12524cdc0dd", - "id": "199a36b4f6be8326483599431afdc07c424c761826e5dc915103ab677d97112f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 51523164985, - "fee": 0, - "recipientId": "AYXPcxBW4EiZ3hav9w8qPnYCfMC7UiHUFc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205006932929f7e0fcdfa814063682fa9576cd197d2ba944a52244d8ac573701620220540b8abb5d72775ef6fa039984fe4fb068fcbb11622715947e0bf43b00dcda85", - "id": "5af79acec8a2ca26ae1ef4a4bb4c36c9d756f4627407b9831dc1aefce88d41d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 51527438731, - "fee": 0, - "recipientId": "AN8fTyWTGzGMWndi86xLuPZ6sx5RqDU3V8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c86ad1039fbbf4dbf38750f6933eb94de081730aa4bbbab2e69fbf6bffd86994022065cb206c7a6da6a2cc49564fbdbfd861c2d7a43ae574a9152ca743ccef38883a", - "id": "0e875f8909e934d95123c4e39b0dff610726bf4cc793ff32ac3ab87607a869c0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 51527438731, - "fee": 0, - "recipientId": "AcoxZHkmsepyWCs2K77TaAAuKXzYFRhQCF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a556f84c6f3ab557c61a3f103d5a19f967a624bc247905e6433206d17b05979702202bda3ea0fe77b2217dfef8a79921a4169a421451eb08f31350667e8253eacce2", - "id": "baf5b1d34f909d2ef94d82896e8354fb10d59464743e95174fdaf41f5816d986", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52000000000, - "fee": 0, - "recipientId": "ANx31923yoBut3ToXoPugKPnzgRhRGELPX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008e91db1c38a78379d51739c8e7c5bba7c7e10423f90d975a83ae8fcd5073e08a022025c8927e171c351598b0ef39f93c2bc2eb2bc86eccd43b5b8c9f6bca681edc97", - "id": "6e86383cbfbcf26c0268180644f7a946325559b282acd1b4166d1edc55271951", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52000000000, - "fee": 0, - "recipientId": "AN1XZJrPk8cm7Vwf1ZTrKrZtCCLZgkyrJ1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205e39a2dfce53486be7370d92d65ccbf412e079c07e0405d68fecab4f1e17371e0220438de48ce7ef76a2bb2b65bb9e2013eb1790f1c9b26d2037138ba4796315611e", - "id": "6cd2b5fb62f45dfcff3337b52bc1f4750761e88df28c5d02acfecea7cd0164d0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52000000000, - "fee": 0, - "recipientId": "ATGUpBz7YkxB86G86796U5yTEgyK5u4rJk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202a418c3135e189187865442f8ffd4cfcd5469eb1858ceb9add3bd0810ea37a4a02204744e68ce981a9d4bbc54c992382dacd14b47458873da4488e3995f206b09db1", - "id": "722201637e0a6c263a9a2094f2ca0adda2d8b69f44a6f15e4d29a53a93cb2985", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52000000000, - "fee": 0, - "recipientId": "AGyRgpaEFZ2ZnNHL9Kr5bpMGkv5LUWebQb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f5ce9d7c0055100d09575738ccde05ba74dfe3db8bdf4d8a30d0bcd6b99592b02205a0f50872db6a29e0f0bab2c687c44077f9be26558b022dd74b3e2275cb6b853", - "id": "33f843ae2198e8310d2055803607fac2d24907bf8f8daa2f7290967e81672e14", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52000000000, - "fee": 0, - "recipientId": "AT3g4LwnJfZoN4vuFLtMJY9Wxq2uMtLuxT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022059078b6b0dcb5203a2c3ba3a46bef4d723a63ecd6fd3a2e3ad29f67b50fcac1c02206994e842c8011c3de756e762b831e9b1aa12d8df872d5838ff98d53aeec95e2d", - "id": "f5ed89f77f37b64aae398ba0e89fb520081aac2ef2ffb2aec87c42bf29a719a4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52285195184, - "fee": 0, - "recipientId": "AdJLdk5Vkejk95ah4j3ZRcEWu34E1Q7wVN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022056d42be934431202be893e9d5e6d640d2d9f5acab7aac3bc40017654ee6688a902201f407d488855a6208aeaf3068f109a0b5de4abdfc4233e8bfbbb89ca3e33847d", - "id": "4912aebc2a0e8081a4761cdf85238d17a27277193389698d25200c3259116728", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52325513889, - "fee": 0, - "recipientId": "AJE5ptbRLg4SGn7DQcaLChpuzjEo66WTDg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa8e197adcd0a62c4e79017cea097a9e22a2301be0ab22fb3b5466846bb78575022044a063dd5987a6e91f6ebc4858754ca6f7e7fefcf53bbd3e1e08e20c3f7e14db", - "id": "58aee6b0df8cea2a4fb9ec25402b3440a8e5076890a8cba8a9ccef0ca5b52148", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52512522119, - "fee": 0, - "recipientId": "AR57nJGjbRknsTMoDJVPnKjeH7H8pQ4J3e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fb7872a819b54bbc0b30dcf4d8c3e413c84278d631628dca3a6805ec1231e210022002c76cc1e9b7672ad3f0a1db859e4b36564f7f815e494c5f9b1d601b6081e8db", - "id": "3f54c13b225a0c71692660cc5cc0910fedd163ff1aada08cff0b6216df414f4f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 54692496501, - "fee": 0, - "recipientId": "AH1HPESny9sMqiJz1ySzoonvcgVLPNTVY8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204b168552ce3f30befaf9a566986b6c17f67471fa0cc2ea308acf7e580586d58f022017a5a0173699a7a5440f82dabea25270637d7167372d9aa71ec62b09e4c1860e", - "id": "b1ac5258577f86365c9c532150696897e0f5b304fc755657e4166b7f784adf79", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 56071540498, - "fee": 0, - "recipientId": "AY4gFPmvcG2GfTcVE25mqeDmnDQMPcWndc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203b1848de0b3be4cccfef6ff105581675c01006ec69da4492815ed63c194338f30220377f301d3fffb2a88f7ed13d42e2231cc5984f63775f082e3c85e02b37c1743d", - "id": "e1154407af38c6fbb875feb1badaeb5cd8262821b3b1b1a441cf506aaec41d4c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 57049922288, - "fee": 0, - "recipientId": "AQbcTN3TDRc57j6fpwAvLjxsc9FeAJThp5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dbc5ba06661abfcadade917512e640a45ba57ff29ca7fd7ef728405e87f8f0cf022016d0bd3ef6c0a88d161096520dcf4673dc2cb8fc96ca7e61095ea5e67465c8b5", - "id": "3fd968cf660e948f9fe340334684579db327ab1d6281ee05a840dd1deb0e4b7f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 57589490347, - "fee": 0, - "recipientId": "AZDQwckNFvu5RL2gP6x5SfNFtzSk91tPTQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e3824a2385b1f4133877047b7d5e702d269c82e64bf96196f526a063047d97120220327dfbafefca990f871ed25bcbad0bb7308279930468676e84c54d701890ee15", - "id": "d1d7712844a3898546271abe9b6c0d139b3d322f436d26d7805caa0a34e1f8e3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 58900000000, - "fee": 0, - "recipientId": "AZe1BULQ52K9W8Pm2oz7WnS8EFsHSmtcGa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cef7f753f6cbce6fc6513cbbeffb4fed56e32f0f6632d87f4471d59841a8af2c022036f7202c8e437be380ecedf8f1930d38fada608900e93d33334ea8815c4ada2b", - "id": "00acf4f97b4d6baf38f7a16619a7bfb62d93a96cd30f5bb081fa15099dbaadfa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AFuQS4a6wysBSmZJpMXq4xDNAetJJBAFsQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d8865a56a63f070cbf21dc8e48685ecd862077ee52ccd2c8cd8f40be72c3c4d3022057b11b12e968dd5e87df9da6928c6028a131b59bda09ee07a204dfbb9a055844", - "id": "d3ac3ea41460fd549c79fe3badd7a80ba4d467869b62b6b4bb342d8db1570efe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AeWauSzsoZ2FXZwP7GFX92pPLyJMSkSKph", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204531943ac76998e7bb5987455c901b6174d603114ebf915775590c8315653896022001eaa07b36c01e93b07c66aa139cf299ebf25ae16431eb85611dfb9cea9c09a1", - "id": "c6719c892816ee2bdd2c113614cbbd92a59de7b54b3a4c17de68c0da22256d3e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AMmqT7w7oXLazLwQcSV7WHnYx9S7KSXixq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200e9b8f5aa7d2471eb1981ce5482230a2c2fd1bbcc6db017f6a1ddee9e4e90eb202201ec640c890050eac7108f74d40f010d1e7d5f9bb05f2fd17eb5d8fe385080342", - "id": "7d269a6a5a9c507c1637707448d5fa61324f9394471fde06b0b53bbd51b0cac3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AXdGv5eEBNGK5UTcRaFHbW3FwEiXifcegt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd07c0502e712b804344fdc118b52e2bd1f9082c52aaa6a87141f4e46d42107c0220718a1a7d58f887e544c92404b49824154352139fbe33eddb5a7dd5e4ef88d7ad", - "id": "f0425c83dd9d9eaae6158673566fc41a1d4dce9304fc82703c97463b4f93db7c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AYA5LBnUiYXCraG1jz6XxGe1FbViiJbj7n", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022036479d87611d110554a7fc93c08cc78094a769674379c3630c8a77498626c1b302203712a96621cb8133a43789b7f4c64dc6dec8de903de7c7275582c1fba30007ba", - "id": "2b65f2d5ba1c79f39c61a99642af209df57514ea158bcdd790c637d6287175bd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AVf5Nefkn7H2m5ccTY2Gss783w4B2XHrZH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b3ed017b9c924a513e4175d1ae026e1f6e0cf5062d8abf944a1d456c9b99a3e902206e81a45c621846a3b3975fdbc63e842926e2f0736a8cab98e382fb2027309e09", - "id": "afc6dc49249582cb59fd0ef44d5ef26d40d7a4faf6cec4edea6410396511a57a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AZmiLVom6XmmQjch8mPaZjzbiZt7xEJ4Ux", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022001981eb483fc7c992ee3b6491fee84e92940df9fe96655b36a451771563a94f002201d0494084a12cf4de2441081cae004067d1d43f802c7c661bd75d0c8882a6d9b", - "id": "c177aef927d06946f2c80cdb33855fc233d0b20a5bc0b70a72d3e1f6076e4e4c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AUs83fWgyxfn3vFcpr5DLhiNeCrRcrY8YL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022025c7b0874fc7eba0e23e003d5172490a564e3eed0a504861ce0346e2782a28400220165dc08dcf6720694b67de43258f127630a4d4cddbec8a2f66ec1ddf853d454c", - "id": "6e6193eb69cb0ba573c5545757078fab69cd407c3ab4499ab75985fbf378634b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "Ac2vFD8sqzixk7MaK7JwEYoe2QXEPna1e3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ee6a0aead5de861b29caaf1c876304bd9792e917a5ceeb1164b69aee6293727002201edad412f00a6efa6148a6a62d7148466f4d24ef343e406b5291688a4270b92b", - "id": "ab0da3b48e067dfbdd94c6bbcac4f3e468946a508735dae2c7cae0ce051e2087", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AbJSiFHgiy1Gc9dnpnSUgRQVu7cmKUTJFo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022007df858f6b336a855e06e11bde5f75b4584aa8941a8d61ed54b91ab8a76670ac02205fd2ca83ba0694a6a5ff2e552330616ac9896c319a319dc9998f5c6b57edda65", - "id": "6cbc50accd6cdccae73f205c0bddb4c944bdba76323c5100ba6331f89d16f952", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AaSvUFxNvGZcvxk9ncd7wnqtMw69qTF2NG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ecadd5dc516c178c2d80b3b52caf05720883e07c0b28bef1e0a20f27c0607900220178af5fb06c3327e6b5c6767c848bb2be60e311ed477bd64a56caa6e66dfa13b", - "id": "d1bc92a487e9202d245e42abfb7e37f60e08d0a78a3126624dd0e0783c2a9238", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "ANFpv6FWVyodGiMNwfqBvfNo3tt59q2uwW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022064edc033b3ff422b695196c4a6d3a0bb6b321e467a2771632816d92ad05af9ca02203afca6079f18210a50218dc05956311a77b04c9b5c9f7c960f0f796c436c92fd", - "id": "a3429a6656e132eed4dbb74b2ed17238caf2790187166ecf4af877aa2a7087f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "Ab9vTWogfBcwA3d9BVdHLZ7RVasLqZ7qwg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100923954a62e4ab7684af8694637e66839fcff5fc11d54de68b4fa82f7974b313202202d89f173b1c62ab26c6f9210a8cf5b7d12538124c7366c6c1914f8990bdcb272", - "id": "6cb18de69128b7a4c91bfce46d17227f1103f50f0061a9228e2488c58d26d18a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AGonR6i4nHcvrHN95rsmg25YTgqRm7TBox", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f906a1d22b886f4198c8e6e8729d55f15fbd10761b445f92a2b7748ad4ed0295022018b9fe3b0e95e59d612119d349472090f1a78be9057c754dc17a0c23610247cb", - "id": "4c650df3dca440c5d3f2f17f89fe1000e114a60df15a51190134eac8feb777e0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AZp6btpTxvxPCWdsJGUqZJCrZMYrS6cCk7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c8211a6578d9f11108a1fd950260f2e30c15c854c5cbe33af0d6ee329a81bfef02201fa7a8226fee5217097353f9b00c39cd6901e8cd2471cd3d3a34daa56958dc5e", - "id": "9ef08f0bd8beda3e82d6c39147005bdf9590f6df478e87716c5c69942f6e34f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AQT6VvbyD8BaVSBtkq2rQJf7ZFxpQMrGFF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f989f104e83e5091791929078b408a0e6c56d86bb8c9caed517293bb377a92aa02201bd438311e99fac1e31bd4c031ebcb9fe7f05de8a330713be4bd25f5effc2e0e", - "id": "30e10c01d2e0a376ce6deb42cedf146f8a849f9ead0170286e7ae6f5ec14af92", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "AJRzekFXBecN23nD3H17GCvU5ykTqZ9Qu1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cf8ca12989b34d74df49b7f7db16c5d29d5c94e387890a6dc714628ec737f7c802207eda53f3c1d3f43b62598b1b6efc8de7a4b46ffc069750a90ef294b5a5d49fe8", - "id": "167ef310bcb21341cc2b7c32b1ded135a3223f5322eb438751a5081fd8a61c1e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60000000000, - "fee": 0, - "recipientId": "ANVX8UQEt8MkJokm35rGPiQbkMD6Kc7KSt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220723a3f4869ece9ec362b11c309a824ef17ff8287d3a6e2f1046db473bcc0faaa02201618ea49f81bf73c60ee427956afaa75826bf94788d73319c8810ef2c4538427", - "id": "b3734997009c33befcf1d9d3aad7917aed5eed0c4c7de9730ecba54a72db099c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60600000000, - "fee": 0, - "recipientId": "AVFpsz7LiwhRJmCAq63uMCFBXhXYaMNAq8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206c018a247cc32e4807e6fdc62fc83cab278d50ec7a4f0f67e26094387793cf7b022014782a31c92672ce921fe86b3b8cf36452e40b360830f47459c3fd8b26cbe5c5", - "id": "072dafcb1b3e774663b9c7be4c1c2054f7c9357e3708b5d61dde8104101026de", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60600000000, - "fee": 0, - "recipientId": "AZ7M9chTUSjuxSUkWVDyQhAbEDVXbP2ZvA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207ce6a75041112fe7cc42d42ee0e35cddbcae0f33ba051c425b5d3afc235b691e02206092e7c04965993b38e860019c498968baef52f05160e492c5bcfda86a868e33", - "id": "d728112b0c69a9c9f5a3c1800347aa8f87de5ba5407fb16fed5e1079f9a48ac2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60600000000, - "fee": 0, - "recipientId": "ALMaBHtP4Ki4epwJut68XMDxYVRXTwhPuU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022050f867d1d89a62e65a88c9861c6fa93971ea08bd0564d9864a51517244196866022032ba4ba30dcf55cd08834085ddcdd425eceb7cc784f4459e62c11e70f29eed21", - "id": "996aade098c28e002949de9eba9dd663f3d9a29a8042dc516142bd56c91f1b8a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60600000000, - "fee": 0, - "recipientId": "Aapmg3eAWSv4bWzPYMNUWMx2cULimBsKDT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a76f37bb0e4c4eb508597231f437289107f7f9fd266c31c653c2e10c45d59cfd02202bcf4f912212e7d409bf2a425d73fd9697836a63019a064e6ce2ce8f174987d4", - "id": "544531e7295c7333e4e770a010b73b7584d06751a7d05388f3a523080299f929", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60600000000, - "fee": 0, - "recipientId": "AZHUEDRF7B1wHT4qcg66t6f6AZpmAwnQzP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022037ff7edd4d48a58cb2d3aca7cf2b4a6f047b5e0ecf930c6624deec7e6e7ca22102202a038b5e27f88d596acc94687529c4700fffa41e907a34118e4867c012083b2f", - "id": "ce8ac13ef4d89b3c3e9cb359a4e5ecd8ece85313dd6ed08e247995fa12ae92af", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60600000000, - "fee": 0, - "recipientId": "ALMjKKEK2z3hLUY3XubSxkAhzdpU5i8jsL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203589acf0d84cf3f231ae931522af87cd17ba02138f1b5fbaf71b1ee6f530451702204881f4bef7736594e5cb28e5fdac8bfadb467fc853c58ca7f59e55e7a7b121bf", - "id": "2988196f5d08ebe98c6bb9de0830e0675c85fbafb60a2ab7168a633360c112d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60620516154, - "fee": 0, - "recipientId": "AWFzsKF4dALGbqzQgR81VbFBDdTSUJCBNU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210083abf4d927d402f0aec00e8c37e661e8be625b8ba47b3fe3d604500c358f88d30220645351b976be790dfeec71082328756d3610e33e57cb8b396657c69fff96feb5", - "id": "a68385459023327d26fa0e745c0891a34789bc3db0392294cee99cf1ddc1b190", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 62300000000, - "fee": 0, - "recipientId": "AGikMwhSBsgXuwCUZsv3EAkxeYSsHCRany", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022014e9a28ad7815e15bef49340133ef40a0ec92d8dfa80c125eb0c26e3d47ca96a02204a8ee6d3f847fadd1ccbf50e54d4a1f220ad28125f82cc1e653069761e953331", - "id": "71a75c1380f47a79f47847fcf615e733c9b8e4d44511490729cbc2cf7cb04b3a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 62614203690, - "fee": 0, - "recipientId": "Acu59K3HbqucEtsNMMLzogU4tFJxmuJkCc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202448a56483a3ce4580734e2734c1cda29fec792689854f77a3bd2aeeb4b395be02206b17cca51c241b725a9f41d7a7d15e308f0f94f33480fbdea4f4ca2c99d01482", - "id": "d71afbccce8620d94b04bc74c40038eca31034383a296570c0b3181b00433bd0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 62821647096, - "fee": 0, - "recipientId": "AGg3tZ3u4gE8mSM68gmeEiQnjARwUpT2n9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022011162de5da032f0a5e15596ee983ac470d62876eb2e8348cd015eccfa0b9831102207c6b60bae672fd6e593b3f5040733dde21c7ed75741a85faff759d97f8a5e27b", - "id": "ec44a065625a762aed0e093f485104dd5cca3e4d73fae79d6ead07ee6bc4704a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 63651541963, - "fee": 0, - "recipientId": "AQEsgEhmEBzGLwtXeDzYacpgHFdmZzgxBD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210099bf9fcf91b0efffd75888770663c351f6e8158fbaec8ea99fe43382e31ad5230220575000d242528aa103ac912daf6117cb4ffabdd867fccdcf847b46d6e46c0a12", - "id": "130c08efd433f91433f3170cd7bc85b484d2c3e3c53134c0d955932726bc5338", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 64100000000, - "fee": 0, - "recipientId": "AL3BcBwg6R2PZ9pmnYe6wzeY9xr2E4Rhdi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220684f309ec0d8e34d82365c29e7ea21a08e4d044bf5a96e17f692ea4320ad12210220155cd1bce15cfb31d60d097fadfd6aec6bdd14f65c079039d9432ec9c7ab91ce", - "id": "22372757608284b2b88a6c068c4dc994ca37684d765c14f5be0bdb48ad50e968", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 64100000000, - "fee": 0, - "recipientId": "AbpU1DmufxiTYMDQrbKjeKumxoVPwKyczD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202f40051c81e2bf95bff6cc78fcf9cab2b999d48ebd9b6524b593c95441902d9e0220347c380e7fbbdbe5dd4ea8492a5deb17835b48ac22f388c2b519fd4103fff0fc", - "id": "8726a184e737d57567c17b077a424fb7ac9fe5b0cc9acf509e3af87baf5167e4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 64700000000, - "fee": 0, - "recipientId": "AR51dQ3b2iPD7kxY6Jr2f8nvG9V1Tp4bXn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e701b0a5ae50d35cc985776be08a2af2a1189df4a7dd2bbaaf7b182ff5fe5ebd0220123def90996bfddd4c0769746130d4e13a8150d75e32770b6fc2024cad56e80a", - "id": "9f890472facb44d3f0fc91cc3100f33ff80a0ca419d6654479c0ad2e1867401e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 66023125672, - "fee": 0, - "recipientId": "ALMVkKVthbGLSyCAu99UXPTcJUCgycJCio", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204c190c4ae5a7885bdefb89112f08f782934fcd60fa2fcc0ffb5a57fc25589f0702206c086bd249bceabc559d1bce46a35687963c7af245e982b83bcb4679d27f048b", - "id": "f0db655b4c6a0cce96585893a9ae0b5834629b471c8a10bae2d03ba7b59a86a2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 66682567770, - "fee": 0, - "recipientId": "ALVWWv96ePvZpsr2LJiYkgczjp5HFh2vS6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d4223e4d6d86f362b5d9bbcf4887e3b4a7905504509d7cba066761e5ab4f2eee022053f2f8f1e12ea2c979c1ff532d2529577ee1d5128e4863e4b88081fe4223074f", - "id": "e49671791471089e19114260ecf0d0410571a828831fc693900129992cbdcd32", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 66985670351, - "fee": 0, - "recipientId": "AK5aMpBxSFkveYCPLK7655BNCYxE2oDWTj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022030059d3cb8c99259e250803496094fcbdf5c644a54be5de71ed67579c50dfe7c0220551dc20a9ea8fab2756d945ba96b6df226f786f1517ba500808990535517540a", - "id": "1ab441b67ca4f041fd0b4de0ee084f47808fc41327afbf0db652ec6e8eb0293d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 67500000000, - "fee": 0, - "recipientId": "AZiCsJNur5AHuBmzXFm8hrvzMtxaXeBmTP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100819f9e25f210711c95d4f5bfda34f90c4cac0d2ee7fc788867059cc8437153a302202f94cd63aa4e59b051dc69a1acf46cd905be00b8547d377f705f6f63b3a61119", - "id": "373da2f2c548fb4f41d36f7b39aed883804bc453846bf42ad223c7dc22f65903", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 67922251265, - "fee": 0, - "recipientId": "AaVsZhcJWbpsrwHFYQ6Ms7dwRamLQGbiEQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fadc2d82b1e12c7ade65115d3d2a80c59be0570cdcecba6b6d46500de0b1723f022027b16d7f88841422575354d423d0cde46edd1ff3dbfe77cec29aa3cce7ddb61b", - "id": "b6cebe893f177fac2b32bb805b3a8b6ef79dcf42d7e7e1e367da46fec3c6e659", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 68047923959, - "fee": 0, - "recipientId": "AcfGpnqyxHTTCg9qGGDNoJZxp9gP3RZJEs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220056ebdfcf97cd26c4f1ddb8d2dc2eda872aa41c72122dbf4b1fb7dfe6a13a566022066e8ea897d1845fe11262d60550d930d4ec0be1408c874ae05b6705838499ee4", - "id": "636db58a8c9e586a4eb157c7b2be73af3edae8b5ff48248933970e06cc14ed67", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "AdReKteT3KZV3nxYeaUwbxuvNu4Tbux8QV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220045e674e2020858cb6db65016b0721094b18d69d5c40b21e420eaca23c7e5e530220581f3f511f5ac357d3dd730ebf0e6084d62a9aeb356d1317463799444ed3a327", - "id": "194bef1b8604a0d86e0dce346149d9a67dec990def05321fa4ca2f5d857a3b45", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "ATBhoLR6mCACu9T9iHcvPmXBbeabR9o2Af", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fe79b98c6ac6eea23af36c1cbea5f5ce104ab7e8cadad9905bc86c36555e552f022050dc6c3a5334e5efaed6f072a584319c51dfda211522d9424561e0d48bf5d7d7", - "id": "bc5c5cbfbacf7ca885544dd956d82dd4a99d3d9a9b922124556711c35aeaef1f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "AULwd36ELPQHp3MBCVjQPGsG5vLbWDwsX5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220730f890799382a99a6082648a97e89117ee5ea2cf79405ddbf726c8732a48a9a02204791eb8cad7f9b987dbd4865a507c87d446b502f4faa5b469ef938be043e034f", - "id": "8de5f8c57d06f1a26d57f2b30e1a6de70c1f4181b5c7a5ac46d4d77d4aa0d1d0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "ASWtE77ekytMEB82Snx3iZeyECBwcyYAGU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009e5a8e6261a7c50f8cc6afeaaf51cf433a55a2ef85545823811c2bbca752046402204d879e4ff9709ac18288c75ff6b2d0bf210d4466a7c46d82d688727b3a49eaa9", - "id": "a0d0bd4e4470ad7708a1b6a8d8c26c5ac9de239b8e15a581cf2a29699648f944", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "AMWKeZaKAZgtvX3McGVZGVCjR519mjdxod", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e910ea19a113d2fe010320fba9848358135d3989f92c328a59d9611e1382a2c902205e83c177d11dd68147d97a9536fb8657b18121c69e3257034a101c7c928d4cc9", - "id": "622e1654011fa90a79be805a43dcd875b0d649c5e5d455285c2103430f5329f7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "AMP3grMYVWSxx5g7KdRf84PjvT3f9NDiZ4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100acbdeee8bdd4dc28809d30dfeff20965f48211675bddf7bed7a105a6e04092e202202c61fa35cc87ab1b02a8530b0021a8d05d2d20f34520637c9e17f60e603ac2b1", - "id": "9ade4fb6bd9dec55103ea68751040bb6cadbdb7f7518442327d72599dff0ec36", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "APyUPnRwawq7LHHQbuLVwEVvviUS9gYvqf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210082e9e6dc9eccb3d40725cd5434128ebb94f56d47bb864d879a79cf6a6d7d92e6022020e76a058819f2d1afb709d1262835bd3571112422f885d1bb5dab06900a41dc", - "id": "961478ef66c79ff2bfe8f9c1f7f530c1111e998ab0f36386ae9b5d6a27fe2875", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "AaygtE13tPoD7HTNBJiJQ2McDRxVaWdJYw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ef82c8eb76122d5557befd50e52d7c297ba3ec741cfc050fee3a1ca10d3bd01a02205b199789061011b890a09029642750b784f53a174082d8e113a43083fce2cd24", - "id": "5291bc37168adc5497986964e7f44a553aa5007143cb056be070857b285cdb0a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 69300000000, - "fee": 0, - "recipientId": "AamfrghtYBbWkQtRV7vohTfrJCPv8Sg15f", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220757f338c7cfd2ba9342a5246b2c20ab46c2fab13d8b8475f748ae596d721afd702207dd4f45e4a0562765392b4041a812f3caba35985ec8b0a27f59af91063ca0ff4", - "id": "49f82d3d4360a92c509afd96a16e76adb8cee98b82b17c9ef0d8403a7d933b8b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 71253891180, - "fee": 0, - "recipientId": "AHcjnPJA9tdupQcjcJMLtD6nnWJ9e6KFQs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009ac53ee5d6f50ae5eb6021aae45b59f77ff49b259a42bb7b072d1c9506c0063602203238177cd22e6d418db707699b4051639c4c530283d13a02c8c9ebef4fe9b4e9", - "id": "04069fc9bc169922314401d138568a956bfe70d8f3658a5fc456c33ca10ad67f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 71429604399, - "fee": 0, - "recipientId": "AYFJQE3EPeSkuxEa4PubKaS4eVitUtZkiR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200add47cd5990fcd49c559868724063fc58cbdffe8c79aa958a7ecbc75a603bf7022017f022de0655cd62e383d4b7d9d4a4a074b8222ae7c1bd6cf26716c1292bb3ad", - "id": "06ed48da4f8bfc579c4ef2298b43745eb77529889ece543daaaf68ff2bdbb9b3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 71590501751, - "fee": 0, - "recipientId": "AMP1K45yauAQU1c2NqwuUu6ekCmBqtaPoP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022014db9f5d0e46fd745c970ca39b54ce13f245ddff480e7abd19b318c262f3b951022062ca17464681bdfc5381876ebeb66740f14ea227007c34a4dfb1fe0946c6e2ce", - "id": "34598c7779c1aff18a82cfb53f52f7cd9baf51bf2fd9eb2d8088f38e9de615e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 71600000000, - "fee": 0, - "recipientId": "AQRhAZwfV8rwCvsb4WM1bVTbenugJn3c8r", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206b2f68e67bc4218888b389cadfca07d954a4388635974d1c4872819d9fb4305402201dfaa7ca2e83c0694ab80505ac5282ca28ae45970161acb774b38d87efaaff22", - "id": "e9a1f1079b09dbc72a80b56de6c65f7414455d7d569c6ef55540286c6e6cebce", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 72653688612, - "fee": 0, - "recipientId": "ATBxkNfkzM8NBxdCsJ44RdABoATkD98qdR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ca29827b5530b9846ee5a9084d75a067fbd9a932073e731af57c460bc403b5210220487342cde09bc5c339223b575a8a4c325098202707c68d09e0ed48bffdf72f1e", - "id": "11ecc7e569168d579cb6273ce6d27f58f29fd4db7c2897082d6e49cc6a9bbf8d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 72700000000, - "fee": 0, - "recipientId": "AW5d3EEDJBQnKFHkPJnKPTinSivnBzUYkB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e68f134e251d796c0c4044c61881a7e49f260d17aef7fd2f4d54010d9fdf52d02203dd02402d012f4d91946dcb449f95d61d874e02ca02ef4bc2c36f7d4eb5b4547", - "id": "f38d068010566f2d81f972525779b84f6e8693880ef6251b6a63153a2e98d9ad", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 72700000000, - "fee": 0, - "recipientId": "AMYvEFsHMcctwEmRrghWE46Jj9kWbFPA3A", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a593780b9925611723664234d85ffde90f314afb68d45e4a0fccb00d40c1bef302200ddaf8f1677d988f24302a388e71d069149437822d9c7d2330fa037f96770110", - "id": "b1d8f7741c54407d505bcefc467f1daab79772ff77e71bbd0ef7f8708ae43582", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 72700000000, - "fee": 0, - "recipientId": "AR44YGUBeJXZDQcbUebK7Ds8fVHy7DHjgW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207ac0a6b2a5468d60e9f0290e5ceca35b896db702038309ffae2a410ba25f2b6902205d3643b2569c0b185f2bce2a1539f5a1cb92438a1e6efe3512080712fbf4774b", - "id": "1bb883cf35619f46d4839ebaff381c9ae4e00c3d4cbe981acd00bd98a1c4ab91", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 72700000000, - "fee": 0, - "recipientId": "AM3F4yHDLbzGGqJ9uMCjnPCv8xzoJ872sp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d6ac3a85f45bd763fa1fa8850dc8fc12978155982c3dbc250a5514d2e4f6081502200d08e59cbb1dd6bd22a7ca8375ae7bde3dc2499367482483b84b5dbf2df4a0f7", - "id": "e46dc48c6353759f1bfd73f83112c808d8a2ebbb687fcd177002f7facc3da944", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 73796297519, - "fee": 0, - "recipientId": "AGcoFdESM7NdWm1HodtJyDU32BHyShDqat", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022071e03ef704b56aef2dc368d7ca2642275dba4b81eb085aef6b8c1fe48307006602200a4354795dd801e4752ca441187d98f25609ac9b58a0ab79d2bdad3659db6ff8", - "id": "71e2650406959057c6503cf336a99c7be97b592eb47e383b03a334bfc3cd5bcd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 74014667709, - "fee": 0, - "recipientId": "ATkzTWGZPHNhEKCToyULmtTaxSJYNiZRYz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210096a5bada156e5b921023bc5d2e79b9cb6058c2a6b722674c3bc04ff45e2c2d0f02202a804f965b55887303c2f15ef22bdfa0f70639141ab4db6b982832b76e5111d8", - "id": "ebab1e0dc5d5a02577ba83b3bcb0641722dfd2f08acde9ff14e7f4d5c5415ecf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 74124099984, - "fee": 0, - "recipientId": "APpwmia2SFKQmhNZq9zxav8aidP5vBBptx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a4a58e349be3f5d8da0b00d71bc150261f2538a2031e06fc8dcf0df8afcb6865022003308a299af829c6bcad2f9bd36c2648d96eb3a3bb8b0c33e687b33099cd1fb3", - "id": "0dc2b86a4ea26f04660150949d00091900028bb6fef863392f2bb3110e77b498", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "AWshU1zde3iSo4691GzJ1ibHDCqA8n7mGW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207a0ed602d07dca8bbf2b4d9458a0dc70fd92c192c2bd05cc455dab5977be3d48022044f872dff762af785e654198f8342b6083e66e9c0181a16c3c21a1f2423b982a", - "id": "2df19ff6e35c5a76a99a062d893432c27fbc7e98e6d4df4ded66109311b58744", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "AVWUFjyvKxXZLYexXDWLiJLuLXZ49Ymb5f", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210093a55677a758062eeb56a99d5994dbd886a2bdd33f76287fb9dee7ce6f25322302202ce4a80a24aa3b75d0b8dce030b04985f3b625252ee8411223735ed013535f1a", - "id": "40dc85e19386d391e0da2c9d50cec7dbdef55e9777f8e0952e26ee7c9a2523e8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "AL9X3YsJksdtUqAMS2yojWG3rThNH3rXqc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022007d901327f63e75971e3a5a023ff97c2513cea96dbd4f4d2969fc33fcecf3c8d0220606584131bc5a786e15e7a7fb7e8dd752cffb73c578b5933a62d6ea6072c4ad0", - "id": "9d0d26d7fe267421fc7db3b7cdeb2b877696d056dfdbbd4dbc4fffbd052c5584", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "ATE6aerp28bsHn4g9uhKk4ncJiGtKpuSNe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204faead30048606f6b057ddbd4ee76da47657a853cb8b644e588c29d375c9f2ac02207785f0ecd77c1af9f8f0f68c5cc3d344e4176fbf911037ec8df681e72f5d5f09", - "id": "7f2a17f9a14b1c84cf539431573a00a63d9059051a58ee50a3ae575effc08bd5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "ARc6St2Vj9T6PU8JPMtUn7RBb3DuM9uE8p", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100de6a748d2991d40d71bbdaa2de5cfd9cfef49c4bb9965174e0bdccc58cd7e0f5022027ed0e384fdaa03164d3e0f317f475d6f7728e1747afe8ba829a47edd3f39b59", - "id": "ff8a36e128dfadbdfd48159c0b65c608702425d36f3dbbe0db0657a80cae66de", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "AHfcau8pKsQVGWzFCWSXDyoXGGkTKvuXsK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b1a48ab2f689a43aeb5e2b8d59f6e777ef945685301e64dcee3325bb9b3ef46202204b125c359a5d00bc54f0c0d0d3580a9841c73cf8e2ae19fe3e9666587a83a143", - "id": "64e0e887e60dfe4cf3c0a0a00ce3c89c5dbf6d76607e6c1bec2c8e65611492d6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "AaSGhysJYBesZiFSf4zHj986A11DRvYtca", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ed44ab5a22710c41eaf06885e3b3801bc9234987085adae32f7277d1c31dbd51022022a855e38b1e57ad064a4004f37597ed6d31deaa6378e06dcbee180075ac0fbc", - "id": "c2742c309400d0bc0e4cd6b4a183cf9c58d2a8d00ad67564e6243e5019ce79a4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "AS3gXToMKTMydG7jNvxUMNxygvUc7okXXw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d8b5a57241bd6982060e2a5260a6d6a9ad076c10e9a6b863503686b5b1d9ff0002200933f2e858f279ac24011d5008fe4a6ab99efab3ec795954a5b86c121fc09a90", - "id": "27fb7f36d8e0e19ff8caf5f74033b5912eec9611c439561f1d3edc37537c569a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75000000000, - "fee": 0, - "recipientId": "ATS6jWiztxiZw8EFr2VU158ZiJev6bYfiF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c434b9605d6bebe5950ae910388235326b701d218a72f630a523386a3d085fac02203ba1a9016a6738f0d6078d89d7e481aef5fb7e1c698262a345453f8978211ef0", - "id": "e78b60e89f9757af6f2193efd826c7f4e43cf55e992abb49a49fc6efd8488ffb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75435988442, - "fee": 0, - "recipientId": "AHZ3K294SDAyoNfoBX7ofAvxNr3u6bt2Tb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b700ec2b35d2a4a0a36802e503a75794557148e37121ea7983bd3bfad456d19202204fc34d8b6fcfd513b1e7d74dbcde9963eb606b234897f1d8fcd1024d236a7118", - "id": "c331e66c718f09ab4d637cd139a5e0a9620b0aa268b8548b51f82113a6f91961", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 75912885491, - "fee": 0, - "recipientId": "AL8QkKgRswzkHyPfaYuknd8F98onyYfsw7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b2b7083ee6801b455e14c24c1f739e1f19f39d429054e86c77475d82c54be2470220128182d6a3c7b36eb456a5e2438b9cf7579b981feef78a31fd71e63200ca1959", - "id": "9f74b82aa9272226bff1637d566a3e3ce213957f053eb6fb167ddc9d1b8833d2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 76800000000, - "fee": 0, - "recipientId": "AcHCwqRbb4vneRguSP6SXqFq9RzUt8CCto", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203e95f92a3a0ff1cca53e0acfd410a6204074cc52135671a1cb1ab193e7e42b1402202cb648ac7bcefe13152483a85f5cb54a1f2dcbbbdef70fda2e9744d210692614", - "id": "1758ed13f7ab2c65bf54bebfff06763f6955bec8836c1bb84b9277960aaf3cf3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 77291158098, - "fee": 0, - "recipientId": "ALikhBgeXghUsmNXXwXD37SPJJKP9YKFQZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022037329d395aa796dec8db0f41eebeec510fdce6cbcbd5bf429bd308392def4c6102200f20bb5f7d5849d26ee324ebe881ac7fabbf3c4e7fe5e93605c0f7615810f50e", - "id": "144e1c573644f0461ed333bd72feca9b94c7233763ef21f196cc869937bb5de7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 77900000000, - "fee": 0, - "recipientId": "AG755Z2rSmeXi4nXc2Dp5xjEnV3h9DSePY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022072dad4689c20a7daec81141e12e12b48ccd71072cd2c6350321523515830f97302201e442d4448f5a0fb02e73deff73b0f798262beeae00d47c8f8a4ad94e041a4a3", - "id": "50259feb79aaba9640ac99629d6dd7cc84a43c14529be5549267c3fa219e071e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 77900000000, - "fee": 0, - "recipientId": "AX6JKGAGAURGfwYoy8r6BqCsiHibZQFtSH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100894bcf3faf2ced40b12da116be0cebcccbdc17d822fecf2d52be9dad273dda8102207de8bca9554c27b1a37e7679c1b8ea4bd75b1b47697b9535789d5386f2a69289", - "id": "c6a38a970ca5a17e2632b958bf0f035c4760e2320fe5991c19dcce0fbc2ab0b8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 77900000000, - "fee": 0, - "recipientId": "APkL2ZzBb5Smjhv8f9ugVgS8iRJP48pZEs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009f0a965cdca67e25a459cae3674e261d235b631393a521c95118260a50ef817b0220667fc3de471fea7ebe7d92776f8d0358bf1cb6e480c1964f45d104ee9ebc815c", - "id": "b11d02a6be882a82b57e34d0876feb673843ef6baafba06421adffbf34512108", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 77900000000, - "fee": 0, - "recipientId": "AaSzFFxYD6brxjKMaz6PsLM8Mgy611VWXU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203968e6322e183b266a578d5f6599b707c74b8d35dfd2722b1e43abe8850434af02207e4a7783ba60cd39d8503769f886d7f699b673cc1811879f37c97e000aee180a", - "id": "8a51fa7f53ee7372961dbbc9dae868c1111748d769488e36cb21c575ef3e9475", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 78500000000, - "fee": 0, - "recipientId": "AavMxN9AF7pJ1yV2qmWjYeihGZN4QBVSTo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f4b220829fbee7eaf190752898c3bc55359ab757413f2acb3d8f3f3c154e9206022076632671be2f123a47cc6b18b1f4ce8c5fa6697247a7b4a4da6e87e9c3810585", - "id": "becf405f252fa1c2c16031dc31feb20317838c2995476064f11fae9e7b3f23db", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 78806671001, - "fee": 0, - "recipientId": "AHTeBUJSraRKMDkpYmE2kzCTdEMTw2iS8v", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d1db2ff69f14e7066d9e3a8e9d9325f9c9c840ca2f3cb4499b72d615cbc47763022067e19df7ea14b245626624b76b408827093fe02036142966ad7c91c2a847bd83", - "id": "98e5b97afe377508e7c517db5d694167a9380169c78903aa92c557d673b7895c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 79513639585, - "fee": 0, - "recipientId": "ANg37TzJQgskwThhQ526R6EukJff7jWXZD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e68c3dee5a9eb77645c958a81f8b3684485ab7ef7108eb29c95239d618ae6cbb0220779e760390ec7ef1700cc84b80361dc80c23df1bdc8fae6e2686e20f489342df", - "id": "e6fc80f568069d7ab3bb599d99e81f02ee2e8d85b449e37b2456f996f729f727", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 80575550021, - "fee": 0, - "recipientId": "AUwoiLFPxQimPnE8Bnp7Z9GVLGwNMn777R", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204d6e44f453221389ffcc58e8bc98a9a2691483f2e0713c1c0ebb38ca87236593022041a360781403cb8f434b221177060f206b3bc7bb184e011eb1e5066f1ea8d70f", - "id": "75dd1bd25d4413d6d4d5c2d9853d9c03283a2606365b1f0458d1f05d2923d4e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 80648439946, - "fee": 0, - "recipientId": "AWojAkunWdVit59t4Pr29yGWAdhCrMzeNf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022030ee47eac2cb2b80f4e22f407d8eee4c29733efd0e11daf7a3937bee384e44be02205695c43c6ed886a5db961b87b6008ff171ebc5add97e0318680528940ed0f007", - "id": "52f8f83b4a2346b02e454ecb7dbd9fac6a8d4c3f2596eccc5cc5c1444844a0e1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 80880602287, - "fee": 0, - "recipientId": "AdTfUqtiB3NJuhhw2T2xPYHiMJYGJLFXBT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e5957017f40d2b9a0a7f10722697b00e49c16e791e1debccb1c2a072e3d063c202205c19ad3b770b138245321ff7dc299ed5f411f3d9fe727f22b54d1165f0447b8a", - "id": "f8900c94b8fd4e9c80d07fccee69881edfd4c525f7d624f076522bf677c50c56", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 81355763706, - "fee": 0, - "recipientId": "AP2TBQe9NmoirGTja8cbSuH6158GFprhoS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206a6476bd2d646155d0bab7dc4f952a8856c7789a6512718ae6d8b7736ae1ba1802205fc63cac9eb495ef7e2426a64668b771a694826c943023891f0f46f5ea8d4741", - "id": "ff059b0e5477e3417d2e3ed156d6cc48dcd62cbd7ab13e30c5120f80900d8e73", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 81837696809, - "fee": 0, - "recipientId": "Ab5x44ac1h97exN5QJsXY81WfMTAJx9YyW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022014c66256aef1f3094558a0890efc5a0f22a25dbcc1ff52c6f618a1ba519eb5cf02207a79ccc6cc6e562604c2d8e11eb041c27ff824a2682de9f399906e5360bfabc5", - "id": "4caf5c2aa360f7f94a872c50e41028b7c83540dc65ed63fe8c982fcf0472c0b8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 81837696809, - "fee": 0, - "recipientId": "ATpmYfefxVXdB6raUbujbqqj3bx5kVoDfB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022073d07498a3e0ff75e0786796c973e8700139fe9222319fed425e96b03c78dad5022012fbc2aa62a479c88197a3656ec4488756356cb4d568ecf65fce5e8a15f5c5c0", - "id": "5e3146ffca3c3c29e76ffe1bbf28dacecf88566d393117d870feee892111cda9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 83100000000, - "fee": 0, - "recipientId": "Acp2FNSe4DwXdMvJxBn6iLGGMZnvmtaR7X", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cc65894c33488bd85d10eb2104a142710a36d8f4899c8198dd50db51ba7bc6c10220527d3192c1357d324ffb84a70fd02b848328fef04d26537255c4f8790283f138", - "id": "d584d07fac0668f426731192de3967134dc574c6fedf27d98118df0c796c2dc7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 83353209713, - "fee": 0, - "recipientId": "AJHws2iLF46S5j8JYEwSHnSvcJsfGmXuY1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207934233bc6857a2ec4440fb1d3d80c30f3e71b86c59438fedc0cb8f773e6d15b02202f6915682768af0db3aaaeca4e8154acdda3446ba816d4086636af89ed8726b3", - "id": "f16ba7a15ee70202fde2f172673374204a3beab555954cb8edb829214d4d1f9c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 83989725133, - "fee": 0, - "recipientId": "APwVRmToNQFy3vq4wiEHfi8F1vYfwGXMxe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f04bd60b6b64559d86587bf3c83f6407c20b0ba82b91db0d79a284222d5ccabb02204bac2bdc4e1b328c668aa33cac6f435b666cd6e3f907b91463b3f5b03678d799", - "id": "6cc3b4a346d67a46d101749766f64a6332dae291d148b969d042cc8d178085e4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 84900000000, - "fee": 0, - "recipientId": "ALaNWEzy9zhCVGrJw28wSV91ES1fhWZpQb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c27e16af3bac0ad147ffd8a884565fced7a6a609fea72d2b3e23a54cbf83e77b02205a8ad93c4da3473bec4315ea1917af5734413171b22df8d1f1cc14b1f0071f0f", - "id": "d7b32ba893728ea38b48d632302f22e87dab2c04a01cfd809413b7991afbc70a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 84900000000, - "fee": 0, - "recipientId": "AGNzHhBmg7JkSgae3DKbhj45cJvd3at1ht", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f0dfcdf262541ad079d15430a8057ca753ae9ad4030811913110356b177afc19022031c0156405f2ca3262c121a648242b65f40e6b9e02f981af090bf5fd83d77234", - "id": "d3173641dc8035ce4e2bcfb9d20ad323b3b525d9cab25a86f8c3ecd6cddb1c07", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AbcfAjLyDM9xKnnKyPq1esgwrpPffzuhpK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100868c42ee9b8707f4c5fe317090e9d4bf3ee8fa023fef5a2dd1ab6e5613b58c5d02201898a17c6bc802c3bf03522db36e4d23a4fd74e780e83aac72043f51dde78074", - "id": "8c0b5fb020e1c904661cc97dddcb5ab0a946d6b4f8e74e4ec22dd225ecbc29ce", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AKFXsYK3u5Yys3gaQVtQnARgQYr7K2qi7Y", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202298b4575ccaa0e90fc62851eeb490411cf463a7f81205b530d9abba7151376502205b1834247d0e24b428d211069e86d0fe7498dce5545c21fcbc669a04738440f9", - "id": "83b38ef3992d3ab5884eb9b7bdfd37d7f937134f238e0810aa807ec6c78bbd34", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AHiUknBbPYinqfG6pGkgJnLWXyH5SdiAwu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022068147f79aeecf424c969d66138b8cfeb1a543bc833d0c1dcaea2b361ca67088202205af3b0111769ab1ad9a03f98c25b3df6db75e352c973cd6ac2a5adb9a528af23", - "id": "8fcf8c21aaa30f9a9cbe2e8f17ec58e811e52a7cb71ddd76a824560dd5ed26c2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AGpULTAeC1WWDY8PFZjkzrbhwUqnksCTKA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ef2a3557376455e0d25cf3c951db49394f7b211503b42b57a239be0eb76b41a402203406584ba5c6b3f1c791f1bd54781c27cdc2e4f794b1c187e3ba05d8710559bf", - "id": "7c09fc8b85c9a9b231aaa24e4ba3149f2da51dba3693bdda7ef1dc584b6e2094", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AWhFiapndC1wvpb8o2cxWak84oFu4NBRMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009a5bed09102a91a9355bec42813eb86645d93ef79c7eb0b26db2bd155008678902207b8ce7fb53a59078b53581a81ded88b791737d1caa5554c16a05a9c4ce15034d", - "id": "0288e9d30d96bde32799db384292c36aef36840fe738d86431cf001180e171c1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AJUmYRUYf6hksjU2eWExtQZuxf7ZCACSzt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dfea58e5ce12a20be120decac2941a279eef12189d7a5491856278c7d18edd8e02202aba95df77908c8f46353fb1c4bdbbb1d68ad510dfdb082fb7c7f74e3f4f573f", - "id": "eba13a92a631ea574f1e478f8d5612d3af6ce7f6d610b669a7b46b30d878cc7d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AY87NwPwRs4tYVYHznKKoqDWLqCUHrErYg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210098df44bd72c80c3ee825909494f2a973484d1b9d806e8b2d6359fd89afe5714b022002b29eea85c6bac9267d37d0ddc0f74a89c5833cc15f5b8cb5afc98355b6eb4a", - "id": "74d30f27ca09703402c3f09089ee5c4cb8548625974a0416f5efcff722984606", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "ASmTzH5eR9V2nkrU7E8J7G8Mu8RTiQstJe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008f15851112cd2825da273a761843bfe8d772c88da8e8108835f18758758e3bb7022040497db4055ffa5c14c2a2f3fb000cdbeddd5a8fab8a9066e55b2beddb34a790", - "id": "850b70f669c62b31665190b27391ec3dff00222e4e86757e04bb35cc01076357", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 86600000000, - "fee": 0, - "recipientId": "AQqd4UCBGeKi4kJZyjoqPB62W7Gqqi41YL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022065e858357c4f598f1cf4c26e20e52b245fe92c2c58fb66c986106800e106d02e0220071dfa1c61fac54e79288d6b59f7b15c7271269e7cfb4c93a76bd0a7c6858011", - "id": "dc1dcfabdef49122d646d50d52fe9b558803df411c765ccf04fc4d86cc084d9e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 87141991973, - "fee": 0, - "recipientId": "AR8hYbKPJYajTNZ9pnpHjWrm7CFqSFaGHL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c68c155894dc39eff0136d6aa9d7046964f2abbff63548e669298b937a2554dd0220669383cedd736fc3524a286ca3480001e17e6fdabe616b0290d151bed94361b5", - "id": "1697ab9993ff077aecedf05e5897d1366435f757709d0d4b8ba163cfbe64192f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 88300000000, - "fee": 0, - "recipientId": "Acd1WttVcq32Xv7agoi1KnoQZ3uhS3xx5D", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a6e73937fdb2867650c86e5ab5afe66407d79f38ccd10e9af6e6b9210852bd96022064e9f36604a80e8ebd292143d6dbb4dcde5fa48567c1389ab448a87b57a7edd3", - "id": "129f59b0d566a5081114b4a4a287e8d352baa05b6ecdf8efff73fd0661d92da5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 89405066332, - "fee": 0, - "recipientId": "AGzvuwhRjpXsCiF1acJxFnnXgNrMd8ekWe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205dd8167fcd2a31bff75cb1a6a98adc1490bdd1c8a073929aa05bb56ef3cd199b02202d18b11bbb36c91fde4ae756b4cf7dba8197433c8723835ba91b1f4424a40a32", - "id": "87618b8944a3032bb912cbece834559d1f043e7d7255705c6abda7bda031f445", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 89514072770, - "fee": 0, - "recipientId": "Acn1izrpJYNCFrqFwFQhKXgXHHyxpGAVTw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207a5fdefe87c639533e3a5ab01f52624f4d1b468ab88eedab0e523cac50513e1e0220388fe25747ea2764ca18cecd4ef8c3ddfdfd2a689effe830b22f336d1db292be", - "id": "2eb0bcffeb0067aa69f8438afdf76f91969faed45f26cd0c641fc0b11695145a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90000000000, - "fee": 0, - "recipientId": "AG1bTaALP6A4Lh7Z3pDDehv8saCFpf3RZQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204f0e2ff8ab6eb771ce67474134ca7a8b59f5d0fb421730fdd306803a548f867d022029256ce0d0c018660a1c3a9f38a7c627a07247a73b2058ae52a000fc06781267", - "id": "591c06ac993f8d1454caf4553330213b9fa246c7b8e761077768907b7f136dd4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90000000000, - "fee": 0, - "recipientId": "Aaq9zxgsq5heY5uGvHWpUanFsLp1aFYtzs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c537c102f6d3e77742203961dd7718f4441db6f9c73e6dc17c139c7639707592022049a206a019ec362981d3377b6172d499de98a4e603b2c6b6772e676f3161ce7d", - "id": "c66078f962373fe9663a4a89460d2cb8694aee100ef510c511376b64a6129d14", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90000000000, - "fee": 0, - "recipientId": "APdt2CLhn79TsuFb148bXN4yRWDtxXByi9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be4c6dd3e6266e8f3c488f7a70e43547b289a8a838af519fc0e3718270133a7d02202b438a20931ed53c67c4122f3bc8543c5f51fbfdd918e87e2da4d0aaa9becaca", - "id": "fd445aafceecef71b3bf57c4f047fda3759aaa279f5bd7108a5ef27538b11002", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90000000000, - "fee": 0, - "recipientId": "AcAs2VGLLA3CTwB4zzHBnbzrUpFU94qpo4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c21d6ea2c66bc26ddbcb250dcfe904c10684eaf8a30aef233aec5b64d3ec9687022011396be43ba72cd7cf2f5001e619fc4856d972745d012194b3d951834562f802", - "id": "0963c73ce61593cb65e2faaba4c803783a37198d3b93ea54323d3d52615d45b7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90000000000, - "fee": 0, - "recipientId": "AHvDiW6VVXRHhGLkMpuR8ZX5auBZPHK4AB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e333d51da7708efc3ab80ad3e510482ddfd87f2193b1d61939d38d5092fc5be402207bce382e3990b99c53e0c8e5dc3af4f948a7f511e514684e0f59c37aa0ec54a2", - "id": "f2d908530b25ea4f3cbce6547e4aaf248e18fe149f190d598f775fe26458e60e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90100000000, - "fee": 0, - "recipientId": "AJ43cE4rT2RoUAv3LfkDzYYG6podChvrUQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f4b85a3222ca9638fc1b8c5a64e4e23aec2cc4ccd8d957cd1ad0747cd430fe910220716428e2f6d7289bea53eb029006d1a1472cdf4661c0e55eb9b04ebcfde5958f", - "id": "a533655b53cfeff54fa341c2d9cb376e71c70f2b7625f798a66e6e1e0ca1c505", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 90882035337, - "fee": 0, - "recipientId": "ARQiX23neMcAX7SPfyauwbixNhfWF9DF7c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022018c324014c8bdd445bd2025fe527f52cc668886705b068837b9d86cc079ff215022075646fa0b7eee5be5355c1630eacc5bf4b98b1653db006d1cbfd41e07c156218", - "id": "4111147e598f03fce985dfef561a5205500865a7adaf07ebef15a8035b070726", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 91944813749, - "fee": 0, - "recipientId": "AZRqkB1K4ybcsZXL5ZV6sYfcqYD2bRYwzW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220794d5cfc737927e6e6e2a628298466bda26bebacc349fa3b75cae81c636bdfcb0220463b4ef0055b4bad1150add80f192999486d91c4ccfdb07da77bc9100844271c", - "id": "8e3e472a029d3c34d9360a92e08f71ecadbe5c2290fcd295d691b35444510626", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 92749389717, - "fee": 0, - "recipientId": "AXiaU4ksGV7AZHLSPPMM5Ey1wbEwEXoV2m", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203436a912a9c53191957842d0d04401a5b33b3255bafecea63ffb703fe36bc4a402204bdd3dc39ea956d43a909b9e5ad8c71909bb5291238ef2b0ff7bfea8cecce5c1", - "id": "6d4a7e3b265f562969594ee5ada3abf8a1a3028a324803a7d8566b294ace73f6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 93006402914, - "fee": 0, - "recipientId": "AXZLHoMLqpEVAfeP1qcdtprba9GBoK8rU1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022074f4c6f3bc573b7e6ad33da49b2f429d8bd93d2947c8befb2eef024e24b885ff022035902bcc93a8f39dad93fa36d0c378378517d50ed2bfe1b910d8b6bf2712b3c6", - "id": "69adc07393e5f7e41408f665571ccdf56ed41e8414002df3a5ee78f83d4e51a3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 94906857132, - "fee": 0, - "recipientId": "AGwRjNYG47ycvkzKjM5f6QrzcHoU8MNRHT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f31598c11e21ad6c472aca50ae8c9e762bf7b9dfb720bf81e4edeb0c94a0863302207172d053daf3cca142027b29c62cf8ad23f2bcfe856d9ae9bee82f2398bfcd38", - "id": "c311c2d17e5882af306b820209a4cccc28f382dc7861e8e59d39b7a5e023502e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 95300000000, - "fee": 0, - "recipientId": "AY26UuQoCQTQA3Yaqf5Khwf1dWK79AkfuQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205a0874f7f7aef5aa5eb650196cf1ee1739a3c0d3be23fcefb43cb63efee96f3602203aa0715dd7b00a84926f6e7081840adc97f9dc8dff542373b614bb9a20912074", - "id": "493a1e157bbab8a11059fd93f54cb3015076628ab11b09a1f6768b233d8c1454", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 95300000000, - "fee": 0, - "recipientId": "AFwj9n5ARhWX6oWNbTEeR9bbX3o7xBikXD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220483099ced6fd44669d578eab70d4630660597f0df1a0fb734a62c72cb2581cf202206636e3f8066fc4857d558eeb5a3502b3aa2dc25b2f51f3d44aba967650479988", - "id": "a3273ca38ac6a49f276e658b11155fcd151f5745ce220bbf63373cc8971d784b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 95300000000, - "fee": 0, - "recipientId": "AU54w4okTRtFbFa19MC79VBL9B3QW45trR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022055aefacb9cb4500f4573d6ab8e50e724970b2640b8a01cf071c4f0bd7076ca6a0220474772c7c8f44a38f98a1fd4be1cdd65120bbd3c2404c91f21fe5331f2f429c1", - "id": "de221e24f05801bb0c5681342127472e05e716852392d8d60733811779a0a142", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 95300000000, - "fee": 0, - "recipientId": "Adap1889XDiKC1bMLuLrSKzsabXmTuBXjR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220083115c84effaa090c3b79b7e7036a9bf2d9dc8e5818aad77446445f17ac8e41022016a2dc3addd3d82605c5b20e27f89019dba0af6bb91cbb609f4f3be89f463b4b", - "id": "97aaec895cc684d562baab61c97def9a651cddd63b1b8fdea8ca846a86eb595e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 95559787156, - "fee": 0, - "recipientId": "AayAdq2K47PQiS7jMknrMwmQZujeEp5Fic", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200993e9038199932d4f9ea36724f89ac970875ce335ec578abd5792c24c4cce170220048ac8441077ce7e8eebb2fb0d22d4216f0ada9a0f8a3bff22e713f2d20b2875", - "id": "28103dfb6b5efe7dd7f5f880df2b3b910a96c4ba733c7bec80040aaf73b9a754", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 95612193593, - "fee": 0, - "recipientId": "AWgoLa9CgxHtUPAHqFvMpxvvFFK2iyBxUK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b71039017534404cadacb04535b1da38415411d5a2f6f5b511131c6a7d48843e02203504cabf89ca95390944620ed9398261da2a0a825e38d26ed5ce0c94bf58469c", - "id": "2a4c8eb73ba5fda2120f2ab91c7806ac9b603ca1a2b2d8301d676fda113ac29c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AbD1qLeYWasvZH4PX4j2h9bXFXJxAdhAuy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203d888caf166966ca8a3559a40b6e8a7ac840048ef9469dc143bb1f9be26acbf302201d256b315dcbca537eaddc86c56850354726e67148088e38ea6763c8f774e2eb", - "id": "0725b2165c79199d1cb3087ce9f6989d0781c38bb4fd3bb054e9f42cf577532d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AWffb5DY3ceB9DcHknBsLSmZ7bQCX9BgZa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200a3142e959218f0b3b7daf341222c3d2ceb5be21817e5c97bac96f964823faa70220086698050a3d8030bb6a1f01d4000297561b0a6204d58f8e4795b58a6b1448bc", - "id": "50f6375cdb90d9bf764f1544c5c85daaac335148e7c5b9706fad6564711eb832", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AJyxXb67KmdzRibbRSp55tC1NHcSXzNQmn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201bc4e3f5e8bae4e7bd81da4e8d4d3e8a1f9c607c36e39e442235b133e3c350dc02200cdab848881bcfd15adf3f38afba7582753e9824cded8a96c35de855ed394c6d", - "id": "41cff0cb50f6902d42cc455467415829d66bc857002b904b63b959fdcf028215", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AYo5WNLUCNSRKkvxxMs5hHCNxme7xQ12ZK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205958c081b5336400e4a7a834a33cb8b5b05b81cfd7fd435b6f2c2109850db75d02205ac3c51a116db91e925e26da94efb2143e3e457669ebed1bd7464c9b1306099b", - "id": "ec651028a9b2b6924dd11472761295fad83f3738d2b1ca5867b32a867e17d946", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AZFKF8mKcoFVz6nG2zvbFxQXfumsXK5a2J", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ffabd89a120c2ed7c03c125c1da112043f5dd98cc99f205ae87488f422278b87022054bc18f5d6c732e8a2132086bb9961d9b3313bbfde4f428cca2f61db934275cf", - "id": "6dc4ec6abe9f5b39e3624841432e9c9af338edc2d4214468029d204d15bd50af", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "Acye9MMv7bbhsBoN1T7TuiCKwPFprnoJgk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f2dcee2429c1d6ed797c1d8825890474a4770f5f18774575308946bafb3b4c2502201ad6bd91169df28bb2fa6c827e8cecf0696d87136de193bcacae2068d0af83e4", - "id": "ddd07d35e2e0d2b1eb9d1fe7297051c22da9762283b767b6f8377d12959328fa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "ARWYVBZxnuxnp8JQp96fSZHZEw3Ea2Zd3X", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022007e6b2cbc4a6bf18e2b310325e1d7a56e0d455cd9afc6b7bffe419a9e70b710b02205faf2e91e740f331c04ca133da2e6408fa7f3ae4e5328b8ed6befabc8f7adef5", - "id": "52d56ac56c6e2363f94d1a953a908ba3d841bd193c4af65799f652044c66bde2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AdPV4RprkYHnm1YajzZYcQ7QjVLwyP9HrS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203827a7141256877ba780e3b36875bb9af64f7f4fb3d9bbd4e9705d25dada25a7022027a1c9324c162cdc510aa1130099435d7d5569737249c3458f496577482a297b", - "id": "7cd4765b692240de26ec4341109869564754a541571ac191a1ecc960220cdc6f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97000000000, - "fee": 0, - "recipientId": "AGNE5beBRiKtzAGxCGHPUEzSTbYNmB4x65", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220639fd4f4f25e365b6cdb6ba4cd631a93cd826376a07ad941aff9c83eacb438b3022060a52e0cfa6f7b4b0048d30deff592b185a7132439031c9f84aa5eab945413e6", - "id": "aff7235b112fbaeb9bd31ed8ddd827a95d735d74cfdbec373727031a6e752d07", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97297237832, - "fee": 0, - "recipientId": "AWxt9gGVbxQ7cX7o9DBniXLA3FNnAzo5Yc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dda67cc7adf9bf59523c49e3862a569502c02b69d4183681ba73fca12720e14f02202744c8bed417d5bb28499eb7bd3d353041c32d86247d9152090925395642f2eb", - "id": "08977af886121fdadc51a53156c4d920998d236560d5ba5518eae27b0cb75460", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97764047435, - "fee": 0, - "recipientId": "AXduXps2TeHn7AvTiJW6m9qqNCtrPAXD3H", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022033d10a0876ec4a4dc66e809ab4a852ad48cc688c68891ca5934b78550420e7ae02201798c11a5dcd1c21f873f0bb299a86d363895ea0e6d4e7a1137327498fa69256", - "id": "9d74cfa57b58e0470fdb88a9eb944096e1c749a4196820575b99e948c125caca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 98045028617, - "fee": 0, - "recipientId": "AWYjUa8Y5UdaZKtit3Va899xqZDdXxsRaS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c60266fade798d0a234c8e8d8fbdfe5f73fb015a29f2497acf95e78f1fb5e192022034c1a2debfaaf6068d46e70e42eb606082438aa53eeb75ae1d84f84b6cde0353", - "id": "828319edbb770b68aa5aa91afd76c02f76308f17f4a1c36523fb2817d1bc1e28", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 98100000000, - "fee": 0, - "recipientId": "AUzivjaUXHG8ivXWuj6evpdL2TjszQmVWs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022008f1b85ead0a3a3328f43200c0c532a2a539715fbf4856e084dea1e7498a096402201e0daee01c06927dde071f5f5cc7e6fe1eeab7291cf4f7a0f330833c535d2deb", - "id": "fa1085f0ac80d410ef9530aba0af26ae0d652677f5e90195bf2184be93dfdf71", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 99262036541, - "fee": 0, - "recipientId": "ANGen8SGYqiBR8TFgtawL57qxbNZRN1hrt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207557798214dc6bd35d8dc197714dc93287dbd45edd7ed832991635e5f20cf9a202207ca660d57db42578e31b0622648439ddf0219a862579b1c933d5987b76ce53c5", - "id": "f46f63e2877adbb32ffac6af90c5cbc79dc19ca5592a0579d395739d249a22a1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "APSNQNbPQabUiMqxLxjhEyTkeh27nWQDu8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009b295629afb30454c67ebce29203ec8b3f684f194fddb167bc4ef9d3739a300902202da7e99a48430663805db9cef9cc32915ad694ee75b5573a5be7d7d7d688afa4", - "id": "423abfb5a05784af1225894bc22e224dac42d421e90bb4227c945c35ccb9aa54", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "Acj8SdBVCKnaCUFAydJ2Ggk6E7cfiRP1kJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009d471ec487447ee43e43ecca799d7d1eab2528d75a1f98a35d9f1c777ebbfcf5022020d40ea9d1d3903bf5ee4e2f67f97b7d62fdfeba622ca8e72e67622c9dbdf8ee", - "id": "0977be517acbf17a25ebbe3c2568a91667b5bbc05c6deea85d61b2f1154440b9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "ATz8duPjUnEL5oXZeD59rLCmtvHEcbui6i", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200de765be6c4d07836d73dc78eafed1a80fd2e91e3217e7bd104b6b084b09d78802207b244d85ea74fb2248bbd4c265a9e88a5b6a55ac6126f1545f839d2c41204f95", - "id": "106903e0f03337c08dc2d9af7c17263b53376474f56134b76357113a4e92b3e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AUxTza9YeaMXKqgU2FXp6mr6kGyRNFACRF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220697444d31def970f5a73bafad9d3c0865cfa82a8f200d95913a6953314b7b49102207f70b0f71f2177add874eafa7ddbac1c3f5162f1f410320b06cd9f312fa5cddb", - "id": "e2b78d5f015620390fc6dc0bb39dfe1473461281c1f2de2dc47668959cb705fe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AdbbGesHxgSbb5S9BFVvvmgrLbz6vMwtti", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ff6e1f7efbd6cf34bd7e27dde486d29e6429d28f0407a2cb86b9d3b2430cf22802200f64f32c92f87a8591e674426c34e43534c3afae30c62f08b6492f53554e99f3", - "id": "900cc87a9f10d54378456a777b72168fa9a8e44d256d8c46c362a1e068b8a3ec", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "ARRdKZqLoEz3BqkspqPFnjDMXUWHiK7rUf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008ac2f93dca4a6e81886d0cd48cd8d05fe612364e9382f45eadce42e3051cb0da0220349b5ef16d0dd502d887fbd2caa4d666cf0080f55a6c8bb92b99819e15cb148b", - "id": "48bdcc189c2fe7e1796fe78cb49f18b99187feae93e80d3e014d30101c067f84", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AM14wbgpGVFcR6EeyFrZ98iBSGQdKGwoNR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f56b28811f377035b6f5ef6597f622a8f45d0ab7ba46d9cf8b73367dc2ba72c4022021bc53a68673a43fbc49c98b19708527f61269f16bfa15571a74b468dde373e2", - "id": "1c0411c1f4e0cccf85034f50469939ef994740903f65ec5c51d3ea04a0d776d8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "APwycJtjaT8LaXEwN6DmyanzBWMGLCDffz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022030e5363191d9681297a0b85fda62a95ed499f3b6ec18f7dfe115371456367bb5022018807046a5e802ed222f7b2f87e583942782a7a3f3241592f9d7bed5d5d88784", - "id": "f0825d84e64b56bf8f388530cd18cdd4909be54d364ff9703db24cdacece080b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AFwTR2Q1TyWgpYvnUD5AKZ3KtEhTZnjSen", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220767d49d3c2497cac443b78c806287b68deffea0b7bbbeb171335997b40a10d7c02201a7e30b1d7ac9115d98589b3b54c16e19e7466b8ccc4394b488cb2637315af0e", - "id": "0dd5220da9ca8ff958bbc8a4040a0502d8ee757ddde806625c00f4a7cdcdddda", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AWWbW4iYXRvXzXAG4DVNw13kxGyZXkXJZe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e00353541efedb21d3bd42e33963056ec65f0a79880f8a3a5f991103d8f64b3d02202f324a1de8c96fa2fbc41d9c3cd544e3e25bd84de68013c38696115a497a12cb", - "id": "3fe581b3aa87b6cffddcb98d27f62ef83138e2e78fcf3feea8a44db4575f89d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AZcPTCo5A8M36wCFvDvPz3H2cBBJo9p8iq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cc27cbdedfcb771e9d222bb6cb13a71d7aec8e36fd433ea04942633c5d5e84a20220564dacdf36c5d0abaacfb62c445c8fd1d046a794339f38a05b8f40ab1d338d2c", - "id": "7682c2fa3c74fc40d917290a665ca4412fe74b737cd464c908850ee631af679e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "ARoVFy8w9CsSBazrXnigzRZVrrsVhUzfLs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022072790cfc15458125cf1fe54e85d58955797d3f4409f99fe74d3405742fa72bda022013075f4dd0ec2054b42eabc9d41d4cbec07d885bf1650f75ef3936826dc2d1b4", - "id": "f20634bad0858d0e9b1fae15b9608e8b6c0eb8715adba0977e0f17cc20dbee99", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AaerL99dZfwZL7yghV9k8Ri5Hhpi9YPsYp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e67003e1c964db17bf2bbb00e6a4c60ca12e46753d36785b13041a136bef4ebf02207846bf567a2bd72bee3289a321e58da645d3a64dd9030d0f52028de50efac7b8", - "id": "8aae4664984ac30d28b171541c9a41dccabe558089b073d8236171c40456c4a0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100000000000, - "fee": 0, - "recipientId": "AYA1abqExAqcWxP4qHaMNN6MdwSVAzz23e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205e46b3935995f1edd2797f8a4ecd5181cf16893d4f955747824e0cbd7effae40022012f11701804587a5c2d9bdd8295fbf67524bce5a26ac6758bb7d43ba6975ecd0", - "id": "fb4d5ab6087839d6bea3c4b93d91a5654cb556023c0c2648e3ddc47268b29447", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100023851656, - "fee": 0, - "recipientId": "AG825v123YvZQuL4vbe7wNxvdyTewt9gGm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009d37379a461696e7b42faf48419ac770ead1768095ff38997eee98fc01bccc3802203272ec96815df48dc692ff686952e04053d60f14c0cddd821f0bfca775034b27", - "id": "aca358d024faa5f4fa68b7943ccace6bbc5a9ba2bfcc614445b7a1b4e96be417", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100749519793, - "fee": 0, - "recipientId": "AStWDeuQ3NTP619nCiyxbYbnyHTnR6cxb3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022046f3e87a0851d6feaae6224127051c783d08efc892aa52118f89dab6f11f02310220030a37ce70dcc5b4554397934d67ea888f49c7ad825a337a5540f8fae99cca44", - "id": "b92dcea5e20cb9348374898f686f4d2e2b6ecfaceefd6a0ca637bc61c6a2bf46", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100933159396, - "fee": 0, - "recipientId": "ANV8jYBCQdPscN31ES2NDFQ5Xe87zUrFzX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022000ac1e8b3e471505f8476f98ea31920128876a804d22ee446af4ddf210442cfd02203032930c4d6d6646d3d4140ff89c48b4e58374770f6be9169d04c1099a39d216", - "id": "c57cb34e723d065e12f74b33c2ba5bcde69763ef191505ba9cb38af0a769360c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 101680182605, - "fee": 0, - "recipientId": "AUQroQ4if3dKyFwgv6ESDM8MHyJDe8MVLP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022025f1efed2ab491fe87764de2cca46aeebe3e31e558210001798d86c831dd69fc022059dd3257cbb318ab88d7f21baa2cf56a975582d578a3c67cc188ce46793a2d2b", - "id": "f1d975e6feb911d060c7ee86cebf9f56ea0d3001d8d1e9813d24c0fd1791aeb4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 101830636768, - "fee": 0, - "recipientId": "AR58rt33ihFKG56DXMVdKoidzNm4C8kCnF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a8e1e2102f75026fdf1cdf3bd8a737a673315d48ccf8b28730235ac9f78dd2ed02205d5a50aefab9c7817d6769a64c83b19b96df69769a735a3051c9888eb4844ce7", - "id": "84ada517a25f66b23c3127830b7d77948fab1c1fd2e643f4166384c1a6af9bfe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 102744627447, - "fee": 0, - "recipientId": "AY7JH5b3GdHv1ZcbFXW3c19ovA5k3jYtDc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dc91d5af8df4ce928414162c63d64452bc1a05ecf20fca159cc374deab7401ea0220749733c55f0532b454f3d401d41bc3bb79fec7fef8b439f54c34c17fd5865a1d", - "id": "edbb728bb5f8ccf9384ccd41be5689507179d05d60f72188c419d5163d76b6cd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103054877463, - "fee": 0, - "recipientId": "AHRjCLd18MVx4MevXGsygteD41pjjEmae5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220223d32f9daa36a1d0c96c35d6825922ac18fd23cf9e69f640f707c27a49f9a4802206e4132f40a65ceabff49666671a7f8d1d22848e06ff91c31a42cb6cc38f0d9b9", - "id": "63a23da52548b2b0f932340589409d453f3d46656f78dea4d8688d926013f84d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103054877463, - "fee": 0, - "recipientId": "AYwziM1FFZdiYJFpLjSDBQUnJD5gQG3T6U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ef3d526e72b7021dfdaf9b7706c88ad8bd985f453754a116254d13b888e127140220259eebf0c8aa21adee9b573ceb4bc1b4597c72cf6a18cf1617cf645eb169dd4d", - "id": "915baba7089f762e6ff2398ad391de54f7285f0b155a277d88e5a9bd049139b8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103054877463, - "fee": 0, - "recipientId": "AFygDabDnSBm2M1iDWofGNHPa6ZeCEeqj4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009fb6a8eeca7df3b18fa9d1db1a1a4c1c63fb36dc5f71396c9f6d21070e7fe8d90220426ff00438c59643cf1ccd01575832f0a375d1217c43cd816538f3968c643fb1", - "id": "886d32c7624d952ff3db788984073b74e5dc6bc027ef926d1139b0a512dc3f2b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103054877463, - "fee": 0, - "recipientId": "AMimZDyN8vdDp96CHVLtWkhtvmERvTmnnT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd942aca86527ff6011d230a0ad9626468d361b91f44ad22e32c0774627b0cf90220410fe8a28dec6354408b5da8bb3b77f5f5a4e2b491dabf5177876a8b29928c97", - "id": "b7f7c997bc93d99f92b458affba8081b15e2349f35176e2100ad38bd859bcdf0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103054877463, - "fee": 0, - "recipientId": "APdPdgkVCNJqQz1xP4qqzRY6K2tjybdH6V", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bdb4e44659fffbe283616db6d45315d3abd1e4e559bbf323629b9efc4ed81b080220069b6e53fd81cea49c8136ba9ae23e00258ab6a2d30130d1b31784a21b669a96", - "id": "ffaa043601b679834ce66deff3b7ff00046acd8bb3f573ac6610165ef087879a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103900000000, - "fee": 0, - "recipientId": "AZiQBaR5TwSH34NXrdrvErMX3aiYZWFxo8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c87c4c23d0a4432c99df14afd1c88bdda73f3a156ad89a247ea662080b7214dd022055506cba670851b036bb39e638986879199b28312fdf356a685bdd1b17003708", - "id": "146b10f76eab1b6783af1c108c35df9bafa3c6ba25b72ac6597d9fc57c7cb215", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103900000000, - "fee": 0, - "recipientId": "AZo1U1VRipZ4aWx8LYfEXmyEBcFTs2St36", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022040317a8ac4fe1308c10fdb2ccf695db0d4374cc57ce5b012e7d37766a7619ec70220014d508f6dda12b5b9ff095e6d53fa77bdaeeb04713de273725d09b55ca22251", - "id": "549b4107541aaa18058574d381323196285df6645476b9dd4e4f791ea4c49106", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103900000000, - "fee": 0, - "recipientId": "Af4CtTPXJ4Wkgrsw1zsvmCoEUS1AksAyEk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203e6c4eb1fa7b5311f04bc7cdc38445b9b737592b9eaf0df143998cf5ce66eeec02204ee48dfa39a93b9afb978afdb4b2de537dbbeaf1c82e31059d344be867378cbd", - "id": "3fe8bb50ef8e7f6346f7447e934e9cf8e51d981ab151698ac258cdf03c72b8ac", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103900000000, - "fee": 0, - "recipientId": "AV1MipTPz9KkjJKqSfagNiQkpCDACG6A45", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009c3986801d041cc233bbd394cac166ddcb2c55482aca48a85a59c130c55cd8d50220164eb74f9ef23f5a6307756fe57ff385258a9fbdeecb5525802f79cf5dee8956", - "id": "54fc495250ae8a95f231553ba952f8ff502aba015ad586f03f68bd6fceaa7606", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 103900000000, - "fee": 0, - "recipientId": "AdFF3NRiwLpVuypouVth7HKk5Ca4xejtmq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100efce8d12b523214f79402697597729647211ba94ef27ae2743af9d6ff0985da602202b75b8e0a0a655a8863b39940726b51b31566f98ddb51b2b17735a7970596edf", - "id": "436256751f2e97b12d0a99f725a71aaa4b4ace4644b4087298592e0a5cafda7d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 104570390367, - "fee": 0, - "recipientId": "AXowRmmMSAB7GFCy8XPpNLu7E23B7DmkPa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e0b952d2bfc38186a4ca9ca51607f44ef64a3a0e58836171f1668f0f47fc10b5022071d9e89774682c5cb84897d5c406aab779527e759ddfac473c900add865e420c", - "id": "ef7a402d8c58133af24b24b5ec33821e0b443e680144a20b9ae83ac85ea2d16b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 104811009865, - "fee": 0, - "recipientId": "AJNitjA6dbbiz5vDaQkaKSXaNiktVFmopp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100aef19847d686fa0e802b7eccdf3e302fd9bc3aacc87f6ed76ee2ef0d5abd0c570220248a00f81756031ad2b87a6bb367e7e6cedc11b4412ee3998db66972edd35166", - "id": "1d956208a3979f3224a4dd347109102afae6463b060bd94957440442904988fd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 105000000000, - "fee": 0, - "recipientId": "ALrkZgaD6BBZCLx1FaUvcFRL9BkTsjXa6P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eb10d7c5fd4b9c27aae44cccefec7315600dbabf3cbb180fc66e996ea2856783022037c60b02b33ac06604cc39077d11372ade6e1a66aa89f99c43b6fd0f0fcd8ee3", - "id": "1d10aadc7d67af4ea389a2f0163fc174f261d71ca6c80c2b326b674858948ed2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 105000000000, - "fee": 0, - "recipientId": "AZnonaWAZjaGFbku3DLoeGwgg8PCy41sMj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210091235be3645f78fa9d77d45882767b7280e779f3532defcdf0b71eeebc73bf63022050c9631aedf3c42231ed1fb9f385763c370ff1c9faa6c14ce07d14500fab3d0f", - "id": "a65fe81748277af637a844a6a21b8cd57f4d1dcbe6aa58a553a94cfcf0ee2650", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 105000000000, - "fee": 0, - "recipientId": "AbCoHPSKn16NSKL58h81seENSohU6xAT9w", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204ee2fa79681c3c86b4107e239596087b33a0fb6108e2bfe286598618945c859902203a32f45dd2538f1e49eea075fbda05db26f5c9cfb2b3d1f4aa85117bf4a92149", - "id": "306de8162df8007e47f95095c1386c4f838d32bd6072ea1e740e27720649058d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 105000000000, - "fee": 0, - "recipientId": "AMng2ZJC3Gh2ykVUAGUeKB2q16WnaqK9hF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204d0818688736ad3bb25e21e4af2716b2038a3691e2cc7f27bda2df761df4c1c802201c2fd04143b52f2b5e5bc1d3f8c17cb038b90eebd5282ac3877012a874f8fa95", - "id": "1a64cc2367fc2d05a9fc862cbb99be77166117d5ba2ec25907126561b2d9e1ed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 105381567713, - "fee": 0, - "recipientId": "ANzYwcwGfMg15SgWMkGuYPxjePuXLtvuEG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220750181471cd14e07434ff8f59022127b7ebb36b643aaa1851142144fd6d0bfaf0220268280752dd52b66eda35bb6384e6148135461ae865154b175b36162860d71d6", - "id": "e4f2db2efffd43d8a99a2df742cc93ba40d3e97cc4e9cb421f9ad3c85a1d5e19", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 105714987355, - "fee": 0, - "recipientId": "AcJFXLL7uS4i8x6Jg2hZQftgirzmFt2X1t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200c0d4ba6f603d8dc3b535fb8b442fd1c92c2d7deca82c1aff622981874563b0b022043f3c8a59e042b847b1359f1a28a34b44fccaa042c41947a1565ec2d7e719167", - "id": "a85f33a959089b825b8453138de87a63137c1fc61ab1038f3e8202d761133c31", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 106221504166, - "fee": 0, - "recipientId": "APk1d1oci5TiANq2XcdQjy8C8iLwmtTf1G", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206afbd9bc04c90409a31228c58e89f493ce384d7dd97a0b97412a4725d7358ccc022067b1f28540632ad989bd7458fdcd23e5897615e9d9017289850277034114888a", - "id": "b9cf7363f5730e0b1ac4ce9114ef5eee22797016bb718354940fcb53cab874b2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 107239514002, - "fee": 0, - "recipientId": "AGLvTbXCtBAdCD7bvAi8PBSSvRexEv1upC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ddf53eb746d313d24df80c2edb7a3d01b533a9691d05d4c863afe390ee5a82402204de5576afd6f9a54ad7f106cdc71691acbe16e206914e9da123709f9ab78ef85", - "id": "84b3f5981b62693819aaed660e3eed3e5f13042449234ab73a5870e760a45c7d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 107692346948, - "fee": 0, - "recipientId": "AR8jXLCoRWnnkaD78YxajVcFsTv7eVutRb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022077094180476f993bcc0da365d9f9667c8724c5d07402689ece5f870af7e63b2f02200faa17f80991d65bc3e8a2593eb2ccbd7b12fda29dc3fb856ae0d222bd56113d", - "id": "dd832724781a028d0427b13d14813c82d8e949fa488e9885c872f1d7e6176642", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 109100000000, - "fee": 0, - "recipientId": "Ae8ZPCEgJEUMkR8oD1Pp9eDocsGE2MRtiZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022040ea35b1906200719f7b79139f0bc3dcfd2d1f34b19c3e75a4fdd1e3ca84b60b022008522e9eb7dde5ec65fecddb318added37afed341c66d4c3f938cdb539bab0c3", - "id": "ae1127f0c65bc53762bb7b5e79c3dd2f0da85fb201afc5bb1577b1fcf371c31c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 109100000000, - "fee": 0, - "recipientId": "ASoz5DXAEBaQy8H5KrYWH6bzYkJ3zHVSsx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b4b63ea88288f994776b80c78ecb41793cf70fbf72e3038acf73099c5677be9d02200358cad66ae2d98a08c43be0c73fe88a6ee869be49d0012e394db3d14659c43c", - "id": "f2d28d17f885481487547831f316bcf701a1be0d2263403740dc0516293384c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 109100000000, - "fee": 0, - "recipientId": "AaPUG2PrNDy6UVUBkSwPuKvKh2DSZdnDqe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d7f47c01e28bec5afbf49a5cbfd68f4a911a4e840b624e13abfa4209c2e67c102201fcb385bc4fd9b55c687e5a253b2f181a658b7fc462d8830e63deee0c82967d7", - "id": "0974483b28113dc2ba87c4e8caa564f71b5a4dd10167252e66124b02fbddd7d9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 109100000000, - "fee": 0, - "recipientId": "Aek4aTvvdA3hUVUjhgkDtPQF5E2iBuAVP5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a05c9fc68f17c57f5adfc88c3bef13e616149a53b65a6a97c95d8f750a3251ad02203cd2aeed88591b244df1c05342d79fc7a5c0da989d5b024bb4c72582b28333ea", - "id": "cb54ba7a6a747c5d20e66648e3a7d12c6e5193d0ec1daba2dbae6b8bda726165", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 110800000000, - "fee": 0, - "recipientId": "AL32Ure9XiVh6emxgHdLQPRbLbr5otNqDc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b8d1cd4c228ea834d371edd3ee9c59053d831c124edc7588cb358aaf0639f93102200cbfb5d6f0cb5498709897a24af743cc1a80a66a948d411c9d71ef18394872ee", - "id": "4683c51754c198ccaefd949f1becfa8dc36383bdf4f43b9a5ac3df0e848cbb01", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 111541749725, - "fee": 0, - "recipientId": "Af3iDQ3qYG53LHVXgGbQFXkffa2pyjdEFj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c57f7ddf68a0d08feb91bc9d67e9b246bdff6d8fc3dc07e3524cde453ad60bb9022007a08e49417b03c201aae30e9c6b12360c1e5e92b7ea5e72cc3b257d48578f54", - "id": "df3260c10be2b53c385cbe025e84be776d6f9cc5bbced87a110ba3fee4ada362", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 112600000000, - "fee": 0, - "recipientId": "AU1k68nmfcpAkTkday2TZFoKuttD6eh7JL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3043022004abba6942eb051d80c8faea823133159c8207251f82bae328b0227b77f92097021f1580e0ff6214cbec6d97059469e5f498b39c25873c889870d0450b1b892be5", - "id": "c4b480f9254eb4290b7acc61e2e85bd2131785e482f6e617b8c3a6084773760b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 112600000000, - "fee": 0, - "recipientId": "AS3SgCu4BQ4VxfdJyu4HKNVHZGTQBP6bTB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bd132234f55b6c486d34b3fb5b1b52558d45938ffbe0a02e2ee06ba9fe7673e802200f61ad1ae7b20468c496c0526bf726546eae7907387f72f85245e0c7fd9888a5", - "id": "66b7e7ae47d5663ceef394c8cec8ddd1851bb4034c5ad133c1b4f6d2b18b43a1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 112600000000, - "fee": 0, - "recipientId": "AWepJMLX9wD9nm8RqagQasNfypVX8n2wLF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201dbf3a7b09b8a4e71db49504e3cd5bf8864a877218bffab4dbce5a50b199cd1e0220452ebc9b95206fea3629f11abc6a40a6be3c0738bf496ab2215c7033bba185a3", - "id": "a94b2af091d991840007d2644ece123f0f42d18276237f163935d2df2e075685", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 112600000000, - "fee": 0, - "recipientId": "AXBWn3Kf2yEy16mYKrwnTQsRFL3geunAxh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220663a6cd1bef053f3376b19778d7256d08a34a8d8177e9fc36b3460cacea75c4602205b50dbdf88972c8868d15934c2a466f7cc6b82234002cd08391ffd13d173107b", - "id": "068b774d4990385eca9fb6e1ac6726cf6eaf99fd3687b35271fe407b33e9a784", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 112600000000, - "fee": 0, - "recipientId": "AYtpgFgBb7FJL88VBShq1GtBLoGnYBzLe2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201d96f2acded9f3d609618ce34f387c2bc46402e7e14c7bca2592aae1e00562800220593d7ee4b3f97caff848c38b497140312855d9df2f007b8432c69b43182462e8", - "id": "df619578c2c85a4e2a3f9f41c7afd5992439c5a60f8d17eb3ec7a9ce8b23c96a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 114624967068, - "fee": 0, - "recipientId": "APR5muHZocUvsHqfXt3Y3QDuYLHHgiRnov", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa7873fc955e0c2de2a6591ecf38fd0252b4b25f83c92b6eca220dca2c593e7b022007b86133cb60ece69909502ae21d7a5a0fffba04ae88522ffe15bebeba2882b0", - "id": "877ce4b295db21844c7dfdbaaa08571c2a376400d60cdabc9af1eac7fa2b591e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 115000000000, - "fee": 0, - "recipientId": "AQVCqqgTRUDR5NMaBBDNxgdpwxkZbtqGxs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202927f829fea3a19b90b2144407b9a268daa489ab146c08106be90c4273220b960220220f2de203a334b320f9852c527e55b8f595d3175e6b78c2650c27487bcde63d", - "id": "0674ef83667bbf5c4dddc21c122777e76af7abb6ff73337b7c1f87231573e3ea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 116000000000, - "fee": 0, - "recipientId": "AWEHZFuvK2sucehMNho7bFgs5Kct9MX9cM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022023e9e21d3126edf7419081dc3d8ca781cf08b4f7a6250536859c4161d848799802207e4a553cc1b629c4727b4c3fef627e8fb592e839c80997dbfe631a08313f84b9", - "id": "aaacbb16a6a80d35c49796dd24c5ec944fa8e78323f7df908fee895a651b59a1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 116329005231, - "fee": 0, - "recipientId": "AMuZTwvJTd24XdckcZAi7Qiy3pMoitZLHV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220523323f28caa94c93362d327129e8d3c4088f9c577a77e9c905464c5ebd04b1b0220705fe7ff0d17f52e3370d10be916af09ef1883614c76eec9899adca018888445", - "id": "7301a48380a2745cfefa1cdf1a2ecc9edbf70e619ef97440092bf80e8c392440", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 116897017953, - "fee": 0, - "recipientId": "AdFmnvX2noJxbAqXZvKKCRV8xewsq2L8mU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a61b9e09531ab70652d5a188994c291247b3be964960448ba1fd0d21668d7f2302204d856caa8fa3ff06e8b91c69f5dcabc7c60e57d8b342205ce03233682b109c06", - "id": "55242c764f2a4cd791545e8b71cf09b205b99663760874500a814a942070939e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 118387830724, - "fee": 0, - "recipientId": "AGsD1iKAnYqa7q41f987DzMS9kVmuvMB4p", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b982e36000e67d68c16636c2f869d138f69612e2a2452584831d807bfd7e2b69022061eebe290b68359322fda287642ca41864a581bf666b74f998796b7abf418c54", - "id": "9c19c864bedeae508eadfb014a1716c8f6af01bffa05bde44a35e9400fb441ac", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 120000000000, - "fee": 0, - "recipientId": "AbToFt8MRXR3BmKcC6EpnEPWaNUMQzr1hz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d94cf00a4b6aef15f3243dcb3d68afbad70558b3928f096622eba887da94af4e02202d306ded93a5b9e21cfa7ed9db16dae8b78ce51586f4c6be5dd0ff69c379fdcc", - "id": "fa35c7b607c02653d4c3856476100bdb65b0af57df0864b0173da3fd370d1bf7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 120000000000, - "fee": 0, - "recipientId": "AHWWoXDCa9RSeB5d9BCS2krPh6TzxUa9b5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100af2fbcda194d173772c91a90ecea2ba0ae32aa24dd7a119a5cec4b92dd348a93022073dace8e3c3fb62051bccda308c1e80650e39ddf6db74cfbb1ff554fe5dfe773", - "id": "d96cef527759049343a6ceb34e973a6e873cd89ffdc0750dc94a9b8b11602c38", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 120000000000, - "fee": 0, - "recipientId": "ATqYMZ7zmQy5f3DFrPKaMZjiPVpGAjCWae", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220176a3dafb595afac283608fecb966d35be5f4ea746ee54a61cc837732b368f3d02201df396e59e4947dac9c31c0f6e3596fa366b3d6a1b537a04a3d39bd5b2705911", - "id": "942882235103abaf544d21ea54b7cd466fb9f0eccf13db858af4cf254444cc0f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 120100000000, - "fee": 0, - "recipientId": "AeEasb3UFnht7FJiYqFi7csnAavnTwxUHG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30430220608fd2db9a7ab9160e8071694506c793a63ef0b3a70970488358a661e80e11eb021f6ac85f5fb747484f5cfc189b8cb75e2ae8a013b9f9f2f37b96ccd1a5e21f01", - "id": "5b51d62552fa2d44563a09c0e6b1e30610ace3fb9721fa24499cc19d6252f8a3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AR979mGe91DMX9dUPsKvEHhru5hc2aMqn2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d97b547f60feb499b52f9b613366eeaba66f8f2675d7e1faba52dee39e3142810220617a00281f0c066eba13a80da45ace50b6d94766a50d0b975563c2e1617cb1cf", - "id": "03676f15ae81acf829753f2b3ac30cee6d4749a508ae32cf104124b9a4f612c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "Ad6npQratXMq87v27PgmpKfK9aMvmWTjSs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022065eeb3aeec85e090ec09e772ed5d036d0e7521f09db0310e75622c0bb8e96c87022061e5b3ed0e12e3077522abcc5d15cb07a2d261d6fec0e59622700ec4466c89ec", - "id": "1e2ea66e2f12b91bb85d384b483f8a81c349adab4dfd2d20fb1d26346e4ceed3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AMeyqtTonCYWt1mPJwd1K2FpnHcPjCVznH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b0d13c6cfd44a8e186e659c23a2af522cec69a41b8e28ea6a6131bd12795216402200a0a212cda71ddcc26629b53d7f1d6e455b99e8fe4bc8f4e3ddc29839316a82d", - "id": "2f27ad44ec7b4c1b612fb27a23a4d77e9309461f72c5613c1ccf60db7601e833", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "ALvhge1cucXrjdpnMozCDaWM3C6gm2Xm33", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022059d6b72bf48d119e8e18924861ccd754aee7e71d7d3010daeba835d2b33daf8e02203f0dfc75e84aba60c389eeaa7b8cb704c5768e58d0c3e066004ca01b2015120e", - "id": "b46a24fae79c4b7b18b368b357785eed4efa908b0170fc0c955365d9a8764b31", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "Ac427bp1DynWsNGXCCYwWXseyUCYMWr2CA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100df366d759e02b5345ca59b52de2bcab1906b78729c65ded10fe89c6e0daed4c602205821d77a90716e943ddf58c31392f902c7777e3dbe69bdec5198bb2796c17a4b", - "id": "f5c95f62953e9c219850a8ae44c468d3a61fabcaac19446d99aefdf72714f32e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "ALHBQuNjrFx3i3PgCz1QdkWgAEwX8zQ7zy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200c9fa69d5adfc7f6c1a6dda8dbcaed58135738eb7e20975bf457cf288a4412fc02207be43c5bbf745b851bec3b076291f3800c54388da527b24853363b0c8144f0da", - "id": "190f5a757ecd8332701afd35805f0ab1ade99cd1aa3a9169b0fb9f48148e6cae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "ASn5uzMgJxu2bu54HGiKE8D64ZpkhXrPcX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bfc41e8af18b4505ef221990a17ae16c2be5e9ad554785523fbcbfd5393f006f022017978893e82ae7232da47b6ef37334bfa255c94cc19e9dcc8de51659b4bc47b9", - "id": "2e80037c72f52d9caa96bc27fa934e662be2497852c6162a50ea01b2487c5b4f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AYmpwBT59ZpMKJCUcsKxjCUBYfGb3i4r7P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200b5a383750451bbfd892a9c444c41a029efe9fdddb40acf604851b6cd623ba320220129abddee1f38206fe9b688715471242f10ddfe99fff3717bbb0e17b5a43c071", - "id": "388d78941ad3978004c95a6507737c08e6b2840f1de9e054c215a1c800827c16", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "APaWSMFzp9gNr2RRqwdGzMdKrf5rwa3dNV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c69b404bda50b119d33763cdb23e3b69bdcc387668458dd33253955247763c5802204fc1cd9a805cca13f1c62bc706393a2f2e839a696afadcf528ca93177aaba237", - "id": "1b24b82d490655834aeacee9d6af2ec815bead657ff6405bd6cb29ab19faf737", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "ANo6dapr6rCMDTKjwRzZgWUtQgbbQ1Nnz4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fd106e25de0bd3b3c1e6cd3dd1f6b49968e482fb015f00556ed60a3853c2d576022050e02fdee6b72fa4c6027c17f8485e395391b9a342a6e212b064c0cc27a47aec", - "id": "96834d445ffb6224915b81236804f1a6f4e5231553b4019d5b9a9806afa632ed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AWA6TybTgFiesGDtCasa68Jt15QP2Auvju", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203bc67263cf58ec7b90c524dd694b06184f705dc7a206b1e1a1a8fa1dd29cbaf1022049410d87332f9108ec487dcc2d4fcaf52ed36867b05e4b4f14deb94006e94802", - "id": "176a70f81ae2c02db9e422651bfb9fe7430639d86144b94a4f28585533403cca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "Ac97FP6Zt5mUcz8hRWRy8QgNRsPCxs7BaL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202d1d32121b5e7ec0fcabcf04ddf8da303b4e3b5ffb55ab9ec037509f6d4df29902201526e223d63a19013b7e44fe44879ecaea07baf2c45ddf13c1234ed9c5163475", - "id": "7b50f54fb15fb3cb7c7c2e5b1ca1d241c37d3c10db990db87534ce366ea94d3f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AJEX11r4ECKF3r14KxNMwzLT29omPCtGHA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022035208eb90354b15269c5b1a97197ef357799111600bbd313afacce965838eae10220036a346f41e67380824086cbda241e058dab979994c171be6b9f633ef4c0300a", - "id": "35242f8882db3e61b508761316879c32fcee9a5677bbbf23c76c731cd68b2cd8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AQ8PPGdBYLGPAiUjpo64YAWCyPzsUEEBvM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022025cbb2d13fbc38c3de8cd8f134c41eccee79f133e78c83091888e4e1460f998402201abaf01c324594b4a3e2ebacf46c80ae6609ccb8dbc915304d0bb907a08a7869", - "id": "0889d189eb144fca050638e158ba32932733b39352998d2cc9307b4cfd5ce2fb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AKhaX5DPoz59va7cMYhcUsBEGLw73S7524", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205c29cd985cc57d5dfb9cc47619030cc11860ade66201f73db301a411dcf8769802205329981e063b8f671cf6584bdf65f6d9ee12d304d4b167399d913e1e3fa3a8db", - "id": "2edd2baac328384231be9165095f0553378679aea26eba5b149e3a54303f5fae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AN3u3wzJPUEbKkjRTW7JGnZwHj2U8ir1s6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e2cb4fb8a510d122ff54873ae69f3ed0e05e5218f16caf3e10c719a46c1f493e02201c2a6640e6904f68a9ef5375bf4d3e0a568a558a088d68087071f09f34206d61", - "id": "85d4a553ed41bc54fd93c81ec5021a23088be6f50aba0055270e382acefd98cf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AVL35iLjeDYJFrV3YK5kPHHijJcHbzGqv7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200acc365ced6b1cbff5d6d586f373ca5f9a193a20de02ed784c2ed0969bd33a27022033ed50bc892f08d837f3e38ec8c3d4b2cff4ad2ba148712667870d7615047b80", - "id": "122ae3a92b89b6c5cb1234075b7e5415c0b284c4ccdb66a480cdc47a6f45591b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121200000000, - "fee": 0, - "recipientId": "AYxAEgAvXoifkzMsN9P8nmDGaErUnGz6vE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022079f8b805e20edaa4f0ee8e1a1136ced571a1a964c49f678ea4ac945c77aa40ad02205f03efae06c98fe37f8beb0e730a0f0b6cb5473c70fe0f5f03a33e71699b4816", - "id": "9d1bb9a20498867f18e4cd28d56b1b6d0330c78da178d3ec2a9ea776d657300f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 121482157847, - "fee": 0, - "recipientId": "AXdk1YaWraKLT1he2N3LW4aBaZLyAiyiMW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022036f7275a99c91e73aceb5d904bdd1aff19007fe45aa3c1d926170e9883134730022074df6932914e893362a8f6473bf32e1eca016efe823a02bb9fa3d9ab7f64c6a0", - "id": "6676dc925e0257740d79d42f4963e30cf1451dfe23c282732f9b2a0917e7fda0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 122958047810, - "fee": 0, - "recipientId": "ALHwE12mF1DbBNZCTxcPRB6x1om2bRnQxG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201484a2b3a9bf798e204b01eeed2d235e3dfb11f9710bcaa5c78eb984c5a4cb2102200cdb0f5f44d445cd495f20b75707927abb3115b84fa1c95e12879f36e5d962c2", - "id": "50e3e17e80a61446fb6a9dd016d3ecd76d22014721416802fbb8ec212f732255", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 123284431502, - "fee": 0, - "recipientId": "APMwQmexasWJwpAyLZoDFYWFSGUamtNK1J", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220310d6c899dba99be0221deed8077220becb9da79118276ec3cab9226988e9b72022048bb5ea38ced208e1cb6d065248a390ecafcfc6aefd19b8f439c515132768603", - "id": "817ab6d80dd709d55baf1d885b47d8f83dbfe0009f8777dff77500745797fa3a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 123492648017, - "fee": 0, - "recipientId": "Ac33gzmvjrrbo75WJ6WVuuMYchi2oSG2cZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022011862523ab035e2600fc17663721256caeb4b54de512cbb7f22064bfeb6f306b0220586576a37ee87ce14575e8e440aae35c6b370bf8ebbb3d220337395e4da3a127", - "id": "fc0ba615203b330acb7946235f7f4bf01739cf6847e07be0d23d5218ac106d03", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 123626298069, - "fee": 0, - "recipientId": "AePe8p3H3wqiUUJ7N63aR4tSiSkxnA8Li9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022023c78ea21b06e95bbe80641f27a39ded89ec17a2ee2c8f5be89135ac4098a464022058ee31b7830ed287622216e2c47c34c6fb1929b72917b3f95de4c3e1cab45083", - "id": "5776ccefa14dca3370b26abed21365f1ffe65228a6bf5b25631cd39c461d14ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 124700000000, - "fee": 0, - "recipientId": "ALL3T4hBL5Hy6N1ekevdHuK7c8SCoiKmSy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100da7c3cc5cd62b428639c066eaf88fcf3fcd60b69b3a6988d1059868aced0a0b602204a2f5da3d7958c04d3edfd60bed8ce37a862ca10238c5175d49f8d994f4d0462", - "id": "b44e7fe2d2a0491576054ce4b6df112eb422691517c896ec9000e642d057eee6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 124700000000, - "fee": 0, - "recipientId": "AcM8Mm1xZLsaQ3C1UrpKBGETTWL42n74o3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022051d19bcc61978325c50456ad68fcba62f75356d48611b6922b7b3386ecd1febe02204e16018952ec4bd29530eca80eb7d1485b279595524a69c77a199b7795bf52b5", - "id": "dd34a901ab4d46ed7f9e4b0d9e4ef54e36e412d1e0169c63fd5e6479c9f49eac", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 124700000000, - "fee": 0, - "recipientId": "AG9kM2jLaRKeTdRiDBnmHKFvEodwxkyns7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ed04ff01b76d87858b06a9b4ac1fbbf452f237f3c89291ccc2dabf2ca2eae7650220307fbb2d586cf040baedd8a96b0745e7c5bff914ec9a36c6a9ecc1db28edb430", - "id": "f69027b2324e43c68a23e9a62a24689315571342b63651380d620e0d747cd561", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 124700000000, - "fee": 0, - "recipientId": "AbJe8Q4PeWXGd2hurMguwVe3BLaDHfWRcR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206c292d5bc621a09520136d3763e2ea99994c92164f083317ac7d4d84ad67c9a7022064fce43058f8d19cdca3d633fcc34429086211c6947d4deedec92a96f5d72e7e", - "id": "d6b27e61b96f4a4e503454f052fbf313559f9317694aaf0e3b17f9c12a84ffcc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 124700000000, - "fee": 0, - "recipientId": "AKm4VdRbpqQRxNUatCuHz6SaRGTatMGUsD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dec71fb87031a989bc6dc3f048ee308dbc97ec349a45861efae3459b04424b2d02207a5e5f4a089bd56368ce0cf6a83db247e9a8b215c0aed867dc83141ddd81894d", - "id": "f3cc88247afdfd0a8cab61e339840363c7353287cb76043acaa613f1ee5e8555", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 125174485431, - "fee": 0, - "recipientId": "AUYnvSaH7v8kU891WuBGDA8XraJnqfPft7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210086f778878fc1586963bd8994419927a9a1f6c88f899c5f4f19840b2c42dc559c02207bde1d3bcc0f1faf3143b3845f6671a90792bb2e3ea1b041ac9d80a5daa02615", - "id": "2801d176b91ba63de86b0332a26346bc1e4c7a8a3a967473e18c09e559965121", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 129540204214, - "fee": 0, - "recipientId": "AbnX6Wyoz3d1LLBdY4pkYmRXvkv7qq57Nw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200d2330b9edc3faf556ae66e1c689237bb630dea023ccaf3037dd706e7a9e34b702203ff844c9710095e75fab94dee0d6f15a2c233a4c05e37a593008ff36bffbbea4", - "id": "0a1e802ba4d8543ca30911f77fc551ef666486b36678ef2132ecbfee416331e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 130000000000, - "fee": 0, - "recipientId": "AYxWmuZJwzYkjUarmquFoHjG64ityMAwTm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a55652d4a247e1e536a05ac2233c5cedcbeccf6e8b8e701af2bdb07b42a72fee022058737e0837e904be3c969f81e9622890bf7593604e8ef6ee19aa592ecaed0088", - "id": "3efe48cfa53baaacd2e25059d104d656493d9fd13eb2e8acb508db55db7417dd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 130000000000, - "fee": 0, - "recipientId": "AFyuiPD8r62cdnrrAb8PHdiom3ZgUZsFQb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009f3ec0907177655f8d54eb4eba7f2c96a3e86f239a914401174b45319c482c99022051c830003a1111109f2f726fb888107ec30c5faca8f54a34a078c4831e62ca0b", - "id": "7a58e80d25d0d063bfa357729ce25b66b833107b74beb953899a4724e981d47b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 130884887397, - "fee": 0, - "recipientId": "AGDxH1FYXf3XrL6nJ9qhwmGeri3M8hg7dM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201cbd29a31f62bb30c95fda9d78c8d860fcac3939f39fb7b5638257d429bbc69702206cec558ca827f47711b6b0685b37a571487840d013dae31e95dcaccc31c0d82d", - "id": "218113406ee93b29cc9cd8ebd40d51df6eb6b37dab860641087fb2990dda7d88", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 132401763390, - "fee": 0, - "recipientId": "AdFxLwoJ8JVWW3zpPWkvcpTjbVtawPhh1L", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202cd5cd7114a86e5ea04746d2a44b1daf598749885f03cb8b69dbcb95e8f7591e0220062c9951b5f1476594c34f7bf9a47bb637ea83e843e46bc070ef8f80fd20112b", - "id": "21589db4eafcfe26a65312b727f705ba20dd4403d8ee7fc09dfe4722005251a9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133300000000, - "fee": 0, - "recipientId": "AVzze8R7jrAugNCcUDpS6gvtH3jLTBApfK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e575b4d782d64bfd7b3a5208a9b0b36baae254418f704ff71a519be9e33f4504022011766b87c099d21616072efcb5916ae72965886276837b08e8737ec00d20f691", - "id": "f0d6338a6ae19444416bd44a0a194c4dd6043950bd0488187c2ae522f0c9cec5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133300000000, - "fee": 0, - "recipientId": "AHiw6LhXUDksaQkQ5YEys4bR3KQUpShuhK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220336d5cd425f12d9aba380a3112d53d3d7c8f64adb9ecef94f69e6b42afec87db0220521a58fd1b62eb89abaf64b5ba8e79493ff4b146fb9877ade991119b4d5096f1", - "id": "01968fa393539a33b61a61e9b27f6c666dfc9a75618c5515d4fc0ae3e5e2f7bc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "Ac9Z3Yq7xJeE2Vt5QcRPDmuaTS1FtypjyC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c041ef867527f600f1c23510313ea3765322127147262b2137a7fd6ff8e8ea6002205d20cf3486dc1e2604b447f5cf48b4f1463c0fb8e2a5136d4ef43e5017355bbe", - "id": "fd69e79e125889c7e18e4ee6f3b9b6a6833185dc560cb07a24f76523911d9e87", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AeWazLDW2wcTR5tDFoThm96tZFVKvyg3Xw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210094396bc42de431678812ed874f45e95dca0c9a28a3e67a48530f271fe1366c6202201aab946ee2c25becde260943d413a6b70e800ab2ec02bacfe6cff415c439a7eb", - "id": "99cbdf7999737e6e3ab6f3db168106e630e99e78c7c7f6610acc9cb77d9b74f1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AYKHBz1zZ1dQjpfBu7jJforx7wqX3MSw45", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022066f06afd81dd1ae22a41c79ae0f8592333f9a1f40d0dc09a91fc81556179ef6802201c0fb882cd5d81ef77ba6b1bca9ce4a52143fe2cfe9c06d0c4d1b6737d27b42c", - "id": "ac75980be3c16d88e18006a739c0405ba62ff7e4573297509c22799f36648090", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AGytj4HN26YqbqoYy8bVyAkHr5DBKqck5S", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022000cb76109a01edf4df270e3a942744877f14f08c462e21f48a97e8b401d7e0e00220497ef0efaa27f2b0f98ded7c797f897411629d2500eb9541e8450e2ec4a85b75", - "id": "dcb55ec4448b657e784ffce0a845657369bdcd31ea3118c6852f3a267858dbf5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AKv5bQtqcrS1NwWM744ApRXLGzxq8th83Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022000f10c1b56695f77a65301a9c63d16f2c6a984a921b4c43a1087e41362db454502203ee4bb6a6f6fec5cd685966c65e032b9dff460102e35423f16d677a98d4d9a77", - "id": "da79fa8dda0f553b25b8cf289fa917a8f2b3154d16754749c0beb2c0fd541909", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AW3MKtfA8qu3WyuWf3WzZsXyNJj4T9t9d9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009e8e61957950103cb3dfc4dad5e18efe80e1f1e4974d1c1c079122ec080087cf0220209c4741057ef4819de778e3353b58a0a9fbd45013775606e3eaf16f91b4c22e", - "id": "880ffeb2284de4b16dd38499fc6fd15320071d08a0d19b1837a1973284b44795", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "Abe1tPmDJ7a34syYde7XCkdUfAGjPmhNyD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206ee7dc450b5b1e60271ca2172e1e1c26b23d6b9e163bb8ef819e4d7a2cc3937402200b5b7687c82a928ce9856cc9d452ca71e762053135ebd791bf09e1ecf2f1d94f", - "id": "436a0eb389e7ebb2ffb7215e10fcfb90b707f9a3fe08b46b4a08e53919106bfa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AaCJwF1eVDGA3Jy6MmQ8emMB5LKkPFhqPF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c319ac615e8d54a36c919e40e7a6a77e534abbd952abded7533e11a1bcfbac2902203e9a945df2c06505823ea920839528d37fe946ec8cba4c75e1c2bc035d1e0624", - "id": "65451621e425a1118c592029a9b6f47f4635937da9640edbbe2aa7e1531b032e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AXYh1dUhBWumWF3Saj7F8UH46TqKoUR4oq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210099fbf3734fcf70371ee259534f82e2ef84a37b9000b254f7bef53e812afee3a202201c0aed05b097804fc2e6103c996b323cd0d1a634d2bf08560d1473cb19c98583", - "id": "0e8d05444ae15dbb7cbfaf6ea4d03733dfdcad333267f9e7e7ed18f5975bc168", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AFzhTRcF1mdD8bswGM4kjyiAbvHFZ3utDf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a40e1a906abb1198a9e6749f139169488103f6a089384414c228a375c3697c28022045e6829b51d7f3f0ffb9e6f27132aabb8c13c5612d9cf98b2bc65736d3bb7d6a", - "id": "53b7a5d15d2e6c5d4b977280ec050e8bde87040748e5b25901459f139c9afe46", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AHj3b7vTcrvS81U3xpWT62G6C4jVBpXB9z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a226e5b1d830f56939fd2b860c004c54e6f49260ff1400d21509c2e1f6c7404d02201997e86e6cb516c88bf6ffc7ee6cafb855783954fb8e131bd65561fbbea8246b", - "id": "7bf54e1e48b45d11b86e06636fbc78f412d46ae1e71b2dd24dc3474aa6290dee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 133400000000, - "fee": 0, - "recipientId": "AUjtjkgdiyEpuByP1uajKuT2HSeb68iq2K", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220415974bfe952d18dc3a26a966b9d7c63993568f834a3bed5c4bd08784331230b022029b654a8a4b0da40821d6a63dcf40fb325b58579041008ce1593aa8f1b785d09", - "id": "cc3fd2a9da7d622e24a186044bbe5479ce94cf75be168d9e2ce5a950853648b1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 134701272086, - "fee": 0, - "recipientId": "AGyWeMScLefasU8tiewWRkHBeqvSeaAGii", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e75d0bf3d62eea3631a95d6bf61055b0006c0ca0f92007b3b178e4ae82bcf5de022065d1c1c26883926792929c9945fd6fe6cd1054ca716113d146598c669f8b7db9", - "id": "2b80ef415bf5e28b1cc9b5fdd407c3005c498e6ed0d779defb5386f12a145b05", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 135000000000, - "fee": 0, - "recipientId": "ANZr3b2hPsGwTtVgNxpvMkyZZ6Vg9UZkrx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220774c16ee61fb32555f68f77aa42d26f4df6689af47415876105fcc44049d00f0022026855b61fc8975aa07b518e0b4043f63f6e4e10936b25266754dd50f4d63c014", - "id": "70a85c890f419243809d0fefe37b7e6a03f73d859c8cda6f59cd34d1b7d67a5d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 135100000000, - "fee": 0, - "recipientId": "AHY7L1v8LzmNke19ArEgzN3tocigVaAzLX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204ea6f57c6dad7869ddae0d8bd270a4f3ac278bf5887bdcf587ff4f083013e322022032a61c4901116e5714560a5ade681f74b41cb9947426384ac2db55df4004131c", - "id": "a5bb75d5aa8aad7955555060d8d43eaf3ddf39c0b7e43235466870752a8fc283", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 137900000000, - "fee": 0, - "recipientId": "ANAL6hHrK34Jy7v3YWGodsJcb3Egau8UcW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d12c766b40589e3c976e03c5a0ed64a8e44c32d3f0b1366051dd37bd9c6043520220455b383091894c83178c0ff07a45108abf5d442fa44d2dee6adc8b8cf884e018", - "id": "95fb3162c8f7beb2ab5f6fb61666094226db627b460f0d5ed45a76483064e6d0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 138600000000, - "fee": 0, - "recipientId": "AUArTLyiUVrf9h8N5fM4gtv3yeaT7sD7pt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e4f07b7c14b4136b62ba30d4dbb23ef7c34d0b5aa574b528fc71975134700fee022018350059d6011c7c4310aae232b683b8bdfec4f366cb952c8a71573080eeeb73", - "id": "cce0cd6d09ba3eddc5acde554e079cda7dd1a370a2ad2136bee3da660a7bb6b0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 140300000000, - "fee": 0, - "recipientId": "AKbaSMZ36bgW2jL63Z2RYe221JvEKBJRpK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e1e2cec556612a736da0487ae900c979093566cb9d461ee5f97c1cb8913521d022059f3e13018c3779000de42972f514a49cd359f1681a8f389d203483c95defd9e", - "id": "77de4c98b0eb2024ba7df54f734d9e0d86efe0250eb4c38921be96039a92273d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 140332554565, - "fee": 0, - "recipientId": "AS4JC75Lv8avQHSDT8aRu4qWLLzy7B8YnJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220220c567e49b35270e9ad3f4618ce46a1d9b86d8bac6f51f77a561414b1c589b10220287e615bbcf9366ae1cc249f5652cffe72e61bc76dd6221bfb0b9751b359ed63", - "id": "b45e18990b9f754737ea2442fb4ce19f020f9ff1ac5bc083d69e5e24810d0e34", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 140332554565, - "fee": 0, - "recipientId": "AU1Bz7CJhiNYneNYEuBCXPY9WQBwCxCFSQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022030a79416ae581e874e3f85ad8fac4d2c0ac39d7287c92e807452122a4b18977702205b3d6fc1ccddb6741330366926fd70ed89f160eef85bd2dc161e73984bf7c1e7", - "id": "627b1183a9f109a94542d761904f247ee43527810bb5b32c70c6089121223f1b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 141913175258, - "fee": 0, - "recipientId": "AWSYgoRKRpXfXbA3WpJ8eegTky4SdT6dYi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d8aa4af8d9f52b9b1bac3b38217220c0aee778de34fa652dbc9d2df182540c840220687ddcebd64e8b0b02aa4e18ba738aba219e0a66157d373e840ffdcb4e3d5093", - "id": "fe7f90ba3d98988a846a2f943017bd03c475625ec021bdf13799f8cf7ed70b89", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 142400383746, - "fee": 0, - "recipientId": "AHomeJ2mZdS2HdBnfpznUGWKMdnGE2ZyBi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ebc13c20a25aebab60d043e3983faeac70aea83008e8531afcd3b3aa986395e002207e37320b8a5085c0fcabdc8da57c7f67482b46847251beb21ced9096858d67a2", - "id": "73f0d8477a0a9691839f121b8a28b86f9c3a0283e60b5dcb57a74256ddc19c1e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 142597922946, - "fee": 0, - "recipientId": "AbCmk7JqsA2oNHjSxs33hwEwm7i9ZGfNTc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ff68a3af0a8400a3f5bc2fa933df079aeae3cd8a33df0e6d06883b8bae604f3902200618c92f102ae967f90b5ab0bf2f17d561501fa6b0e42069cf958f58f10131b9", - "id": "1b8a6a9805b7c3a9185eb8692d93bf0130f0c090decfab0f25f33748465c34fd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 143671138618, - "fee": 0, - "recipientId": "AKpcMURctYwUa7NFWwQbHMPShAmGvZxTDk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203de99b3f8f7fb93625f52d07e75e9735674757e5728a0833e282e21887c399ff0220665f3516be9e94ea507964890dd5d1de7d0829143401ef78439503742444cb45", - "id": "ef58d87117fe3cfb73ed87681ef9df9144ef889a2027ad85f341809548a7d69b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 144747007068, - "fee": 0, - "recipientId": "ATw5aBDeZzzxdz1VhJH7zjYUS5c5m3wWcz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022016e3810651bdd50d22b4240aaeaed98bc01b383990a18711671e27fc1c2bfe35022027628ae55345065e5f4e21b462408a9845cfe2b3d9666506017fda87ac6f7e07", - "id": "d068d9b75cf07209d53076886c817de5d449938fe3fcafd31828ea400f4fd44e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145130623474, - "fee": 0, - "recipientId": "AeReN9UHpbqNqA1zRP3My3iQYYw4vBQ85h", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022076fa46b96abcb0ac7b0b0b9b5c8870269edaa7b4a3ae4fe75585837c73c1194d022004e5e214749129ecef9fcbafd06325ec4645816366431199253fe349af14bf86", - "id": "a784b1ea1393c00ac0c8e58fa19b9e137f1e8611a4e8f81f65660dd36b4f3882", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AVgLdwCZLBUVRr84tPzwgCPLcdDRRTvkMd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206d398c5a3c63f66e37216a297e442d8eb490c67bf99cc0a98c5680d9ddbe2933022048170d0c7dbe5ef0a30baee4c669579c1c414fc12cca94af65adcd40e90e2d39", - "id": "0d36bc9b909f51dd5bed75e5022f632bdf8707268e74aba53f8d7400849b49a4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AVroDA7WnnpRhsAy9187e7mRs3c9p5ADoF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e8248c25c74669b3074a5efbdab29b715d4d796bf1b139917634cd27b72b1280220184b4d05880cabc910f372c43c13fe5f3db2540174993467dadd0bcab4c84873", - "id": "6277e6f92b5b75671e07fd26b28a16a82038447cd9ae5dbc4a8a525ea2969204", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AdQNZMDMYsqUhuZ3F5Yy5FvUsdRDSupxEP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009b106d35d40c34def24a3af078288ce38a5c42374086bf13c1d43a354abd4ece02203797da879995e88f71e7a0d04b65ab17f80d073d0cd015bec58f1f4a5bac25a6", - "id": "a76db685b5fc59e38d3e25f7c43cadca9f35dbb079c8b5b73bc8da3066b501cc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AbWS5yJ1gwx1GhpaTYDQvJSy3bCFbA2vpo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e1ff4b27c8fb80dc2427f493822b570600fab317cec5a5cb53a58e10769b6b202206a7dc2cdb7785ae00ab6240db3da4abcff95042a33f5c620e42af976925f2c73", - "id": "560771be8dc4d0c21922a7a57b7fb66a9b20803ac8051150cadb54816086eff4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AXfFia87jEauZWU6DJHRbECKk44EXSQmbY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d1ff7a6ec56fbd8fa592c687ba088f198d0752abc667ef957dd2c2e40d9589c70220218d36c822d70d25b1fe3c0ecc1efe85671b09d16765a93cd12c20b45f1fa6df", - "id": "63d81828f4c3e1455eb6a1aa99ef36b241b5fa9dd7c5cf86c92dc8781df88b92", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AJCWEe6hRQRiCC1gLdS2TUSbEwUPHxMJcn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205b1550775c4cf879ec7500f8eb3880a3ec9173db41a7e8e249db14dad79a0d9902200cd06919a283c00dca6cbc647f1f09398dc377c8a4819db6ec20458c55a4affa", - "id": "ec03b808b1f705553a333f882008acb5330f3fbfb87eeb64b7879f4e3e9e013c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AM3Tdmm7PajytJqccZvjBmK2aNzfkmVNay", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cb1a88bc254deed2de2aaaa7165c62d8ac24599cb716cf294fa7650d731c7cb8022040c5516b3945d87eb5bc4101ad5fd8e28c04a86a2475617d43611793c2e7619f", - "id": "58ea9d0e7a38ac86f105406e615eb08a288053f6be10db870a126b4d5b1a7558", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AY1tE8UrGCykVngKVmmjZfes21arcLLyzn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d0b69b396392afd07b737fc4aab7262c5e693a61b4869afe53696e1d72c159d502200de0cb71068602b2552baec25e1d3421c4d59526c01a99bc3475abec5f63bd1c", - "id": "2f2c0ef7895193de3e7405dfda9ded41aebf2aa0143a3870f879800ff3af4a6a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "APAFtJderG8gJDUr6avxr4thm5a2hrYk6q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d43e939b9b3789b1673ff939519fbcdbfcb6746688d1309318b2bd12f839c8e022009e186c21fb9d2963e2c01d863058aee90c9eed7349d3243c900d606cd7f5163", - "id": "d9e039153b8d06ab7b1e3fe64eca9bb917bd7a7c848eeaeda9f8d2b28d24d5e4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AMFSfB5sy97Dr17fqAF64G93pxkSn6mGqH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022005a7f2529d9264b887d323228132518980dc91ea2d0cd5bfed098b8f05b31acd0220676c8d86fe6be5a7fc90ef3ac2db26674377c33188b694e5106c36850fcdd5d3", - "id": "1f7ff73bd0c0b6c42a73499dc8948ffc23a5affa758ca17425d60d03f0e3c412", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 145500000000, - "fee": 0, - "recipientId": "AQ5kzJMSeEEr14YkwoTTGpjj8odwQj121R", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ba6df9e0ac6c1db0a889bcd3c30460b2d5460ddffb7867652dd4e0fb296d9bb70220039f2b0799cf13b3f9e5f4f950afb53c12c72ae1e6ccc15b6c86aaba0febd322", - "id": "39afbf86f83389014c2d00c1b56c215278a7d12f631a10e24615b4c69aab3dac", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 146600000000, - "fee": 0, - "recipientId": "AY3P1mc5878sCMkCFmJdEBhMXjzZivm4rd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c563a228728d67c90cc13501efe0dafd97919ecec25afc4e56c48c9289f0d85a02205c91587eb8001188c7e291e0c851feed13f6960d1828b7fdc6303fa49eb08b41", - "id": "a1ab69f665a37b685d9219b051428f1e9930a4e8bd908623085b1cc5a85a7a37", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 146647519521, - "fee": 0, - "recipientId": "AVwxCqG9GamW79Xb96S7ipWUpxLgR3shoe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009d426ec91319147e8ce9d95e7f4a2bb2ef20ac1a93c4b9a79e0a45b555aac9840220470cc4cd8993fe92f3e41ac71027ea68d6dc5e873a4f5f4042d2f6e03fea61f2", - "id": "6b19fd28e4fac7c60537e63d10d0bfeb1ac28eb768db7c05a0eacb22ed0c945a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 146868042106, - "fee": 0, - "recipientId": "AUALn8exeu8kXARs5HVxay2r8pmSmxMVdq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022038a92d978af95b79d68342d7da3d86e0de7ec10fe793b87e15e889f962f78bfd02207095fdc539244f346eb46a50d00a5a692b0687ff1d6d4597ebc7f2f92c7b3aeb", - "id": "d3d03e03d5bba603521e8be851fbb870e25dee280cf5a1f6fc7a025d84314a10", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 146868042106, - "fee": 0, - "recipientId": "AUDTxFyejQZcR5bNSXBkoQocuoquYfChG9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c460859d8d9d4a18b8c336b5345adf8023234c90a0d2f86836a39ac3b1baacc9022068265214028204e47325d2f157d27c6ac20c0f43276d6df706d4492687b13e23", - "id": "04f2135cee1a37d6854cd0d10c8db0bcc83fd8176833428f8e5cb4b891aeaee5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 146868042106, - "fee": 0, - "recipientId": "ATxUGRxnCNHpafcsTQAimxEygTJEb3syCy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008b9f09ada3004e0b966f2a208bb2c938b797b1973e1e8c2b53b7ad4b0860939f02206f08c4caaf1ed0102430c9b2f382f58e6e0337ef8c11ec6ca0894263b99086e1", - "id": "7c156161c977de562a5613d8b6b955144a56ebdd9efceee9e8981ea639e144fa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147015057164, - "fee": 0, - "recipientId": "AJeAvhsxGTJQKtd7RHBfmNtCy9fPUZYNVV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eba3c97cbd1002056427991b7c33c21336783809f814936183ff030378ed3c5002203c6dd00bfab286353f3565d3bc7be98975a9c8f412c409bf182892043e350719", - "id": "4511231384b91d494dd617db65cf90e99d7be45a761010761b39b2e07cf0a896", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147015057164, - "fee": 0, - "recipientId": "AeyAvcAptKRxirAwuyLei1YLUMZap6RSCe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e30b9369318e1cad7ec287448f11e9df997d10d4ccfa1cb14c5bc16f042969550220716c4d0772ffa77db00d076cea094ea196e1bad98aab377b20e87aea6e7b8b34", - "id": "a033da0ff01712d4b8e724846bc0f524a3931a0e268cbe798a006361a6b5a2e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147015057164, - "fee": 0, - "recipientId": "ASHqJXYyyKCVuhjs761cz5bLm6wy8nbSgn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203c354c4d829f755371122ce7249619b2df5a86e74830f1809e977fb80875e5610220713491f7571edf18f009dd7d0a1d4ddd94ba8c53fdc7a1e53596dcbeaff40315", - "id": "86c30a20741f5fc1738e4f0180b64b585f0adb966d414ac45316a879fd5e7fe2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147165816371, - "fee": 0, - "recipientId": "AVpQfwASwF3WSC5CM7HErLYvYBkpzkTmUu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220030a63d6cd68e0fcac0e2eca89c14eb6a8832d52efa56e6d1a5b772805d1ea480220091c1fc5904423e14a6137f02d153573765905e76d1c4db66e945ec7851763b7", - "id": "93ea7b1a7e4a623e75cb4245a2ff78894c9f63c9ae4438a2b614350b9f65f1cd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147695811297, - "fee": 0, - "recipientId": "AevPmhwSCevsJkXhDcKvaRNQwnG32ygbC5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d748dcbdd417e023f4e3976a7625d8c7f2ad45644e768bc5923210b70b8e535102203ecf2b3f03c5caabde8ab41047155bb14ef36073bcab9a76b1fde86baa8eb21b", - "id": "6cf7722a4793a9f5134e80853eb954b8772ec20f9e7732b109c7489b722f7c0e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147792818385, - "fee": 0, - "recipientId": "AYdQPyMPRyP2jpp813PpvKn18rew13EJTk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c946a710663b006a6065a032f29ae1eb13ecb2864ee79e7443e795a7d566714d02207dde19f663673b881bb01ddcffdc1bb88481b42c7b78058ef722238c3acea288", - "id": "5ced9e0a7ae04dbe285c3087898108f56f6ac791e3650a11c9b6d05c3279f0e8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 147794072165, - "fee": 0, - "recipientId": "Ab2sMHQxXcEFT347HyvsSdXqTwBWcYkiNo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008f73302141cdd6646a1b0c2be14aeb4e40bed6eeea5ae720ba24d482f45ce1fe02203213e8ed9fd98004e859de842345e8a3856b9a1920608fa050211ea76dfd7cf3", - "id": "ef80b356e77a8b3e18c21b11b6ac09b3f89b7b99f33aa12fa53b4d2ee3f380b5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 148338192678, - "fee": 0, - "recipientId": "AVS2aoVuzEnY74iWsgzfzmMNQfSRc5KTzq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c6b3c68949296e1794246ebbc64bd06f9f5d099c5a4498030442a6eb8a78d6f202205f0be315dfac6b7647a33934a04df6c40a2e40dd8a17303f0b20bb09c6bec6b9", - "id": "e38e2053c5110aed17e4a7654650a3061ca5441c3a5fd8d159e2140b20d26f33", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 148900000000, - "fee": 0, - "recipientId": "AeNApTdu2Kf7vphV4H2ZTbopitfEchc2eb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201255ca8ef4fb94fb2a0f9eb089f9f1d48971adcc9bd153835e7df739109fd6d7022022a497a0b8ebc793d25e3348fd884f32cea85189bdae2f66799a3818c15e6936", - "id": "5e76229c1016f43b961ab11b51d4be7598434be225f68ca4347df5106c68b0b5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 149442810367, - "fee": 0, - "recipientId": "Ae9cyMJ32RatQDd1iPd5pftvBjFpRyghhJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210099179595efa579206006e9b6689f62d222f67a4ce4565508d7dea57e3325c917022009425c7807f05f46406a05f02a8cd70dbb6cd20f7ee172749a54eb21f9fdfab5", - "id": "578b56b6076f8b418eb1df12980143f0ff841890cbdf447b7d1d5320785cd0fc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 150899497250, - "fee": 0, - "recipientId": "AZGVDCCTnVz5zye3hsckws1AZkgnUdEwLj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022056e8422f6cc77310114d7e7aab0eba13fbca2cc242a04594af17c8938d7a55c302205d8a258dcb617f878e4b25c84f08d69f77c5575e7c6840028059706635f5bd5e", - "id": "c543394aca3a02fea18e06f89d12e6f170269787be922539139bf72ba173caa8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 151036016000, - "fee": 0, - "recipientId": "APuhkVvG7u6itm8bUEfqDBpw8vTqTZMpdE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008db754d788749079dadc57c9bec350c02487e095df746ae3e196c566b62edfa50220089ecf52f504b54a6d5ec5da34b0eef3bc5f151d8a65598c07da8cf61dc2524f", - "id": "2a5b77ad3a7559a3b066e6845f41c73b368032bf9c125825f6ecfe7f84a48f78", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 151200000000, - "fee": 0, - "recipientId": "AVJ6FhpMSHC7sywQDJujkwM9yv75tyzjrj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202a8ffe7a80afccaff07d8d972d11e8af2396f9912171e3fd7ee869a30262739b02205b9785b72fd0ff1d1028c44d29d9423cf3a22a3e5c6514f86697742777868398", - "id": "e1e3ac19ac46f91d4be3977a0e95de5cb136219a984b03b1782243d0b5f2757d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 152400000000, - "fee": 0, - "recipientId": "AKMDyKrWkSbpU3q6oTxuoS7nr6G5US5Ppm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b12f5c4a7c7e0366f303803b62486f0d6685eb60a873fdb9f15d508c8796af2f02207d28afc1bda7c2af2c650a7cdafcdd6111f950cc10d403fa95132e0bf5669661", - "id": "188f9c090bca5ec2214a0c41ef741aa5b7f3a947cd2cc6ffd51375c837bcf9b7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 152400000000, - "fee": 0, - "recipientId": "AR5yuVPejTDoeHhfbAAYBMCoMPWfT64wS5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207530f550343588e92edebc0c239f9d9215362cdba07fc9fb0a9a0ab5a3185baf02203a151c01fd5ee65b3b90915d6bc0d49a756a687e6e409ae21dc64f18cdf61c8f", - "id": "b5b6f86c2a8dcba704275301e6f78c80fc63a7c3f1e6f80a11e4c1f35e738e2d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 153500000000, - "fee": 0, - "recipientId": "ALhmTEX4c8Cu6j3LmvMy9QRScP4qd5uS1X", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009582b66e2d7e23ee2cd95f4a35683e67370dd60f84bdd4bf13a70b0e950255ce022027d4f0e29103bbe2c7626378b5d92f9976ef5637d4e30201f41762f6b58abcfd", - "id": "d337a4d55a6f848c4b56a1d8b4267ec04379b7ca49d51c4ff12bb4c91dfe07eb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 153600000000, - "fee": 0, - "recipientId": "APL1n3DRcNdNvtGEAVXrZeF8aE3kSje39B", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022026c5e59f6f0389d02d8303807aa6b56a1677759397bf1131fa923da22cc0ff3102200c206cdad36b67bbfafcdc42bc5af3a43865c4dbcde49216cd6acffb1193a86c", - "id": "f855d660cd350500dd7c352d9e1e298f7faa93fe3b99d54fc5179caa3233e9f3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 154067041808, - "fee": 0, - "recipientId": "ANe5pi2srBMSp2Hsdi4dvEQkmRw6QRufWE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b6021389172baf3c7416f95ce39a517a755f44e213c7fc174dc47c2a9f67aa14022002f08e8c6e0109219b258fec7dfbe25e18c5d563a99cb751219955a68ada0e0a", - "id": "300260d69932d0fee55a854007a7c8e16da8ba9a531fef306df009c500e42901", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 157600000000, - "fee": 0, - "recipientId": "ALUZm656LEa4TqfwdtkjP3XFidv8hdFG9y", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220564681998c47b9747919eec5e8d497815a14002b497a43789007d5fcfd1fa37d02205b6097bfe9e3c44dd8a7bd9d610a55d72c674cca6f89e29b37847ce57eb06ec4", - "id": "2c1eb9434e82279b5ba0aafea3b7813e4f024f3386126e65cbbdcaac3e1de27e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 157600000000, - "fee": 0, - "recipientId": "AWtwaGELZxRTxu6znTh3JaxMJWyUf5yg2b", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a87d31acee7834af08c83ca70917065c1a5a413a74d7c4443e316f24a8526ada022077ddd8c1add111bba68f01695dc922f680b83fa3db1edf0e9750a0e041a3c5d0", - "id": "b724ce6e1fd1bc009fd05da5523c735a21300fbe1a4fc1da52cf2c6ab9d66ba6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 157600000000, - "fee": 0, - "recipientId": "AH8dy4DPGzSnyMmfXmJrtyWhUkZoqsscWC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022000f7c8a6f8a8b0bdda54992f15a566dd1c9c9d5d48f03b0205104ccef6397312022052ba2904b07535406df11778a86256ea7893b6e41c91a7889aa4e4f4e93ce7fa", - "id": "a7d0234ea7c0595d44d473619c7464781cfbd6b3be2edc583b624cb9210ae7f4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 157600000000, - "fee": 0, - "recipientId": "APovNoMU6T9i1yvfXczPauvFA79FFPP3Ns", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e8a6834b4380b17f340853bda75c96ee9c553ba2b570402c28261504ac6f11e8022072c7c130180e042c97a660bf1f6f2b17625dc24acc16e03c4b54bfc767ba4e02", - "id": "86f6394cec011bda87c30b080e27320137e936adb8e67a9f9fde6afc84582c7e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 157613342003, - "fee": 0, - "recipientId": "AZd59X8LMYSdKgz6zz64jKxhs6YHUcE2Dc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022021513ab40baf4bd11223c976ec339be0374e8a96ee60c90e499202e2e14916150220229b9355d285561da1b1cb33ad210df305e961ac485e3ce51db39faeac96475a", - "id": "a3b4066f32a764dd0cc8c357a401e91c30be8b7913851aaf8815c61f1fde18ed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 159675898944, - "fee": 0, - "recipientId": "Ae4moTG7YCQ4yvzW7NV86aZVA1SL5jZGUy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220799233668ec57eb2ca1b0c18b433e788ef25df88edd97bdfbc5919e1184efdb3022074a3d4fac5a531fee1ba3a6c6378d5fd080acd163e8e7f983969b1059376e9b1", - "id": "2d900441744993dfaa79989178ee5ed78cbf8dc5a67ed945b2558b0531c23653", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 160600000000, - "fee": 0, - "recipientId": "AeR1SNtu15o3UocwDDh8kBeov2RWeVYzve", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c3ca5aaa9b76d2d4f4916b9268fdedd5f8c75427bb980e7437e5e50e4028560d02207f145bba9763452fd5cf86013c35ff8a71aaeebd0966e2a6e218e7dcfbd43dda", - "id": "4f732a783e8fd503af9584c2116f4d2d9cdb0763c1629f202646dbaa79445be3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 161600000000, - "fee": 0, - "recipientId": "AP8RQXpVHCYEjNaQzHPZLYEARWeMzYAnUH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dea089096a8ef631cd271361fd564a626f541409e3e166082890f5c3d39a5db1022057eaa7c8ca2fc37cac5129c8994f26b231dada2607cf7f3e026bc5b22c9ab89d", - "id": "99aad499192c22c4bcbe71b8c133bef61eb16efb59b9281323e04c6c4b925860", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 161657213879, - "fee": 0, - "recipientId": "AdLtfyiktPxNa8Wf3uEgGiPHHRvBwb6BVS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022074316c70b4b377bf7ede11dfd7e246e947479c62c6343c8370db0f0f5ba9e19702202487a7a8e86ee4970e3bfa3c42edebfab845b42f98c454db3bd0310c085acb07", - "id": "70a2af7bb1dc6807071d4c2ecc193a16ea4adaa872ff14de76d40e5d07c3498c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 162022981403, - "fee": 0, - "recipientId": "AW6FnNuwQGBcJ9nujbPDx7CBjukP2tK8KN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c29213cead6a57f15622d66bb90dbfd8e9f46bbdbc186c752113d264e930486a022006f20912f179934ca1f2f94e0fd43ba5b415666d9949c11f0092de3553f648e7", - "id": "5c75d116edc4dcc35d0ba18d7ad56aa45dac0d9404fbf9f52e857fe8e8f34406", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 166527964751, - "fee": 0, - "recipientId": "ARkk3RAAeZSrA6Q2NiQYi1F5J27yW8Fz66", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220297e1206fd771d82406755d928bc86097eb8126f245e500f9c3f425d5b30fb1f02204b2258bcb30b1a11cde8cd76c81ee20482f870715cfb861c62c1512bc2737344", - "id": "4f3aa1ae966a9f4ea2bffb9e340bd522f3ee33479be4fea1f63d5a7b5eb47c0b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 168221932329, - "fee": 0, - "recipientId": "AN7y5f2nixsdifEkZTcgpnXWBzpiuCkcQQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e460a99b932fe2c9b28720352ea943683be7120993e9dc4bd82bb336167c597502205a46ceac87e8dd5179eb40194229be169446ccdf085089e657b200089bcec557", - "id": "6bd4850be884fccb495c347ca9a9ee5b46be31fc1a0404c03cec0608912be6e6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 168973133925, - "fee": 0, - "recipientId": "AQ6ziotXeWnocAnwzz7V2idW8uxz5V1pka", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bfeb6d49f03a89820a6dd1e82d238aca795866ca245be3f43e869dd1ae5bf9f20220580a0224b2b99b38564cee5b3470f88aa7c7106068e2bde76fd8ad1d8ec0ed98", - "id": "81e4e95e91247b73cd9e2dc3be2a84efb9fb428f04b184f63df43d3c1a9de40d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AKDpBAVgjd4cYXd7dP6khrK2Q5GyuyQfoi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202619a5fef5f87ca49dadda59139f17bc7f388f09340914c5e0b997c1f0492b9d022031a12aabd0dca56193409dbf39fa3e9ee332d3e47fa4656252e161ea90563c6c", - "id": "6c86bd9ad81ff5305fa35d8eec25872292bf417ed456a801bb2301cfd9d5e79d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "Ac6zKra111cp2QccMk6zDNYwyWrYsjY4zo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203677d3e5102ea36a2a7693d8a47c86b3f182aaf7c4c8cb4118efb17eed87b6f002206a17cdfc67917a0c18faedec37baec1be75245eaccb45dc7fdf8ce4cf49b1e5a", - "id": "df692fe1270c264c2cad4bee6383428d2038d6b82d29b805d367690097136575", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AZbfRSargxVS9qk4cBB2BfVCiqir8bsrKS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bba6694b9e169a8b77a512b9c09079d99586551db4f5848a059b18d7500ec4e60220438d0b0f1d75a0621a6a06375ba8a3f75dfcc542d7d13f08175f9878b5feb409", - "id": "c0ca1a1626b404ec63d0a50fff867e2e9f9a1edaa744840c39a58c979862e33e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "ANeUUTh74UcDqZhgxEwMe1SX2CP44MkfZX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022072f2156f00911ae5b22ed9bb3701c89ee4ac9ede3f3b4451911e1e284e21f3ec022001918a7e52c65cf762ace735e980547e60abc3d00df59cf16770faaedc05e1a6", - "id": "b119402b26404e9bbba26df0e59cfbf8d50d41d28d428202fb0b293e19c30830", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AdVL1oAWGLDP5vKax1kfA9izMrZGkaStAj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206d1a108b0dbbdb2c78252fc7ad9a91ebd9a525fc5d6a0c13f94d64a1e6f8ffe002203c04d2e0f607398933e30df73e93a40db9ddc41abef1980e954654a6bfdbce12", - "id": "ffce74f355622c5a278ee320d0d9eae789d9a8a81440815bd919179a5a0b2633", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AFpTaoXGqbwVG2HhaWJK5Vky8QMXCGNxAh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206dd92556e443b03c0d88cbb88f562223e8def85b3c4e5ac40cefd0dc958ae157022012b06f7fbd4551ada6d470d25bfcd38cf8cc711c9af94ac56e4415f4f099f528", - "id": "ea0ed0b1bcb706d1e0b05b818a91691221010f7fece97113227de6a2851dac02", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "ATzVyx4Et7ie5KLrjEUm4iULw9Lm77vgkt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f42364a66c031a8584365c68b926931d1ffcd6edfa8a4a859a9e07ff248a507902201731c3bddd83d1f64f8514f5914b0bba1f385c6ca410241a0c313c5b8b614b1b", - "id": "6aec2e00bf4ff94b4374f83df59e85d0b6a5d05fa5520b021d02c74e9b582e26", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AUeaN1Nvksrkynuxe2KFQcDnE6U8dHFPdD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206facc2f5e57cb6ffe15840f80384f3d386125e186234a6825accb2c8fae4dc940220456e79a2aa1c1302dd8d9437d815582b5cf6e2dcf9e7bb18f5036a1a1c77aa80", - "id": "965f90cc474b98cae2de4369455e85d1d10b71d5f6218bf8029eacbd8ac70324", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AbFmTF7BXLzyHYPsbzhH73fcUSzYrLcZeL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022025a0f243b8cedb3282616eac1cd44a0f15f0d908c75cbe5cc9b0be8b46c4e72d02207463d2e7f585be69268fcdb655d3d23c6f3b50c376b2683d7e2f86cc7a5e1aed", - "id": "c1d0407270d31a4e5a569d340e0e71f3e7771dee306d0c229c2c4badced51dee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169700000000, - "fee": 0, - "recipientId": "AXvyLRRSRYxX8HsQHwLgDJNwwxQ6MPyuhz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a149e9edfd55fbd717c71c7150b9546a47072537b1021c7fc4e23703b4322c93022033f23dbec845e796d00102b8570fabea477c75c88b574611bc46e3aa94afdd73", - "id": "d7fccd7b5b4da68f0deaad41f9f132f5891a82295259510d2a4c8f5c1f2fb22e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 169938227241, - "fee": 0, - "recipientId": "AKLvGM1T9FCVstMPjWTC9ytw5UjyZJ9xMT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d361a2adb15f3e5b20238eb382fedf6a502ce30044b065b9169f4bd223d30fb902205e6004454fb61b7562e86ff5dec410db6765e29128e23285a6dd2b34fd437791", - "id": "a59e761db39ffcf16d170da0745cb36b5fa9d13c103592cfc28e1083ceacd886", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 170449652084, - "fee": 0, - "recipientId": "ALwMf5fAPMJhW3iwjxMydBBNr8EgZcTkrr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200d97423751ff5c67b4a4bf29412b80ee84ec48c34a9412addeba4bc0a6278372022010398454bef83c100bfcbcd6920bef23951cc1534611f143c9dfc07a247d60e5", - "id": "d0dc0ac9dcbaea7ddeabf7fc94171d286fccc49fd07668e417b672e96b49f362", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 172000000000, - "fee": 0, - "recipientId": "AbpQwBzBGHQa2NdEpPbg8p1N5j1u9KUMkL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203599ccae223b04921b15fb6dbf2282c0847656785d0fc12874e739520462d8b602207d431393872a4a6c5e9a7014e99c5d5dc6ce17ed2d21745661b36c34096a96c8", - "id": "245622a915732fb38c48597bce7bb1b91a4ae08bfc629ab7108f13f17804a962", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 172479663865, - "fee": 0, - "recipientId": "Adu9KbxhD6etHEMnjkf6n7diLWdjEGzwiw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220702ff0e19a0dcc3fb0e62185a5cf1c094b6b67e3be700252e02ece19e3a53e3502205f45c8b0ab6d45cacc2572799244c5304b832f068e0cee10dbfefc14384e98d8", - "id": "3e2a032b3ea825987a4f374298f036806e3d032d7047ffb77f3c74c62d1bb8e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 172700000000, - "fee": 0, - "recipientId": "AT5ziEWMZjWzBjjKbgFVwa9XsXvgLF7E6x", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203072cc3eb52d630364deff654fedbc54d5a05feaac0e34e0b56acdd843c5d57502201e8f8c94205b700683610b373a3014b04fc0c064ecf7e48115febe1dd26e6c07", - "id": "760939c2cc34d8b911d94e7c5d33e577b4b521695ef1283793e47e9550cad9fb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 174283983946, - "fee": 0, - "recipientId": "AUnkYxRkpMJZ9jKzRQMN9p5VGDmYhk6bZQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bf56948136cd8aabd99228cfe5b668e3c31ee9ba51904413885a0c28de5785b00220529937211a9e16896a45ce426309e06db74d3cf0014316d6472088d843ccb6ab", - "id": "1440feb6aeb77c3ceb720c4cc1aff6c3ca840ad482b13ca6fc44943f26bd41d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 174283983946, - "fee": 0, - "recipientId": "AYP5kYDXLi1c6wrL6AbUX3S2JUEZPVLyug", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220564b80aa83110cfcf4c05976c11cb0bf022aea5539ccfc7285e6bb7024ea92a6022054cd14b6b69a9a7aced496951201a2b39b665282a053686b6095aa8f9af74be9", - "id": "2350844dac54c1d2b56608babd879cd5d1b986f4d0c3b32f587e0d270f9faac5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 174996809637, - "fee": 0, - "recipientId": "AZ8ijDkmLBLemmH1AVPQZz1ucfK7hmpLaG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e3e8f38a1b850c94c87b33bb095221210280b1f9da1d2ec58669063f7b9302c902206b3b20dc5e9fac39ab48f418d4119751c52692aa353361c20839b564ae2d86d0", - "id": "af897d4026d8b58fc97ad3e0a4f555c1ec2d38954a590611780229514b07aac6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 175000000000, - "fee": 0, - "recipientId": "AQyWsH3NpyMMepkGbMJmqBqZehhQoDnzgu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a9dadb4e5df8e5b324a66eca8e5a2abbe608886ceb3058c3b28d2c4eaaff255602205bd59390dd1b2b0019a5d928913d9379cad4adebd2b686d61b91733acc1629e9", - "id": "169028e76183f099c5c6937c4b0f33d085a4dc67b0cfdd5e70ef985a7640cc46", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 175000000000, - "fee": 0, - "recipientId": "ARaVs5XoWqrTdQrfXSSr2PTFWpz8ex6Tpd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3043021f2f2ee4045f6a3a4072212a905fb674ac22021fbe4f627ab06254c9d8996a6302206011608f1e33f52c6e310bb6f5990fb97b40e3621266d832b5ff3336998c75d8", - "id": "b29913407f911e50a89ee33658efe0bdd856c17654f022df7dff3ba66037456a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 175000000000, - "fee": 0, - "recipientId": "AaAYd5qhdUN5Suq6wnJyLFgvVMbz7tAXhe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dbe0cac3b1a67b51c05c4dcf60cdd40898c0d797c9e82ead41c3d253ff5936aa02206a1367f167e42d0272eb4a2aacde2f6392b845324131d83e4060743e981a2982", - "id": "8c1d279a9c3e6fa01cdd41be4de0389ca0628a81e9f54738b3521a38cad15199", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 176418068596, - "fee": 0, - "recipientId": "ALemeBL1Rt6JgL6Z8CJysKDckba33SxDJg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210087e1ac43a27da39206017ddb7b7d65c9104eb079587244283e27156769a6669a022002838c9763c2b4c53926175830c64dde70709773dd9ec5fc82880f8afcc57ac4", - "id": "d1e866be9ca942556ce9e7b7e86fc181cb4b2507a254e213837cc9549af568ea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 178300000000, - "fee": 0, - "recipientId": "AK7bbYXM4Gkyscxp9sVecVn12z3oniuEfm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204c1db106bfbc3cfb05d07cd96f73e94a858a3b48d98e6ecd569c70e6bd381b0d02202db38c2a3f4796f90719ac11132e0bccbf90f3c4df55a423a8102cdc2617dbf6", - "id": "dcfa805aa6fa5e3b48e2977f881e0b5e958fcd6d8f5e954d2ffcc19ec604b548", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 178300000000, - "fee": 0, - "recipientId": "AeBnLajb2S3ggu919PuDkcHKvQUyHogQ8e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e22d2b3fa99c81cc47c253d98cf068c6e6d4392cea6b98046a6f9bf96f08cc2902206b95a356720f6ad9a1fc35a0ad4a20dd52ff5554e917b83a47323564f6a59eea", - "id": "b7f38f5e6054fac0d79bfad045c9d6320b25c5daafc7981cf20e97a86d0c960c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 178500479084, - "fee": 0, - "recipientId": "AQdLeqJwgo6Tq6K9ydgN79wyaFt5gzKFde", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa25b2247f33f524a63be9001bc7bdcc5bec69b58672a2716c818bd6628e2dde02203b4fe9e4e3ee61142c081368f69b7aa585bff6a006d89be99f11c6812f8adefd", - "id": "f31cb0587377be839be6a8c831a618f305cf2aa81524fa8b7db848d1a890ff0a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 178900000000, - "fee": 0, - "recipientId": "AbZg4v2npAwLZfgjLAX3fj6ygRRXiT4uJ7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205069080158b4cdea2222990a79074e3b604f8fa3c9fe3c64a6ef43c28e17235502200ae19cfc468e9a6be38f7d32418a8fdbd9645926cdbc41f69983b814a2370268", - "id": "741fa2cda9837f82593d70443034a0fffcce8513f05a6808230b4dbc222a517d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 180659436535, - "fee": 0, - "recipientId": "Ac9dEA2bwQW99JBREyPDyhEotZ7YQRH6ra", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ca9bd24ddc2d0da487f016603fcbec17be8c090360e57b8e8cfc33609bf72d080220033a502c201ddc296b9ffe5a6c8a439a56ae3b3e7c21907467f22b5c84afec74", - "id": "d777a58efef6dd5addf4b41c7d606d5087d2c04da46cc3de40df6b253179322c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 180700000000, - "fee": 0, - "recipientId": "AN1yL23GyoHGo9sa9k34ivBDsbJ9xNTso4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202c19e9171610eea55add6b8ce88e6dd2df3cebcb2c7408750e2d439462053b9502202a0ac408178498ac59f3efef9fbe11595c6354e192d93384b40016ce4032d73b", - "id": "9baa8ef575418b7c21c114677123bdf2994083fdb026f71f3160e42749a3d896", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 180700000000, - "fee": 0, - "recipientId": "AQvopcUU3ynU28hy9qrgjNgAfNJUXxxh6d", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f192ccea6e18a7c7085772ac499c17747e47086e3e3f8091062a5024f6e1d77402207312fda88382fe4897d0f36660b431290e6cc28d09da713929cffaf9f6a49924", - "id": "bdb82c07d56940e5aa3ed4738521380d13c46dfcc2c41a08a552870b9b2f26c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 181302136380, - "fee": 0, - "recipientId": "APytrs52Z9zEQFbTTsVseTAc2ah2w3Rz6E", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206333d9530c4bb019d2fa8505536bba29d34255860b173bbce1f3c04dd5dfaae502202f71f8001cd13550442493273dfb28a76be302bb4958fa2c00c1da0752c5584b", - "id": "c8e21ed6c8a7b2de81d217a948f55842dd2d9f3ee5c5355e333357d7345a0276", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 181861548464, - "fee": 0, - "recipientId": "AK3N2csioKfWiKcdFiM5Jz8bh6pQurd8B6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205db6e528d94b20b8a25c31994a01ed5b2735bfa1bc5cf2e8a44d44b666ca11b8022056eccb3b38df08cd32a6d10abd316caf075582233a4e3f33cb73dbd143ee2905", - "id": "70ade5cda886880e03b60be04919c3623a511b655e98dca632b59c49c861a285", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 183741686918, - "fee": 0, - "recipientId": "Ad5ZbazVtWzjZJ7PPep5xcd6BrKXujAVRs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022046b4f82debedc188166749d7eac5367ea18d2c56ec136943dfea7081d2a52af402207f6b27161c1b32de2738e9d55225e411d74872db44109b4ebd740abf1f3751d8", - "id": "d7f3e67bcfcf0e9be5368f8c3a0d23f48271465c61a3d345054c5be9aa01249d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 187700000000, - "fee": 0, - "recipientId": "APaeLc9yFj2Kr4c6TzDvSEhtv9yuyYt15r", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201f2ec1b7426bca1a40df431b64b458e41c3da447c9110f04752b17f27e19922802200f450da9a7053e18de7d92f27b373bbeb5e8d83d9ab97e521e7b2b97d70a56bc", - "id": "2d55f798fc91fdbbf8e8cadb68372e3562b7c14b5c8cc9c15e9835c442f37133", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 187862979565, - "fee": 0, - "recipientId": "AeK3nfKry9sXXTKdf1TpGxukzwjEbFsYzB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049d80b6b0f5e5592a925a105fa49d3824cf6ade3e1f82c6e08458fc6d1b4e1ea0220084e937408e7f66e690d2170723464652d7fc5e49b5a50d46edd4daeec028806", - "id": "90e496cfb4d6231cf2e764ba08e6d236086ee2b0126d754e96e1f046990b4741", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 189637269418, - "fee": 0, - "recipientId": "AURDY45qCCokzgcLMHFgMQedxx3fAAvrmS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022070499c504fec7c112ea30ad8064083d50c86fc7bab9c35b906d3f5dc574a63c50220295bfade3e5b109dbd370a713f8166eede8aa42754b532dcfebb2e1208ee064f", - "id": "a344917af836c95bd810bbdb119bfa4505d53e314f7d8064b947a95765acc668", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 190000000000, - "fee": 0, - "recipientId": "AbQgQntyhfSarnTh8cFN7o5voAyDvbyZFa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bfd6cec5bf192f270babc3b0ba8435d592bdd369e3624c9c6c3b1a01b5f2b6820220191aa370fb41dc7e0f1c4ac848290419702e467f73dba7eb5e63f31028585fbe", - "id": "5174fb0eb29a8303387005b4518977b8386e811e228754863418ea5dae98953a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 191010602651, - "fee": 0, - "recipientId": "AQXAihgHkyTH2GaA5q7txsuGN8Wm1fRCsh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f63d5da7c7ccdabaeee4c6d4d7a6a280feb33e4c0309291d9d32e689be6d83b6022013a07a743b78fb4a3fdc0c405279a72a1514baebb9dd166b9364c46eb934c3f5", - "id": "0b931be5bc7475ba3080a14a864bbde1f78e8dc0e9097c7d6541ac32990f55fc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 192343079840, - "fee": 0, - "recipientId": "AJVL5Lh8fjMSrzDMTXxsBt8bjjEfMp6eFb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bf093f56441607b038305a26408c77428d3f41f0be77f1ad8a990b8837902eb702203c3badb9b7c845cd4c84261403de3ea42e644f11cc5536878828ff3536e91c0c", - "id": "e47fbbeb7f8b522d4a60ae7731df51df96a687adbea76a3d216c8671106e9c81", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 194167513244, - "fee": 0, - "recipientId": "AR8J56ZfnBCzB5Lvx5VyMbjKnfvN1ue8KT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa1e878c673805908d682901f13bf1f0ce133822ba183e9c00425bcc1d43e8bd022043c553f199195ec42da2c5ffead7abcf0aa9046dcb8a5009f0b04d64f3611c8a", - "id": "2da708e273f79eae2ccfc37fb5ce48fa46f664f90715e7109b4b03d4f336820a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 195295588810, - "fee": 0, - "recipientId": "ARRpaRQ3t4LQdiJSN2ZMsuShCn4RqV6uq3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202b4e543a3ac721ced2a4cde390f1b7e31d5efdc3d036d9e6a525c5a093ca7768022030c034a23ef72c1f335fb32fcc0e068ad14ddb0973302f8895f46060acac864c", - "id": "1cafd9c19b66bf9168a643075f84b1a78d80c909e2d6fcd13ebe51335ce45d83", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 196578368156, - "fee": 0, - "recipientId": "AXnet9aeWZ6SUKqHqHaZfCZNuTvQcdS9YR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fde0862fac2154078b283cf6d5d95e9913b5b19ba708cc17f6287832216e54e502201f71dce1c39e215b9d5a3c971ff5a6e8ebbfa6fb79a7135ceddd490216fed391", - "id": "4a3e46a8378334a550bd54172491a19a8832ff1c4e909b68f54a9acd22fa9e0f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 197344240463, - "fee": 0, - "recipientId": "AV9rVNEKtxumKAuw7ZRui27Xqm5oDQTcGu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fdadaef2d3b1c8eab520b7a5738db35eb35c224e2fdd0d215911454d74b09da302207d468b503cea3e7106c55f2b4e3081036c6ee71b0d22f3c7ef7c6ace38c2c1c5", - "id": "7a263c4f547c159bfeea9542f083060435453a72ef8251375930a6ce50788e35", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 199700000000, - "fee": 0, - "recipientId": "AGJDGWF7ZdcFg3py7fg5DXVLv7u835YEu4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200d2557315c3ed28f6e9006adff24c8c7653482ae2c5a46536dffcd2de17187c7022021bb54089176340c71544b27388cd2836274b5fa4578af87a8bcebf8509160de", - "id": "73ee85253599b0a5c6795e002a0369b02caafe3d471e3ae50bf06257d9242ea6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 200000000000, - "fee": 0, - "recipientId": "AVQp4XzGX7xezNvAs7zK9THH52sHQ5cVMF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f4497eb3cf8dab8dcc034e86323ed133378045e5be031e8a0aa7e146df51185102205b4e2fd28d73d1c6b0363163f3d43f455e909c5d1e91ab351f1c54a0c1180c56", - "id": "ab4b8af4e3bba9782ddd6e1cf423572c08018b97c1edb458ead90e71d84196ef", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 201600000000, - "fee": 0, - "recipientId": "Aa5mSdp95Q3UYCfq14hyqoFAb17nbvwtce", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a859f753c037cf28dd0adf78f00b25edfb1824a361dacaa6e638c1a026072f4002202f3fc4d70a9a783cb18cdf15cbcb44152c33c4b1bd3258a8d40b080fb677b93c", - "id": "f8f09ffcd58da0f41474741a12846a993315794679c3689f683e2caef0222616", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 202607842389, - "fee": 0, - "recipientId": "AZ1X4tRmPioTYsF7M9G6LptiwUC3FiTcSR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d687ac1127083d06605b4f903c9052cc4d0d4b32a6721e2bf5802d7363dba00e022072fa83d6653c638db9f2f011a61c7277e3feda10551d6455be11f7ea8282e625", - "id": "9fbf5160fb1d3e8c0b218e2332a1fd4ec0bac96c346fafcf8430cad2aa7dfbea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 202700000000, - "fee": 0, - "recipientId": "Ab9yCa4xAXYPMhDVQn4pq9sPaNPf87oPn6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203b4ad209e97f90c9042d145d904fb43b7415dee167bc23704e29d1b1e6f57a7602207cd6aa93c337139424cc160791262310216b131fe347e6261f29c30fc470991a", - "id": "f553a38a56e7591467c16af7ad8b3b33661f70cc6e7ef38b7193ca30d7433e80", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 203859765516, - "fee": 0, - "recipientId": "AdAVt1aKEhoTSpoThdzoBc551Pd8TvcnTK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ee9a0e9d21b09cf8456a7af712aff5576bbb7487afec2277c6ce857ea32044202202c7c723a32c2eb349c27cc9b53aba8c41eee9c668e606e8d1f39a4f4c51725cc", - "id": "fc7712497be31254a03197aa586d121307aee0db828ea22ae5f64efafd4a7d2d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 205000000000, - "fee": 0, - "recipientId": "ARpbHrGteAtmcuq4ogVhDHGYa5bNX43vuc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be97fc5e92f8dbe342499e2dfeb861e2a3034d90db0ed46263c492c02821842602203c072ccfcfc88cf36e9e2bb05f4bb6bad6055c6b5f409d46a686e2594d92b2f7", - "id": "3fa38602a65a4cbd8289112ad9d2dcc38682688422da1399192e9becb3713419", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 206040822146, - "fee": 0, - "recipientId": "ANiKFTez41cCVTDCeQMGDah3CZcQE3QtqM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b2d3c8ff809e06ce7f18e8578c098a05f17ef5725f5493455fbdafb0a18c64d50220278ebca469d1ec78d436a6876bf69caf7c43b61de58bf725768e20e5467b214f", - "id": "fb110b753cecae568ea684ea89219ecc435e4c0b36c4732f39429b96c4324d42", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 210000000000, - "fee": 0, - "recipientId": "AMEiCa1gAAMQkkNBLq6U8XU6eojD9NDLhK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100858d68292d61d3ee9765bd761c669b387362768926892d13e20de085dab00b34022063217ca008673f5d910d52e76172ae3d5cd89c5397722555fa02ef904046b3f0", - "id": "5e160385a766397e2e8073acf3c6e61ebc1e5531c051511e2305e1f9a99c9074", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 210742589495, - "fee": 0, - "recipientId": "ASa6Ptm7PRBFEabEA3xSAaTzkXAATsgWYH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201f7e5d2a2b1eb413415147c5b0531be4acc9657a258b0642674a9222c9e111aa02200a6b12557e8b15c93363cbd2f16c5da5b347808b81ff0787b509a5df402bdcb2", - "id": "7e416a3161f32a1c8fc7a50ddaee0005af24af5d71634ed412a850b272773300", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 213596124973, - "fee": 0, - "recipientId": "AW2WdVA4pT25G6Mg4Y8v4zVzwMqf4aSzVY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f483231b8fa37f29b442b23f5cde12bd0a3c5aa9685b0474c9d8b49e29bba09202202909c7cfd49d450ac8230e0830b501c30d4bde12daf911c0123c2986a6cd940b", - "id": "c40a53464e01b2d155f20f7bee18aeff16912e6d0bf62ce7af98242c7d17129c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 214366548708, - "fee": 0, - "recipientId": "AY4zDyuA2GRwYA19o9MfJWHkYX5y284Qef", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220433e6ed95528685139036657dda5bd0d00835bf887ff48b50f2ab8fee55c862f0220454511b151d642bf325c1b76e4f87c10e2edcd6888db2ae93b80dc002c64fc26", - "id": "d11284b0b9371d544a9164d028d01780bd65f8c855c147788bf9863670756898", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 218074902818, - "fee": 0, - "recipientId": "AcGLR6W5eVw7Z4nUNxAKAsPbff7W3g27LL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022059ae53f97ead4bcffcd1918d7441d2af25e3e82b8177543cf2ed491506ea4452022040e8491b7acda16b0cbe26376c7c363c31e007fe6d530d0a7b59a2e0fa8bd2c2", - "id": "492487dd0ac90a5e56db964bf01399a2e68a6ae90ae7df983d28bb7a016fd3ce", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 218233858158, - "fee": 0, - "recipientId": "Ady9TkKQPteVBFnEgsB1jReC31ChZPQTrM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022044026bad7aac5170ffb87b2fab2d9e042093d05707d14e8cecb33de925fd04b70220181f5fdf565a57905911e0de52b99de3fb686768d269bd825e660a7136e88baf", - "id": "fe4a120e7ab4c9029ce22ff565bb6a3b1c94b271d356fdfb5ebff146b521e983", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 220000000000, - "fee": 0, - "recipientId": "Ac4TAJqrvBq4ts5VZBhHdH1NG9t12T5iG3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207afeefd18cfef28ca2d575ce09186d5e73dc9b26164ce2f236b3191c0f96279202202716438a52ea4a50fc87bcf18b2053506f19cbc8063c6115d878c5f6392effbe", - "id": "97b34b6492aacfd46d6cc9abc808fe0a24753ddb97bbf6f6fecb926097faf36b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 220522585745, - "fee": 0, - "recipientId": "AVQxWb8vskpGUtBHGyWtZSdM9gGK4oWGAr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022029c9ca7067178a57095b53e99b9ef5bf510797b677eff8270dd269c44d52af190220773c8cd4647f9bd75fa8d67164990b3dd5c4e872564438d5125be7c08224858c", - "id": "8bf8a19bc12cd56778d9ceed54b0f85bf35d93074acfc79360c845b3afc49701", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 223213787338, - "fee": 0, - "recipientId": "AdYX3f5TQrDG1E59vtT67yfUkePMA4kdLs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ca26254fdba9aff2aac672c8f81d6501c7ed05f2310408211593616c79dd1d7f02205601d25d8967f49a12d32334a65c2a5135bffa6b00ae0b8409c20012db15c977", - "id": "01729bd3f0c4cf81b3c5ee92c11104e3ba3bdaf5ca1cff75468b1909b0702c0d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 223400000000, - "fee": 0, - "recipientId": "AW1Hn4dANHJAbEYZeEjY72kHziwS82vgnU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206554e7abc84d48a0446916b7dedfe2ca80b81ee68fac6a1306e20f21fff13277022069ce2552631634c4ea0d8715b01b78b2a6a5ae0ae27adedee6fa6e943845a33d", - "id": "489622fac2962052e7aebeee1e4a4b2474432b88164ee70863fc4597ccb0f27e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 224600000000, - "fee": 0, - "recipientId": "ASHHaaJMGmaBdgYzLBnCf3QRzmBfiCLwCZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f52a3a95b11ff46babb1a698a31a2c15543cd1e4edc0222b7f6593bf9e1d4a8402207aca0fc68e3096119766e390f2d20d2e48d308520f2700a4a1a8400fc9472407", - "id": "83ac0a39f01b70bd420881285a9cc7b4a99fe52f1329da58561a10bb7f933c4e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 225135109888, - "fee": 0, - "recipientId": "AShwN32PRrdnpqBNruVJBba54zvBxQT1fZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205855eb9048241c2c794ceb57f89f4cfe6c6e7db7b75819eb2d6b613ddf830f3102205e55a04f4d782a61e969ad9cfcb8926990001650282849122773fcc25efd49a8", - "id": "83093ca92b08910d1701846610d091403f0caa583c539c555c676b6a31fc62e0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 225186625152, - "fee": 0, - "recipientId": "AU8vV51b4PJZb9fWwHgVUrgz4Qq9ybsM6o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202ceb7829288662087ca7db79218ddb0d576cbcd112fc065bc5121d2726f7195c02202deb812e71c09a4e21095b881ba24d55a7cc135cc9dd739e66b22149d669269b", - "id": "2cac15432180f5f1a0459c96a42fa93dad5626ac6f3abba1b79116c9c785734e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 225944551562, - "fee": 0, - "recipientId": "AMw99hBqtW5JzU69NFnqdHmP7hezSHEJwd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202ecfd14378b25946c8f865df0a638300d26e6f82c4c557e47acc3ae5fdee98590220047e32bd8b1cefe6ac337acee1671d0c83b6c4966bab9de30615c36f5b78afdb", - "id": "c62a1fc478ea0e2bf3e784ed5a79e7073e62911ee9d078dbbbe888fbdb69f4ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 226088941627, - "fee": 0, - "recipientId": "AYijkoBCzzshgn3N89mw6tVHfFVDEoHhTj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008a560bec818d7a518488d631663ef9db1051402d2e9f24b7e45ed008de328adb0220783be0fe7394775314a1ac063e286a47d8f0d5a03bedef6fba927a08f16991ee", - "id": "b5ee7b65892cbd18ce165ec3039e9f103163b4775c19774223c7a039706c7be1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 226432949161, - "fee": 0, - "recipientId": "AQg79cKKYsHB27WksW9k5Zj4gfCgd6y334", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201b6566d5b7fef4e562a28df76b1cf240b1ab6d8e404fceeb6a73e8eeac2c410e02206fc6d1f95cc2f73268bc8c8a16c018f1e3d8841b5d1689a0ab9a994eca88c35c", - "id": "7f3c2b6326ae39f356f638e7ae2a62c6743f9a0ef5d96bf5579d4ab633945d3e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 226920728780, - "fee": 0, - "recipientId": "ASvcE2iGk6NjtGzsCdhtyEmYTZq8vzA4ro", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d491f759be71c566db0c613b002abe362d5773c394c4c334b2be2c594339cf1202207af821eab08a548cf1ac494066c9f5c44f1e012a2f802388cda6dd1d3e929136", - "id": "d5f945780826ed328ed99545019e5a71b6758320802accc0e27063a882bc410a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 227205088343, - "fee": 0, - "recipientId": "AHAD2pRZH6sqxHbhGYjAWWBbrUryGgLXNb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fda316ddaeb730b7d40cef2c6b67b1ab4309013e1fc2dfad9c7338cdbddd99d8022068dbecf8adbe1f3772ec3ada57f712d621c6dbc05586a7d07657f515ceaf8301", - "id": "c90e06437de6b21fefbc7ad2740019ed8a21d3d2f8cf4dec41c9ca50fecf9878", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 227326935581, - "fee": 0, - "recipientId": "AMzGgQHh5MHzFzpMV2RnP2ZEaDxh648gYr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022100df2e8ea17c584cd5c2b98d8f482073b0283a0e08dfff592d802b136397f482ae021f3b7a55fa8f7e6cf81dc6366730081f9350c929a64c00d57b278e8e927a6639", - "id": "4ff0f93c6049358eabbc1fc17d2085b198fb2f853898c31e4e86897921bf0739", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 227395512019, - "fee": 0, - "recipientId": "AadNd7r4Sxm8nRqRXauh8e93JieVxHr6SE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cb986908cda1d4cde6fc3d3d1d677cd884b44cae1f8baf1ff29c60ce9ec230f302206c17455c3a4a0cbac75aaf7827e3a2b6e04904114b580e6b2e34e1830824afe0", - "id": "8198e7da0ccb9bdddc19850d2b12b7f142e5bae867e699865c6e07f380249de3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 228227511241, - "fee": 0, - "recipientId": "ASyPsMFeWujywouFojtZHAmk8MhB3ZYMyr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008f58884d3e390ffce7e7dd713c9f3c0522a03f995ab5e78310ad81e490dcb80c02206ef0c5569957702d9260aa8b1ddfe7460737fbae69f7685cad4615e497b027e0", - "id": "76cb4cb4da799f5652caa816f58da0851a83281f7412938263b9d93535026bff", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 229694826743, - "fee": 0, - "recipientId": "AQejgdtXnkygMaN4iYGsceURNvrnGLN96D", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022043a8989908833bb50b5a74aa77ba1172f4126ba24fcfbb7502a8b3adbf7f9bf602205227d5352082b42dc8ff3a6971d5c04cae58f7c84f44b9804a42d7d424e076fd", - "id": "5f4e06198e65476fd625fe07b5876e9e9fc27cec66fe4e48faef7c89bd79fd57", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 231352203905, - "fee": 0, - "recipientId": "ALr1QRnTAnsQXCKVoc5nHjA78uVSDNGArL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202b798a36b794fa8ec3a75ad0edea4d6376cd39df6c0d263025c4b09b606acad80220371266d010a2cfd2a22e5e46322647528cca9eec61f6e17aedbb64ee19854b3a", - "id": "cefa0126ac7ae644b539f9004f2cd71b5e4520af416d93e4541c5ca8a84f75ce", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 232283790318, - "fee": 0, - "recipientId": "AeZPqVdexqu7VGES4UMMGdnySdKTRapfPi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022015972edfca96fef8811354524dad92eb10b6f891d80fd0daed0b692e60f65b2b022052ca9bcd9a6963826ca5ed308c0268c94a0136d236245c522859091d405d9599", - "id": "e36e0129a20398b9ec6d8190e2ef349e4e3ae61e0e1a09085f153523f57bc3a8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 234568404307, - "fee": 0, - "recipientId": "ATAva6NSSHp4tfH7N9toWikSqd6YYKBBLk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c2dd0fc577d306894f4464c70fe39c83e85c80f31003102a5ed878f6095ec21702202c64b5a075465f7c3014c9255ce0cbdf593b2158d6cdbd8e91f1b44ce9e3803d", - "id": "7d7fe67913bdd7317ac7b930a9cb90e7649f05125ebdf1e8691e4ed32a482af9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 235000000000, - "fee": 0, - "recipientId": "ANMT9kqisqY2KKLx3VMokjLCynqMxQGUaS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a8e13b798bd778badc34d9280e1d2ab9414c813f938384c0a86550e826cd8b8402205950252bc1efb41ff5b354dbff2ca5a8ebd5d4d815d9b5b3c39f2c787b881a48", - "id": "14b33a1754dd1dd5f243301dd176910255ab19ddaab5f6655f18790b9d2d8331", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 235931714746, - "fee": 0, - "recipientId": "ANpWLmDRAqDejcZ7GCdcKChmRGoMZCNJS6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b99bd138ec399c08edd91e0219e9bd049ab7a80e32c813501d80442f436d2d19022056341b7bf689ddaf371c88ade20a7952815bdd784dc2ae64d30b2ad311e79e39", - "id": "6953ea52c0b42bd950b6a53eb1e0b4e6af5c13d0836a30288726a0cbbeb34f56", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 235942815916, - "fee": 0, - "recipientId": "AcqrcF2KJgjenxBxXoK5vtWo5ELqwfkbhX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210094dd527f0c312b8ec349fa10aad1c7dfea7cd7dcc9c4c9196fc5a571c05d85cd0220595c9587b8bdefd34389e98cd78ef8b4bd9f1912700f0d3d713afbff6f5280f0", - "id": "6572a91980977673ed2d3c74edea33493e87d2855e060e3ee17a015e35331159", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 237605753191, - "fee": 0, - "recipientId": "APNyAxuJpws9bQ9agQN9J1hKE2kMoBpv1c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ec9cc4a71bdfe43a491208b81c164c1a7dba990eaa9232235a6f26eac2bac7b002202ace026d4bceaab43df36c3a7dc38d859e00337e0a278550fc11d3f7ec2455d9", - "id": "45c45d469ad237b9eb967bf033998866e4671c955498a39bcc4ff652d7366e69", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 237874783089, - "fee": 0, - "recipientId": "AUn658PgseDk5iLkcAHwEMAYy7SLz7aByk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022010c2a04ea0859438a7c8baec0c633a993afb4f2fa74b09fd3cdc36bd2a74cc36022074a2b5210fa27262bb40d9a5d6eaafcdd7791bb62bc678c3dec55d8a0e196b2d", - "id": "5809e7da60df4b7f9c8f359c859c43fae0d319b9ba0ba7e8dc2228d96cc8f651", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 238708836916, - "fee": 0, - "recipientId": "AS62XXxC49oGLwGiaFyMXCxCamuATfHSHo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206e893e194882b8be5aa96582595ca69b55ea0ea9a7799789d0e3a79d003ea77c022034edefdf0793575f1f2139b925e7cd1e87c831ef0e2a11f923440ccbb62aec8e", - "id": "745488ac65b21596e11eb41d380bd595a4270fb691819b5c0437e3b9b0f5f52d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 239265443070, - "fee": 0, - "recipientId": "AUdHvFwv2fF2DBpYqbnh8fakj2Wc1HFDW9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203fadb40ba73bed4b275b2dcf2d205b496404c65e896468fdb66946792fad929902201ce6b87ca0ca7013dc22177e6915c06fddabeaba1f818098b63cc0918c33c9cc", - "id": "7d94f1bdecf28119468bad192f7ed5ef12f67800246f071a312bd23e8d8f2d9e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 240950563055, - "fee": 0, - "recipientId": "ALE7xUp2tVw4XqoA9gK1d2UADbZ5XbNPv4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f2d3dbadcc28cb3aaa43b29caa35493ed229e7feb032985420d6cdae580e8355022005ad1413bc7dde581e08d60a2faa4faabcb8cb1d4e822cbe23f47922e737d7de", - "id": "8b057bffb3bfa183445d801a220a07a2c66e7e4e1aadc440265615fec4336442", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 245331115580, - "fee": 0, - "recipientId": "ALRhEXxjx8RxjBMbFJMZfRPVKYgTeABue1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c10b2b4f807ffc5f82391479dfbe6d1e455e16f6adb0c150bc92200c8fcbd9a30220033b3a351010143a22272ca5b48d73180b209b9d9f2367fff8ccd5dd899e642c", - "id": "09dd0da5b96f36e27e7d67779d3cdf266ccc2efdbe5c9ab88c20b5082942d4b7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 245368130406, - "fee": 0, - "recipientId": "AR8zWTBs1pw55kwS3Wq58uvhhwPjtTYNyr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220076a552745173a93750265a34cb80633d8d613dd72565420ccabe9d56f27c44002200509f4e62d51dd15d11fada0edf71e02af8397fe5e5bad521bf28bf9acf80655", - "id": "39c6bf781edd4da283148bb6f34d1152cee2fc98cec7464932a82bf380df151c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 250593911397, - "fee": 0, - "recipientId": "AGnxrUgmVmqqRNyo8nVVnFuSwiABeiMjXp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a01fa1cd2c20a5012c88ed1a0756a1f643a62c78fcfc4f69a7c983e76932ebd202205ecc72be31c8732feee7ed2f79ada18b52b9d773f26fda89825d5364ffb0653a", - "id": "abe6f922e8ab5509fb29fbe11356a182a5b7feb4e91d0ffabe3969708b5d221c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 253760909613, - "fee": 0, - "recipientId": "AchFpiHbXRyCxc1ZY6z638QtmJnDi7hozK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008e32159083784acaf47a4a5bc2fae2f25b42f27ccfd4d1f0bb2e6263f902ae8b0220068d23c7a19fcf30456fe8117678b6f16d14765256e034764f7be575c53579a3", - "id": "99b8b895b70466c7984edfdf0f05dc35ad4b52b3a620bed624cb11fd68a588c0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 254454616560, - "fee": 0, - "recipientId": "AXt9rWvbGupykENfpxW88mnD9zdxTDvWXQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022071ecea689811557ce917a64653771c2660ff29f9a5a903405a74f5f7e6884a0c022034acca7a4460d555b186d371be5c90de69ee6cd2e562b874fa37323dc9b7479a", - "id": "189b47b1a4ef03306276ad9e7afa6de0a26711fbdfb2bcb6527f679ca1afc192", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 254606167851, - "fee": 0, - "recipientId": "AaPyrCLTj12R3oc3iW2mWAwFBy6iu6VbwB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b4927d09c6bbbc5e300e8e12107822350358939d92916affee3f5bd8147bf845022052635047282fc85c9fb4f115bd03f9fdfe42636668f1da00073827ee72835b4a", - "id": "bb4f8ad248ea6a382f7b492587bd4ac14bd1f06554261b9e2d187aac784930a6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 254687439215, - "fee": 0, - "recipientId": "ASddgAZdbm4jQAWaT1q6Y2zQWRqEkJFEX9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e7ba05a49b3b569d7f55ba5dc0309db7d775c5f1b5ad068f426cecf7e6973bfd022073aa72abef5c92119acbd30f5434b066d2e9c3f68d0a8d44f61c6ac60d76d9a3", - "id": "3aed748d0979d80a807df6e530f480a05485d147925f971b26e7c7826e854fe0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 257637193659, - "fee": 0, - "recipientId": "ASsSVAcmoUukQd71zZdmXu6nXzmG3gsKVk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100823c1f1dd6b34885aaa0e0ad4fb4ce4362343dd5a328bbf0d670261a6b90d4a802205a54410b7ec114c84c47ca4de5390e78411fb580f966a790266ef570425ee935", - "id": "00dc859d0b95f33615098b9026cecae7a86f1aa61dc2402725bf41ed589fb563", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 257700000000, - "fee": 0, - "recipientId": "AdeWwxiobp9H6WiabttYQp5H3kkNrHXQ4h", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022052cfe09b5968829e0742892f8bf957072e13528e293879aaecd0c0c5c37273b5022021eb5fb8c940be0b569c6c90921ce4a5e6e20df8662d50d94f05b03429317e66", - "id": "b55d6a965744191be648d34aed3eafea6346102c9c078ac7e6ad16b3aa9528da", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 258336704622, - "fee": 0, - "recipientId": "AZeA4DJAW5qBbSdGavdC1CikwiW5usc19G", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3043021f791bd82b7e0414daa7a8a9567d935795438cf7e23c4ce95ec0c634ddf8bea102205eda1bf6bbb5f7f374460fefca7e8001e4a37d1d2615e776c347a3094ecb88a7", - "id": "5ab82d277db94a82df64931ca7c39d4708fe488152263dd47abc95f889fea4c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 263400000000, - "fee": 0, - "recipientId": "AQ1qa5TMsSF4RWU6fBZCQCFxcEpzmtkGEM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204fea12280fe69879e6dcd4eb86c2590293a9647627db7709ac6848690e3d9c3602201fd4810e4209edae71a9dcbd3b58daa95ee072bff610b035a8c642770d70de4b", - "id": "0985c36709c6452d9520cc9a2742c2388949c62c811f51add27bb59e0092f144", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 263517383725, - "fee": 0, - "recipientId": "AVLiBtEcZGEyUzME7mkjwn9whygssd6iio", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210083285814a6b94481dfac21296003cc4f343d4d7c8bc7a7a0e5345d4aa48eac1902207915c6bedd8a3a10812da78de0368acae101cbe093685d18fdcddc98b76053d7", - "id": "af50dd79f9d422fcbd301e204ba6a1e27f7b52bad2ef1183ff20e434049c2ea3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 264706758253, - "fee": 0, - "recipientId": "ANTb8pDUmSQHBQK47P3JSU5pTWYDnAuGGW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204175f6fc6e5b7e7b453dc121a8af4df6dfc4c55028d11c3ddf71b9d81f4e7e0402207723e4e6a7ca838f645fbab78af16c493d384a06e70b32909fc4cf915817d5e3", - "id": "0ed931759cb9de29db6d266868b504864389c2d1cf3a81f5d4a5efa54ae82a7a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 266600000000, - "fee": 0, - "recipientId": "AYpgKvx4aBst2hD5gaRYv21FtiELyaehMa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210095cfda172969672c538a02e9befc1343f837d4fb1f3bb9570312f03e2dbfafd602202297f4fd1f28d1c63614a75d47c4cbf3f60ec5f77eadf12769d4de7a39290277", - "id": "2ad61362f35c9510c43e14c3968ae3ab8a1f0b1ebe69dbba2b0bdb094805d55a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 266600000000, - "fee": 0, - "recipientId": "AFsy81B8AvNKkxpneNm4WaPXsXezYLRVwV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220632892e9de2b0f3e915e2af3a869e834b065a33d239888e4d9cc86068e9a8d9202201cf798edd4f1785b50425d3cf0e85b2cf41117479e4d0d354a00908ac809d711", - "id": "2e7d5d559b6bac5a49af20ad2e745daff3651fcd782d3a681e1935c2fec8793a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 267156888910, - "fee": 0, - "recipientId": "AWxBnbH6FrttJsempdhhoNqzzdynnue2Kr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204ba5295df2c4e3d4b1c098c7ecdedc98ec316f115399ecca37ac08606010a4bc022057819841af1ce0f3c979c9b3ef0f6de3493e21b10040dab878cf753849c25b0a", - "id": "0caf79acb11be4d8712ca28485afcee25efc113aea6eee515d490a9235608e28", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 268215057163, - "fee": 0, - "recipientId": "AKU81Sra3nx1y9FcCJYxuuHQjZ81rjHavg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022020e417bdf510dbae5780ddc943a9b2fbdc1a79d2225a3e491a223f3e6a8fda2f022021b6e95132010bfd534af5d9404295e9ab497f6f00798ee15d7a6b5ef49257b1", - "id": "fc18ebde76c5d15ef00d628b4e987f77c4ec85d4fe24dfb56d7c3a20fd8abe27", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 269700000000, - "fee": 0, - "recipientId": "AXHDNRmJ36baawXLcS8ipDHgrunCeRN471", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220580727096e40273d96f9f2c52109eb4810c2cc10797bc59228f1abb8fb42af22022062043d9bef15d4e1239a42735bc3cccf08a15c6b1e51058d3c0ea590418e269e", - "id": "fe60d828232ea75e7df9a6008cb7b1b3ad2e5d8e72f638350f6f40420b36bec5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 271217304301, - "fee": 0, - "recipientId": "AegyGSE9AKWNiYeiBPA7p7f5MDBbsQobNw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220587cb825e6b40631e4b8262b05b170e219c6a42c723ba1fed4f6cc6b472474120220784a7ff6e72b544cd8807dfdabb9f00f3782322bef851bba6995ff4b2b826710", - "id": "fb0dfdcec943df0624049563fc6c04fdbdfe413bbeef620e7a27ac06af71f0bf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 273312359613, - "fee": 0, - "recipientId": "AdG4rV1nutJ2VmGfKDvH1usesnFgxdokd1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220367368621c5adcf56ee4cda123934cc5fc9ac54332b0bcaeaa0b413a0d65a71402201971cf564a643d39c915a8328da5d3cfa36901b373ad69b8a72a21345946948a", - "id": "5a71a47bab2f7cab9e01e4694cfbfb67b77c411d4488555101f187dc88ca3372", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 275823348504, - "fee": 0, - "recipientId": "AQHWx36dxxzBjq7t4HD7sCMtBMBSjCBceS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022020167d72b49f9c4b420066171449eedbd8313fcd5384f06881d19c1648c1b4d902205d0d2bbf805276426751a1a45daf52516d058a1776f43963c583cece69fe20c6", - "id": "a5b96fda073ea900e23c4c85cee5b79f0ebead350d4353d1697874abeb377ec4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 275919189541, - "fee": 0, - "recipientId": "AdmQcPaXYhe7cJcD9C1Ghk6BKbfS9VqFQq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d577572afc4ed0362d6ec5c4a5c9b31abba6d2267c51a69c7d5f7349f8760b902203e73555f377d34d71c77786defe682711866f02f4be29787552bc25e1a277082", - "id": "99e450025bb676e306e237e05dde8a6f2d49913794ed927d9699c78a5e363409", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 279247094098, - "fee": 0, - "recipientId": "AT5ANRumUi7WBhCSVs35yfkbUg186x96fe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100967b5bfbba242e8efe50f90f6880afe77a1e065e471a7885060af61baed6736d02202dfc5b008abb3466e78cc24315b51af9058ed24e2f77eb6e57c80b3a85ce250e", - "id": "ca2358076d0c35255bc017cc7a85023f41551867e9ef28287ff6d5da2d50833e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 280000000000, - "fee": 0, - "recipientId": "AQN9znPRTre9TCbGYmriMAc8Z2m8H8xrL1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022032247fec35acb98d299336331d44cd915e19b15857c36ed9f5bbe6dca7c2d9ea0220350197b5c1a25d9e0dad1d00d223596aff07e8a0da480661a19e27342a26dee3", - "id": "fa5d6719a86996e3661842604a1448e78e5b9d6a91e6c137a4ca6af765b9ca19", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 280803102800, - "fee": 0, - "recipientId": "ANfCguzStf32kWL6HueDisbWajTw8acRWs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022014dc24aa9c13fa450a824e24a25ffb06005807c86b04aad84c4fd502e257647f02206479aaebb822b9c1e0ebc8d28acb759c7e5b09a3eaaadd48409dcca0c7045c0c", - "id": "08fc563e818b09004b5ce2e4c7c5eed2890146f62deebcafec6d5a2e4ad7f0bd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 280988036090, - "fee": 0, - "recipientId": "AQypxoNmusR1UsP1u69SFbjb3R9YH7mszw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d63d05d551bd6c9610dad7ecec2332de5675540eaa9c7989a00b4622d238ded3022071a021dbd0a3e69cf9cdc1ac0d949f803cb5137e08ddf476fe606a774f6f1127", - "id": "52cb8cbb5a802ca5d4f20438a0cf60ce155421404f9b8184952571a2382745c4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 281336687456, - "fee": 0, - "recipientId": "AZx5ahPCDgGR7tdGeZUTv6oV4vfmKvPeJs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201f040daf2812c5bca3ca29ea83bf0d24212707eec898547a76d6f00a114d1c330220179b38aa34cde2de80c519e0ac5beb23be3e14389d5e12b530fcc8769779f9ea", - "id": "b3e6422692e6b305050f7dcc3d23c3ea9efead66f14327c7ed41f02e1471039f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 289471840817, - "fee": 0, - "recipientId": "ALApc6GgN5sAA5MdRfws9MS3PxcP14Vk3i", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220284ba45d70676f0e34c9f8401d06c0d5807a6e84d021ebb52266730491ac5c0d022014d14816d1e86ca5568ea8c78e54def350ab3817f4fefe4404362042cdd12ef6", - "id": "899123718c97ccfc2af2a5bc4b50d97012d46c2d1403c60b51d54ae30e3e7da0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 290500000000, - "fee": 0, - "recipientId": "AVd1HMGBkguKeBduZoN7JD25mVv91G3diR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220623a9ce997e94680b7fda4412921890ddb91de2dc833c76331026df41d4bc93f0220159e4f175d888472f3eb4318344b7fbde4140cac5b43dade415445a5c848c24e", - "id": "b2fd759ed4885ab57848dcb86ccc2026f47bae0cac4fe119183550435dc21d90", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 293042430719, - "fee": 0, - "recipientId": "ATdJXNdCCBdPaD9pTzZE1uHubNd4EixaoA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220333ccf40d7686d7ede92e901701f2e4fb65206b40bf1f71abc85815e6eecce1e02204c30a9f447bb751fa78c08ac0eea0dd9bf22950cef4a8ae85ae101970a963362", - "id": "69d303c8dbbec71c451201ea88e64455da3260bcf9eb866a784b13128ae3e6d6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 294030114327, - "fee": 0, - "recipientId": "AaobgufVyjG5QYYUCHfdD62yqfKnheoiK7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207badea2403c148d9573e4b8551e91ff258250cc339647cfc7c09492c64d57602022058a84d0ca29a3e2a9d469bb5e31460c3fc55a968a296337dda894b1f324a3406", - "id": "e1fc93b662aa17857ec8282b16af5accb50862272ac7e804c9a599072e13a7b4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 297800000000, - "fee": 0, - "recipientId": "AavHyFGe2JQE9ZwkWVHZeZF4otbj8q8aKp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210092332a9a11c56d75f25b407f22b3b910d469d888b30ddeb12ab843b37f5eccb2022008048a684ee0765e703fff4452d81e85eee5060c7730ce7959e66ca4c442414d", - "id": "e908984cbe697b713493d9e3f6a8e928932c5352d5f3eb262d7840cc04e8741e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 301300000000, - "fee": 0, - "recipientId": "AMwtequWhiTWPMH7R4PUf6wzEUVoQf41sE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220284be66d3840c7c5a748d244c17b9255e2977142e43263139e36d89e1b204d9502206d9ebc18bd3f19ce852d2b4e8371baed5853cfe7a57d550fb89332f3cebed29d", - "id": "caa7b788cd2a9c62b6f83c5e549fdfecaafd76dc531f1f29689cf3490bcbdbc2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 301947752203, - "fee": 0, - "recipientId": "ATFJPWAy75j6jXHzYPCG1JbWYMm6XvAdiK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b5fb6d53b4dd98f55f120f556a6bb569291942b574a0977ffb82009a8422d8cd02205203575ca53fc5636a40e6f931e429ff63e2bb55931177732a04a0ce0431d431", - "id": "6f0fd1a8e7236b0e2ea0353de6aace7a1a9e52b2268a2e21a7d70ba50df685b8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 302851017757, - "fee": 0, - "recipientId": "AGrNFNUrFmtbDEtGmdmcXY4PmdcGt52xtD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207085b431cd0a8ef985dc12b3b01b7e45ad8b3c0aade162cc21054fda777114b202205df8eb5b1e36a571083b0c89af506f917d5bb80859852a0435fa7af5ecef1994", - "id": "640cabc266bc124bb7575b0d0d54826bb4298f6dcf86728552a7c85cfb9d5c42", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 304323259134, - "fee": 0, - "recipientId": "AcmxYd8QHTF89awqRBCtJFsC1aPvwfnLqx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c4a2d7ce45f32daeefaadb4b12d0f3022f207f68019a82d76fa41fae5f50461d022015bed2ebd307241f334322ac81eacac8b517a6b4942ee9dfa866ccc85cd3c4be", - "id": "88585b0976182c15633b99af07e08efd91f2e2ade96f6bcac9a87289b7b2f7bb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 304700000000, - "fee": 0, - "recipientId": "AcjeSyWdbJbP7Y4xXZpPuGLuNtTL1EVmKx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022040412540fd1740a5bf25c9edee8e55b8697f4862b476d93c57b7397b5265c14e0220394c3e19b2a9ffff36ff404e4ac60a77b695cd3641bdb119a0fe2d9a74ca5b19", - "id": "09b69e99fa28ffce86e2333899a9053e7cb915d4a6eef962c9b19fce83c42229", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 308731620043, - "fee": 0, - "recipientId": "ATXdja19fpLfnjPbpuusYqtSs2xGK1yYy7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f16422eb334f756c50c1af8e4df9897a0341dd5b222fb7e24838978006d1b0b802206eb59fb05242d6208812e4601aeb2a0a64408ac03aa42ccfd6e4d2176cab3821", - "id": "71036c6537b211efc0da3ded963361c03c47b5002a0729cb8bd94c9171514900", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 309164632390, - "fee": 0, - "recipientId": "ARftmK1C8xzd6QJk4xNvvnLUHNNHr5oWD2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d9ae4512d715c87e98370acc74818cb15f4a3575eadbe764e15a6aa4beff9ec10220042de90d3d51992d850581e2f717c40d7e00d85a07d5418856b8b4d28a6da8ed", - "id": "aeff6d588a242455a1226b253a08412327b1936cb47d911cffc5b0e1d4e01876", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 313315057164, - "fee": 0, - "recipientId": "AVziGRo9W9A4ngqrkof2wpHyrDDeBDiKiN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022077e882d387450d847cc5ccf18042ef7c0c9a15b9f01ed523ab2dd90f3debec66022000b0525cdb09ce49c7ffb1588002e56e20f95798e2ed979e7f34cfb7c95aea6c", - "id": "c9fc04b7d64a5f6cc60fade0d91d344ecd925187f0e2b5591cb1e0748c293508", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 315100000000, - "fee": 0, - "recipientId": "ANPQroio7mrAH6hSrETf9nhoBvhz9UfrnY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022004011cfce82a2b867330e93ac537230402fae45ebd6c28029c20a9a179fd588e02203c1d4518c47a0f86f33d93ddd32f6f97d297c74ce4a3a22464695aa14e144311", - "id": "b50c52e231192da85a6249b53545fcc91f17e03db263ba9ba9830f6e2198a36b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 323400000000, - "fee": 0, - "recipientId": "AbnhZAGRAWZQNfj3NSwWacXCcXsoDg1UDZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022043e245c68837fc34dbf6a6f1806a5ded6a173f97a935aed9fe532ec5df5a045402202f5a8bb2ae72c7995e2fcaf1acbb9615b2d0f3567b2bd9db01af6e02c33c1a96", - "id": "5475983134ca085d94e93c8ad7847d8ec2d124c0606e7f65dc7360582927e28b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 333412838852, - "fee": 0, - "recipientId": "AHFC64BTXc4kKdmn2jw8Tb8487uxsbK65j", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f1d1ec1e2e7af629ab53f68441a83f4107ff97e7d21ae0db068cff630a2ad887022030152a4a06f8a0d390d7957995be0a5134f1fbf58693f4ccfb4815abefd9ba45", - "id": "178b974b999420ffc3f16c4b96a2886660174655e77f83e1277d1cc139c42445", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 334419127093, - "fee": 0, - "recipientId": "AbEtCb6JAawzHj3iDF1CnEBWpjdShoRes7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100934d8a30fa7fad31b7e59d5befa251d41a9b476f381394569b6c670388b88ea702201789741ebd7614767df9f43b73a8ebc1ff546e9747ffd22cb1c2f7060891622f", - "id": "5889c1d56044d500f6abd85ba7e02e2fd69dae5740a88a6321acbf5620f0ed6b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 338228944448, - "fee": 0, - "recipientId": "ANm2X2bmWfVavS68Wf6zmisgvyKzmsYWEk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dad3e59166de998a91e9439ee044e318dd82040d8c20ab971d6ba12fe5057384022048f7c14d431001b88b2ce5abecba8fec190d38a1dad0886b1213c2bfe649b96f", - "id": "3255e17ccac83b9df7d9d34bd09ae2a6b6664423c632046b5ace6267e591fa0f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 338641901001, - "fee": 0, - "recipientId": "AR9nWb2Y4rZzrsCTpHCDBsjeTB2cSqxgLr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100842b25c4dcaa3f85e3cffcb5412650e3d16f65600c6f76b3ffda4d3064649ce802204b6e0ff006c7045ec16bc1f2ddec0c778a192bf5f65203f1c9330dbd25e0317e", - "id": "11d040dd27672f77a6dd8088f2a40c0d095d714ff505582422eb9c280f65ca7f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 339243683819, - "fee": 0, - "recipientId": "APkQyY3hE8TTfDdYFxVX9BXfAhKvgLMFFC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d5444e7ab774e40da05ae30917882abef70cd94d28114658515c122bd33b32c022022e8c524d76b82f9241dcb8d49759dcb8e11efdbb2bc8f41ad1340b0ace8ef3e", - "id": "05c45393976a5e5daa2ad7386d12587d3f8096e6e99875b8aad24b6b87fec4c2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 340662911557, - "fee": 0, - "recipientId": "ATynVRNrhhRBWx9xLuwzqRMatJEebrNag5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ab90a2e21ac0cb5269f1e3f662e29d54fba5e57b867fbe8c716a2212c9c2fa06022032daff32ee5c0167d8f1716462077882e411473116cbd3a23289832ddc414a6d", - "id": "863339c27a73d2b35fb206638b23c1048f35854c4d8ef4e0d2c2c9f664a38be3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 340892990675, - "fee": 0, - "recipientId": "AdDGwzuSnwbTgS5zCn9G2tjgbRSVwtwXgq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205c4da9c7896032a24fb33f087f28cbd4ee68a1af5f242ad4cc246ce189c57fc70220754cda9f04dddd1db8a420374a314b3398d2b20c8a13ca69490c9c48af950917", - "id": "6b0fd1ad749e8923efea2a678bd5fa503cad05c1290b85d87336a46641148a1d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 341600000000, - "fee": 0, - "recipientId": "AZXZtF8s1rtY2x7qZ4fup9iXsGdwLgCy5o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022029cb43b5e7381c8692a5e474af506b4443735f56198d2c03d8bac38edbfc46de0220736670e57d73f4d8ee09c27ddf435b1ddbe206fde50b3cf2362115b11ea8da9f", - "id": "79bd2a6949472941f09c7872534dd18cc5cc7d55d8ce9276f0859cc872f1f56f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 342836625893, - "fee": 0, - "recipientId": "AbSRLLJkexPZCWdhznU1YnBs6UmsvAsg8R", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220615218597c85309c9eb5db6cd1d0657998553494a8ceb6140102d9d6955bcd4f02205264ce22077d58684a39bec2dd60d2b90780ebcef424fa29720244d19907dc6b", - "id": "ebce164ccef6fb06922b26611a1d4c755ac5393812f1b6e6725f5a09eacf9607", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 348484491500, - "fee": 0, - "recipientId": "AVv22GuhDy6QAq5EbWmvqPpyNK327dDKpX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210091c2b8ace2dc7f2d4bbda72cb5d52cae4b6413f823dfb4722d7a6b78d93d07df022046b2f87bdc34fb337175fbaa95d17ecd967a2b3bf2e158759b50edc1be0ab51c", - "id": "74c1b29f70e726c862c8db61964d259aabdc5fc1e5ec4a14caae3338c474b092", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 349954995610, - "fee": 0, - "recipientId": "AZKSX4x6Wrun1sKFcm5tUMQTyEDKHSGvEt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204db55e406f565efc50329b609813b5361fa8829413c65dfbdb81eb62d45d4dc702206eef0b82a59c4ae156d53b5174d667b2239934b2f4ce6e3a433ffcf133dfac8d", - "id": "b4f3fff5b12a05df38f253328d0212e80e1b86f9f783d1845676a395fb46b72a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 350000000000, - "fee": 0, - "recipientId": "AXUuekRSD7Z8aAqRz5oEra8tjAhn5Jvvrr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100875b2ed22d8bc7cef1683d8aa8b5f5bf4696859e2770afc120fc5fb9fcaea1c702202771caf3c8ca619fa15f339dfe2c1106d066804689421836b19d9a5d1fdb5cd9", - "id": "15a1e1313a9a1df3686417a2eed1cae978664b8413e79fea6b38925a286b4411", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 350000000000, - "fee": 0, - "recipientId": "AWnx5rbjMA3BNCXQ2JDXw9jjtdJtqG9gvU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220723a306d9e64fb999de916be02dd0a12c45a33da542fb93eb5be5a73a39c7df0022063a52580d99354654e80fcd43440c0df1dd932c939eb672308bfa4ea705ff901", - "id": "6a717fdc8c6b1052bc7f70a2a18ffa4bf14f78b8d252825c90424d920f13a24f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 350918258935, - "fee": 0, - "recipientId": "AGUqJi6qb2E4F51wdxxdtomHGdTgDMYhkm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a2ed646787d44f8f362242ce96426f32fa938967c409f3d1f748b713aab0a2c9022019adfe63d28f5b634ef60f843021e42514baa766c465a45840d73bedc00001f5", - "id": "a4b4f4cb2902f4d9a0489fe35641c04bf8911b064d9cd29b80cb0a1274ed64d5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 351177075272, - "fee": 0, - "recipientId": "AMQLH7k26GetQAX3q4shwWaDooL9cDcW9F", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e6ec5c6fcc7b88f4325f2e449d139e6889e8eea625463b9cb120b8921c26a902022049986b8ca2d95d0f3b461601b3411cc7e694271a2496ce84088899242a9212d4", - "id": "6f0898653b2de3e94b56625e6d6248bfe50f2e120b0a6425a311f6a47e7a3b26", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 352101061907, - "fee": 0, - "recipientId": "AKGkMr8fTn7d5Wcu3AAyjbVM338JWubiHm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207b22f8fc7e97f5669baeefa2e72d9d30dce95e4ece077da817c391460050de28022045ecc963840f840ebf144a32ca3f47827a3f8665ccca7653a718d273a29c6a52", - "id": "35b48f395bb80488b5b9542206cac08dc7bb6e1c4ac2f07fceeaa028a615b2df", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 353468821454, - "fee": 0, - "recipientId": "ALkHY1xQK74faXu6C3Pejk1xSuAFqCFdhz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022077fe72a93d8d27611229b8f74ec4dcdd4fce7c4c3379d44237814ed7e5b81bf00220679f8baa1992972284470f22d085907bcaf41b5cbdeaf9ae886be71187e38c0a", - "id": "ac919bd687999aa984250568b6c43e8cedd7d1a639aed32a816f8e1588cd901a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 356573668788, - "fee": 0, - "recipientId": "AaEfJQu8y267hp5fQSyJtzR4iJSor7q5xH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f101ca42eae873277eae594351e634addb1ecd245c28fab5a3b6ad97add0800f02204a262f763608d06ae684f5147b1ccf82bc9538bd82937de72cd1d8593c9e9804", - "id": "51f08208e82c1ea12099b608bc6cb3fdec272ae383c10cc18cca09d3a6906ad9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 357292915405, - "fee": 0, - "recipientId": "Ab7CQLYGsWUEwHRY86hEPRXaABtMRfj4vp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022048b11cb528691c8397aa0c8b01d4824ab9ab37b2ffbfd5c93ea7632be5414abf02205d868c12d097942225f488ae41bb9a0b83fb7e12fe163eabbec26a50a5751d17", - "id": "cbb6e663289d0af21a30738d8456ad4bf347fc60052eca55937598ddcd1f8127", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 357640325222, - "fee": 0, - "recipientId": "AQzZvHDiVUfrPTs3PhNi6xXaCmWwPJ9rW3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c6744c0afeb8b450b915b4131c6a5318073f20cc7828f31f05b5fe7a51d40413022075e0848a80f03df23205e31438c4f7fdb16aa36060e87838f5ea4098ae5e92c5", - "id": "aae37ca7a805749e82bc2ccb0f812c0c80c5731d4b0258a6a44b394985f4e213", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 359722188329, - "fee": 0, - "recipientId": "AH45YgpupuJgJaKfNqpxrbusmyxxzAsSiZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205685e59ae6d20fe9242cf689a3a6d7741215b7d473405716b61eaa15c4ae156f02201251a4a0cb27f4f5501c5b5feb891c730a795620d7092d7906b4d853e830ffaf", - "id": "bcf73615f4b48a7ea0a5570da0b75b6edbe053808301a5d3c0465da8a1c8a992", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 361053804649, - "fee": 0, - "recipientId": "ALGxVP1rkTWWUpEZyhTvzT8YJprKADx6jA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100850f4683c8641e83b64d7076b43922e70569769b224813ab0513c88e51470eb302200ba35068526155c1413020bf4c18f2b4b8cc32f5e16877a9e10aa32dae1907cd", - "id": "afd3ee65410f9d76132c8cecbc796bfd54a530327ba33af33dc0e9a07760c1fd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 363315727061, - "fee": 0, - "recipientId": "AMQ8ezFU9gvYfyxxk7VETnXmK6kpt1Looc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201689314194023cd9e64eef30f0051279da6253ab7b487de47d5ce3b81487f2830220148ffa133c7bca9cf6d1d743105be8b3912af8426d7e0309b969c2e2d6334674", - "id": "a9bcd03694c114a041220271fd5f01274c7ae365a6b0f85d89b364a104a20e29", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 364303311651, - "fee": 0, - "recipientId": "Aeg4n5zYvShKUmhuXe7CmzvEJqFBTv3jVs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100899497a5fba8b821e22acd35d5027771d09baf69899acf626c47d63ee4ac74fe0220481eab02c6e999522954a7da743125b5162b56620a93d6132195675464f64c8d", - "id": "e7ac9b3742d019488f8d93c3418e19fa8bf556bc232ff0af88cc300e631329ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 364467189703, - "fee": 0, - "recipientId": "AUSKfmvgiPu3LSAfY6Kfyo4zR1VgL2twcL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022055af734911a3bfe58f44351b3ae7c15653ea3738911cb6d9872c62471eb3716d022032d62b6716655c8a6e9109fada3a82a3b4a20809ab6c0166ee79327529e92ac0", - "id": "635946b171c00b3f9c0983e4a43582187bd2a3016349a6760732e434bdfcd697", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 365586352150, - "fee": 0, - "recipientId": "ANV7tExbU5ZgSDJGYyDtDmfg3Z8SVZ6rvf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220493d2343b10c305a73614abd14c038561f4ae878c6d9957a171c0616f149187f022042548385875006c2cb45faaf5322f455b178d256075a007913ac11be84fd68b7", - "id": "ebba2ab5afa6f21355ca4f77d4b79e55d08b88c0b8b35afb69749ac61f0b031d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 366287950968, - "fee": 0, - "recipientId": "AcM4tvSTkSHsvjLM2MtBEkMX53ErPpFEbo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f9c913da5d2500b202003576ef1c2f478ee3218ac7d90ec443b94b64e96012f0220537e1b8af0d427a41400ba6008a463d00006c5f722bbc6b65e1d83d524863ec4", - "id": "a6fa7609921c11ea4658cea3ee0626f1a87d0ae21324780ce17721ed0d21f539", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 369653766955, - "fee": 0, - "recipientId": "AMfKrnGPBwckMBZff3rR4wFGfeztHUXEXk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202e4e5c658ba2857c9b0d56b5babd7bcb848bece7b38820f4407827132032d59d0220713d1203f84e049caebf326b1eb3424b91643f407ee34494d1614ecc7b06c7dd", - "id": "ac8c772db29278a0bd50fa5e4b0a016fa8bb80b35b20bbb538ead6f53218cfff", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 373149442409, - "fee": 0, - "recipientId": "AVHXn563x7mzNDVN32MCf8Jngc7UKgGoY2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022009b8fc1be1c3fde84bf3099e79c2356bea23ae042a0f76bb1d07b34fd4e90b410220704e5c4ced7376c4c7135004c0f07ae545ea1b5a0e72355caf0aae0150c865af", - "id": "36ab40ef3b10937488dd8593e7726d4c965a5baa8b2839f06620cd6392c146aa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 373746668966, - "fee": 0, - "recipientId": "AUc2gugBPs87YHajno9jVS2niPspZLydtb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d83d24ca3f90fbf5791169619436bc87c015f58140ea0c72a4c68df3996ea50c02201a061ded755126c92713e464bdcfd652cd4f7530aa1c5a56f1c56e366b388236", - "id": "f46f2c84bfb139e6bd22eaafd4b8c18a0547635dc798b91a3bc40567ddedb41a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 373774406219, - "fee": 0, - "recipientId": "AasYFcLgSztcGK66vSat1rgHSpFSxbgMML", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dd07f53bb0e76df7d4dae83990242fb5db90a9c53c1d93855620b03eacf5d95902207957e7389be539c14ff9a17b9b0cd4418ebb14e80a74e3e56b555062e209e333", - "id": "19b48281b3104b5c29a0858d05fb08e8c324437fbeba15fdf68756842b3434e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 374988724940, - "fee": 0, - "recipientId": "AbNopZKDikntAR9jguVZBChPeVmrp363ey", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220238b5a7da45bc180902e8b2c49d8552306a721f6a7f7f435eea329e0fb6952af022028e04e87bb64fb457a3cde60771481e71aec7fc8c2522cb05918e6677242ef67", - "id": "3e5fa869674d84582bf5a2cd3a2d18a52af64b80c188ddc9b15d6dc4b129c67a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 395360944311, - "fee": 0, - "recipientId": "Abk1pu25HhKdUVPwwkGqp1zqhYMTHEU2bR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008e3454257b6c3f1b6bcaf812b5f3551ecaf4cf7118fca5367d4cdd4155208650022038add348b1a48125de8a37d8724e497e140b69234cff0a3923e70fc305c8d58a", - "id": "e0aec5537bfbb10983ad3268b4059a8b08fecd76a16db4a871d76e3f9d82c6a8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 396243735944, - "fee": 0, - "recipientId": "AFrHJuVb2HT4c4tWqYEvWgiY6CuymhkNFs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ef46b5c739f79696c0b0a9a8f735cec91a0dc546accc233ff32f7e5cbf38b40902200dfdc24247a4f60f3e791fba193d9e9d16d83131184028e248a50ea4cb2a7272", - "id": "f2205ba50118bfce10259a754db70528268ee9112d16da6e2bd7dac30d06418c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 396752660012, - "fee": 0, - "recipientId": "AdWC72U5LSwghdenW8DKXWHYuULfUDrCP3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008a55e63ad2b9b3d0baa2b6a2f80606f38d02abaac071387077ccba86f315a80c022013c555cafa7277020701df0004f2b695e34253ed7cbcd6f2a9e0e66593390577", - "id": "61c5f683b3a21404863121a1833c43e94f7f97acfc4100198f24e8dce6e5fd7a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 398860671270, - "fee": 0, - "recipientId": "AHV54m6LqZG8A1YdPKzvJdBfdoV48VzNmt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220281ea4f34930789c6890fcae28d56ae12d1877038a112355f45a8586a7f7108a02204b0ceb0a989c686b91d84d42cbb5c8de0657c2988d9acfda7911f6fb37a4d0d4", - "id": "8aa2f2aec071acb8418bea239baeabcf9e22a4ec3059777df5f2f379f0dde30d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 399900000000, - "fee": 0, - "recipientId": "AMDpfwcmvCFW1jZYJz5Vk16pytx8zwqf4M", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202512aac371fad98ace991a2c0b3c736cb12b18da8206f5308dab02b71043c1df022031d2c0fee93eadf08de6f04abbd6795f15467d0d9264bd22c67bcaab93a40307", - "id": "9147058ff25475d0975386208f9d0d4cc8c60bbdf77737a09dde4bc168638db9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 406030861383, - "fee": 0, - "recipientId": "Ad8LXbyPdTSYMHvcCZiQzaUtsd8VkLRdUk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201df9dcb7dfcd79218826fc4777d0216f5020377bf2c1ab94e3818f17a03a6ec3022052324948abd06924e123c5e5042f9b388bcfd0f127b962f4450b166207f6b4d8", - "id": "7dd69f0c7424458dc39d3500fda4e8b1e5faebb079d6917f908c88192e03602c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 410054755558, - "fee": 0, - "recipientId": "AejLSizWdJowzJq3o2DZstQmyZNdqGGzJK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b53a8ca2840af4fafdfd607ba7a2673d1a3a70c4c3001cdfd03998e1d85049bb02206d11131b4410a8214be0bb9035664caa24e5ac6788ad7201442e151ca4e75b2e", - "id": "84fd6c750dbc8697bf563b042b5590d7d543e0f14b07cc3af1de3a89ceeca224", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 411430774232, - "fee": 0, - "recipientId": "AQwJBgy1TXokikH7XvqpyWtgAexhvnEX4e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022005de47c592104b12833c0de59912e4f98001ab5e263af9d085828237f378a73f022074efe6b3260b7fc941691a426dd73e567eae101b680e360652e9c1539dc26c1e", - "id": "4594cf80dd9c58f780619f1c5b07ded414855e984bfe92f9c8b5e253703eeac4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 414435446144, - "fee": 0, - "recipientId": "ALcuF8EWQZo6wHdhEab3SPG6NNnUhxmtrS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be8dacfc1c3bf51ff37c4db4df096642aee830e9c54b03da3a2cb1093150099a02206694dc3b9cfb53ef9c9af478bb5663979bcf740cedd5c36dc892ae50ac2b5c38", - "id": "3683470aee128d9e3198d1367815a49543180de9e5b6a354c6cace53386b998f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 414495122050, - "fee": 0, - "recipientId": "ASCzbyDxfQipaZCjC6VR5hbGG9YYgsaAwd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b9aef4b9f36b7c0fefd1a5fef061047e327f54935080c118cbf913f607e42d1c02206edd5cbb8feae8309a3ea4e53ce4a86f9d91a084d453cfb51dcbff13522ce002", - "id": "198ee58a22c79eb720667aa9d17e249be4e2eb3114c7206ab0a73b7c9127ccfb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 421404948418, - "fee": 0, - "recipientId": "AcZ8qLedqB3pqDv59QMLD5zLCMLEPsZiyA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203294398344f0e3863dcb9709bf6bf915ff4c678a681edf48f03ed605ef990aeb0220247299f0e0a0abf1595d9801220e2be6ae9baffd4611aa62683697dbfb051f92", - "id": "83fbac69f11ce4a4d5993f8b7c60a4d1da9738f85e0d2e6905d49f41f62f22ab", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 422404168409, - "fee": 0, - "recipientId": "ALEAsaG5tcombmn4ewk6CtCZcT8jxS5LfA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022044a875f285cb908df1025a9246529b88c88320c3d2c5b212721a67afba69304e022035b80eec3f0b523df1d8b235f9c9ad05c569e33f6ed4b7d72127ac88a8e04a51", - "id": "f01d3a9212807fcad775b20db523002d72db87387a2c0c0e6d6e43a96f76e10c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 425000000000, - "fee": 0, - "recipientId": "AaFVVK4bHWAULm3enE5jFuW8w1tGDVAtPP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e4c2fc79977dd2c8202b0b9b710c1d345266b58a583911b8d7dab80cf6a9efa8022043b02b4273d62a6bfb357060314f977c8688fa2c9fd9655f1225d76e5f210598", - "id": "a9d807156e0201c2e6b62cbcd86df7e744c113443547394481215613463aa517", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 428842921747, - "fee": 0, - "recipientId": "ARTND7EwNVQyRRcHmxRMXD4vDxMsuvWHe2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206d2758d81dfb99fe616979b5241ddc7054c2ba0661db8534b27f5a15a6e42f52022075af3ca1a436a580397f182ffe8dc07734eb4b2a67bda747135546f8fd3462f1", - "id": "30cc3a106ccd48d7a88c1f6d948d5701670c7541ea85d7472bc0a8e66899a9a6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 429330120346, - "fee": 0, - "recipientId": "AUnowRvHGnZnpPYWQhjnP5GRh8waH9oaoW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220396919ca6596b28199114e6bed458f8b3fcd53d1bc01b9c55a3e8c393f2694480220072f26bdc63d2ca9bd7c3c4a4ad1e246676055338077894de505da12f2b81a8d", - "id": "b673418ffcaa8a343b85cc02856e8d1aa2167c9e80228b738ac3d9487a56adf1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 429900000000, - "fee": 0, - "recipientId": "AVtEhnUNTLMG1ABGW8MxoprpQpcnjVMMs5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a4775a3edb88c12d3117057cb71c556f460bada4d578d14d52f6f16f52dbe1e502206f0793cf3cba1bbfb17195e79343b6652414cd85a1ad47a6e76819f58a41e794", - "id": "91d608f9137abb9d4f30446786eca2f74cd9173b0cf9e47f3a917b400bea2d78", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 430758143690, - "fee": 0, - "recipientId": "AcsmQm5EH94A8bmxVhBX5azqRZ5HxSFurH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ffcdd344a83d815b35399377593e907593848c590e4f6b690f90c8d87970f7d20220098ae996ee30c244bfbe04fea6f0940488dace06119d2af396b7d08dd925a328", - "id": "9693698248b30da4d32f3bb74a94185545a22511a32789b6e7c6a62c22300d15", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 431169218840, - "fee": 0, - "recipientId": "AaNpyjWJC1SzuYNtPd5eJrfdNGD6syTzLe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a19b4cae750b166dc50c3e22bd12dfc5f490b58cd13c139412b61dc7acc7149502200418a034b9a2b6fa26b12276e5bba9f79a15ddf3307b51ac1d3b35316087a080", - "id": "074cbdd2b8b306c0883d1e6d0a468376c98f00274da7467af05095edb852a044", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 433300000000, - "fee": 0, - "recipientId": "AS31C6KTNhEc6P3pHgpZYg6L6xduuqvSXW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200369f8d253aed3996199457e932c7f1b95d12cfbac50abf255e04a331eb6b30302200c9643963f4b856e9ddb9572797944164f4a2991e2073ddd0a9c0853da0b969a", - "id": "bed6b1d63572f48b674a38739a4f33608fffd9267989b21144f3fa1de1f6d39f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 434358663505, - "fee": 0, - "recipientId": "AMKqjbsJKvsiRyzBwc3htV5V2sqrwkXpzK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022026566e29b8b649d199aa887ab842f5b815df66865cc04f5e1d3a26378c3a39450220284f908cca59b861fcb512612c0e66a0ddd855d4472d0fe6a96d113963dd0ed5", - "id": "73c577979ee440ffcda796ac338ecc04d7a462b69c17336ddcbf05dafeff9e9d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 441082175858, - "fee": 0, - "recipientId": "AJCDjQNpuuEN2byRdGY9xf2qRBCeASsCv9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203e7de0ac5c2dca5bb3b367f2006e33648f0b99fe5321af1cf06923f5638376e702207b223a41226368a7fbf51a26ede9ce708c86dadf563446aadc82eee78a142f29", - "id": "295bc81dc9d3e434b8b4cd76bf32878432742128e94b10f133b6f8596e4ae65e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 442296547242, - "fee": 0, - "recipientId": "AeeJpSnh6sHMH9QFnaS8iNQUxHX5kxZwa1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205a6753a13b5dba6748e93d9ff8c9e0b243916edebc82d50f26dfa4023c5a1c97022015cff0083bd455c998ba99ad3836fe8f569dea1b16e46f3cb0a1789a9015abc1", - "id": "db2bd8f694e5d5d7193469bc9af661d54ccdc1bf5f8dd1672648edabcec5585a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 444900000000, - "fee": 0, - "recipientId": "AYBuKPH9GghfWpVeoXmHheJ3hFEHHsPLL6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022076e35e14f992269d3388839ceb60bf32ec1b02a0ef6e63ddab97fce76cebe95702203cc902cf0c383317ee84a5b6fe7ad1a5d0e507d3dfb9a5e1cb6b72bed9c2e013", - "id": "05b8f0519ee71468c7b25ee5d7e80360de95b6e2076886f3c1407f0b1b2c9f35", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 454296574143, - "fee": 0, - "recipientId": "ANdCnCPbZcMmb5DcuxKiC4Cun8XfNpk9LH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e4072324402b907461e4bcfcf4b064dad72e9099acd171f27d208a9a018c2c4d022011b20091c75c4dc05d32e8c40b202c910a32a6fd18da1b531ddb4f5dc677a0f5", - "id": "9c35f16fe5a213937cb9d260efb89e114bc59310f3843280f6f858626257cbe0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 454410176686, - "fee": 0, - "recipientId": "ASDxEzhr4QLiKvE8gx8NyBNpNJBiY93bBN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be0f72e16ac306667849e550bfa53a55f77446ba1b3e85190a28c789d80e2dda022021706b1b73b64cd7d98e85c79e9787cae57efdaddf31c60e9d13c6530432d09a", - "id": "68038c71701292ed7e30371c8f58c74de6926a9eecd9833f205e85817b4880c7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 458329154329, - "fee": 0, - "recipientId": "Af4KkTdudJhshRqyQknHmieMTzgN1baKGk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200d98f84a03352a7404824fbf2565597e3407f554602a05db8c9e94842121de5a02201500a98f7224e8e716c2719d8981995c6a9cb0c38b456842b8b219c6034819b4", - "id": "13437129b6e18a12d3acdb01c9e113c21bdd85e2058aa56a460af7f3c8423501", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 461698156433, - "fee": 0, - "recipientId": "AJJ7HZHTZGmkLjGLBiWJ2ni47EVVmsPAcW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eb4bc75f85eb983bd0b2a1d73ebbaf29c0163ba020b3fb85781ce3c1a97d6a49022071bb413f07e6c7b13f6f8c82dc9d023da2ef6f262883aadbaae6def1dbe5cc1d", - "id": "2e52fe4e3801a2a567ab73693da5448d5f0bfde55a2f0d6f4052075820ecae65", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 462363719366, - "fee": 0, - "recipientId": "AVsMnuEh8yCdQjAtc81NZzRsxJJDVjf9Ka", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ffd9156e78730c609974cc6a31a597d827cde890c7e5de46bc54cb10d7b1d51d02200303b539a531b513eb059a15ed6a5b08f9299e634348e3228ba323cee42d16bb", - "id": "5ce506c37e536cf8d7da0c3d2208bf27e461c5b261b9eae156808812fa14a6ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 462789386912, - "fee": 0, - "recipientId": "AVmGqru9A4Zpn3kd7goY1kJ3h37nAaxkN1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022039e8e2eff6ee9b7df8d20d070143f244f3a29b4c673932576e2d1d514d31d7af02202a79c76be0c59c0d27af4a7c5e45da0ac00944eaee1eb9aaffb3768eb3986095", - "id": "a3875b9ae99d76e48778e0b37392498715806214a2cfca46307c828e0d160ba5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 464492402483, - "fee": 0, - "recipientId": "Acb9QU2CSwSXNcbZARBwyqBSSsQRubqjHq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202e6eaaf793a04b8fd064315af7d6cc5b911f86faf21346fa2a6e013bdb2191d3022038a043a4914bc834a47c6c903cddf4dba4eef84885201b9c4a637cfdd9552f55", - "id": "7f81ee0bb3fe048921ccb934a4476c15a339019cd8bd3de40685062281d9373b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 466008654301, - "fee": 0, - "recipientId": "AQ2bpZffieDKR6TFatkS3vZwDLyvDpA6Jk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220516e15a8cd6382b7e0b93ee04ce1ceff0621dbb17ec2f79158a1dfce7d3829e902201383e9cbe11f387f87cd8e1701a0d974c61ea92f75090620065125f1b283ceaa", - "id": "838d54e1f7681e910380bc08a02aefa3bd47d347c10c9f17030cc1bbdbc9067a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 468845108203, - "fee": 0, - "recipientId": "AbBz7xmNnHkkdww8fBK7x4EcEi26V5sqr6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009364c19fed0bfc1c4566e19fe01e83bace4233e7f7bbb25ab08971ef5c2e00500220118bc321de72db6de795781e1ccfe1c587b89362be1a8f9ff43d9ec1e2411633", - "id": "de57689682daa61b89193fc451040ae47b6335ef07f363e86bb37ff0b28f75d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 477386564720, - "fee": 0, - "recipientId": "AT3dyR6SqoMafVWKsMvL9h3iBvsU2Q7S63", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b8e109b247b4d83739b36d32ad9e54f11979bbb54ec0629da72f65274e468d7302203b1370fce9d9e950120e102ebce751b2381d2319353e5c0313c6c84e5f24fff6", - "id": "8ec9dac6224398952f9c801261fa4a901ec3e823bc47301201c8e1d377b77bd1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 477386564720, - "fee": 0, - "recipientId": "Aeb22zZXfmhNvEjUH6H3ZT64fJCEqR62iJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009fea8a4720368c5228f554d16026c0286bc036d49f5de51abfa2689d27e54346022061f2f0b0165159567388103419fce40a4b296079369bff483df251fcb9427764", - "id": "a47039058f275732366634483cc82c01a7ceddcfb6fe448f4e60eab614ae0e64", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 477600000000, - "fee": 0, - "recipientId": "AWhmJrkMyrJsdGjVaz2LoqAboibJFdpeKg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202f3417cab05063c88942c5011311b5d19f7c05bbffc67e6ae090388803a8639502200a05fb48af403296455264ca2a3a3f6d4b1cc89706877be3335fc5c490c88692", - "id": "b6c19da13c34a12fb2a06d6a081b172074f07641024f3021b2105864e31faa49", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 486438883282, - "fee": 0, - "recipientId": "ANVReFpTPz83WVcVZocHwuNwJLuGW8Hg5n", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc6894303b0552ad18f66494029308976efe651de9f707a0187059e33106431302203f70460a4113ba5b089efc2d85e885832a349a9860bd05563c55f5d5923bbca5", - "id": "2644a2b4e0c87b2b0fb23f8da21f0c085ea8aae031436e846d45d77699ad1c28", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 486986935045, - "fee": 0, - "recipientId": "APoqq5cehPy8xdqgY5nPMFtupDK8HcCWFD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e1ac0a3e424f949cdafbabed541a5fe658eacf781e059b22460f1ccceeac7554022050119d98a70c087621994890f55d3d8a79e889b02cc186f8be1ef04ca4ede1fc", - "id": "0bb8b3edd973b8b51a57550b49252f708528319de548f0b89a77aaf8f567a685", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 499452432600, - "fee": 0, - "recipientId": "AaVHPBJGnHGAyDsXqcXMkgnTXrP2VA8zXd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202040b29b5ae655905a4be13aa5160fcc682c0c3df0768ef4c15142e0b6d79f45022070a5b44807bfe60f84455ad05957fdee06cf677a99279b25c33eab56b33bba80", - "id": "a8a4acb7cd42fb523baa102fa6dfe72371a2686c99d18e8f26d83cd65ce69e62", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 506413677424, - "fee": 0, - "recipientId": "AViKTbBER3QdyjPYvu6aEikJG6jyMZeBMH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220575b7844c2c7dfe981396bdba8d577ed88738a16e4307c522077255c5db8225502206ff9da12d3b9a67eebfb1234918c19e92b1d94f75983c5ffc8b5598d0992a40d", - "id": "a424e155f169f58f922458c89352fc80a71a214a9b7f5d7edf53b833b7e9bb3c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 507369287698, - "fee": 0, - "recipientId": "AN4SVH6wMd1J2UinEszD1m6rNd2Qf67s6p", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202fc2e97188ea29acbbb5cb669a91834399df4e95258f843e5f508b2ae92bdbaa02201503b9313688be4f868f27960bd4ec9d3e26b49d09455e7261f811a8f4bb9f64", - "id": "362dce6072bbf7ddabf9cefee0b27fb43eae636f5d4a0d2f777319764ae643b9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 509141981427, - "fee": 0, - "recipientId": "Abjr6LJqKumLBkGwWdCmqQU6ySWNTvYs83", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210088070a6fcbbe069be1a6a7b9de2ac4baf72f01078d69e8b3203bc85bc87e68e802200993087e55d13928a55f3a543cb91e780fe082d1a571da71ad6e4edd4ae63af3", - "id": "980d85c7519a5bf12e66c830e13372ce93d7918dea83f61bf88aaf57e5d44e6f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 515274387317, - "fee": 0, - "recipientId": "AKsTx2HPyBzhbNiEuUTjaBefkXsbWBmAzj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206799aa5ca25023037fc2046c9976c29eee3253c6110d6d68da3b38a011f85ae20220498f5755684e9f2525f2080ebbec2c1306b18dbcbb1ef40d9006f17b3e12c30b", - "id": "0b85faa2155edb7516010b7b78fab8170527f6f51a144eda464f9bf148a2997f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 516873898330, - "fee": 0, - "recipientId": "AWP1xqsXMMgBDfRpvJZum4fywFLtRvT5Uz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205a98023efaa4e52f9936bfc80e4f8d4230a2c907a7723a7766a7ec0eed9992fc02202266a5cb028f371ab13c03ac11ec40a711c0473834a595da18591c2293622f15", - "id": "bbf1e033954f8086c4ba041a94635ae3ed8e0a34fdb0ecb699aa1a62edf9dac7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 522000000000, - "fee": 0, - "recipientId": "AZydcTLh6qTuJzSkt2i7j7neaaMxN57bqL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049014a7b3c815b9989d7c4da1275d9381d5ea1bf94fa0197e2f1cce3093f9001022018b6744495c6da6cec8820e4d28edac409e2f4a9383ea42f918a792c89fda18d", - "id": "253a6707bd401ebd1b7d1465bf5857db4aba9e8ad438b8d97d121a7bf3a6e7c5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 522851951837, - "fee": 0, - "recipientId": "AZ1BHFwiQLgXfnbiW8poyzX44z9fg6R7Pu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e6ea691fcf6ceb3735eeca8e83cd6be8664b9c42bd31aa0317fda910b154e65002206556f64d1821aec7395eadb7d2aae1a5a95e7a063b62ca6dcb6176d3ae8190df", - "id": "a01d46e24b46904aa9a2163d3950c53af19460ac839238d03c7304ac83b33820", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 526711619581, - "fee": 0, - "recipientId": "AUsWkvkAVgvaFhj9VTX4U96FyGFh5KbdZU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f4e1b32ed7f1c7831444c0b2452e8d329dc9a0a439da676a5a56d0ef2b8c266902201bf1c1a13f4ecd6289b91e866e73b727ce735baa4128f0825acbd976e147ea32", - "id": "c22131de3dff92e746993d7a01636fa1ca2dd20e01d4db0708271aae6a63bb49", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 528376108643, - "fee": 0, - "recipientId": "AUuTNzGAj9F1aNpcKDJfuDzHYj2ndYLihD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ffbe2910b4d88d818dbc798913af9ddc106e423242640cc712e0d55c1a4a1d602206262f13fc3fd933e9f92f1455ebe280d33727501dfca60f8dc1bf85bfeb51ce0", - "id": "b487b250cc3f98368b60ec6c5fabd510e1a8483cc03bcd74d4931ef792080712", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 531321344927, - "fee": 0, - "recipientId": "ARtGz4V9e3wzW3iRtpYo4DbREDAvCwAg2k", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203776cd4346a82b61c46c2ae9fe7e7b5e412b84727183646992b4764249881c59022025e092b1d5180fb4cf23717ba0de4444e52067414da0bf474752763cdbefb8c2", - "id": "fe48867e9156b1ada766c579bce19e3d01f92164d9c265902b7a6c20159d520e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 536606958259, - "fee": 0, - "recipientId": "AWFff1CkyqJWtugHuYYE8WWzBP8w9qwuhL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220024c4fee1e8ac337e575ed098a4ecfe070ada692085295894459b7b7a3b86c37022034f2e22c2438ebf949955ee0e865b528a781da86b942321eb3725178a2f8173c", - "id": "f1fcd6c5c6b3c83d0bd5accfcb929061d8804a863015cb094100ed09c90b0076", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 538220305472, - "fee": 0, - "recipientId": "AMF2nM6FEMBBfv7qWc9tW1TWv93dxWbXjn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220315cf7c033bfeda43ab267511e88fbe392380aede5ab0087e7295bc4b20b0abd02202524f858f7d42cc1c5fa6866221d7463abc1b94d0185067ff0b3dd57a182984e", - "id": "8e26b02745f1f5ced11fa24895b0bd773a2db5370126c1e92235f1b123570548", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 539888138573, - "fee": 0, - "recipientId": "AUoii6QU9zwWhRJzLZMF71E6dffv7c9f1P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022045f6e18076dffb721463d1e34eaf47a3bdd0d1d0e87d19d9e0a2ece49c9ad71f02206a0b723c4937c7861579b098763ddb8c533a7308dced9baf55d943b17e8b6237", - "id": "b56baa4d48dda93fd85fcfed035b4ee474f64469e28eede0b2369b4ffe17a542", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 540008433875, - "fee": 0, - "recipientId": "AKpuB5YvWrEzgxGqs9RSGr9oT9WQLCyxaF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022066b1533fde6b296e7cc40d4764676bed7e9e088489e2482d9d7151f845f1c7c3022041502f6705fed4575514296bb157a9c5ad601758992acb22be436badfbb66f21", - "id": "85882091ee2b032360bfc72a9502434cda2fb938f5362225d68c4f77c3a47600", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 540816219226, - "fee": 0, - "recipientId": "AGGeFX427aMiLYXk3n6u1c4hup1X2STgdV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220276e0d0e57371d2e5d6d9dcdcc8f9f1267bc27f640e95eb88b8024716e019743022034bd26c909f2653ca8af6298aca77609e8eb367e16f4b4507ad280beeae51699", - "id": "d966f146fa457ac9906b6b928bff9807416e8506483774c8948c0398c0481435", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 552260891955, - "fee": 0, - "recipientId": "AQrYk1AbtQ2brZ3dvsbHh8rdErYGJb68pi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a1ad97eeb99f64d9eae5da29ec69a174058788941b7d7d43348e0e84a8d04cc902202520b1fc1ffc869ab83da821886b904d1e94193dd33210b5fa6cf38a1c9ea9bd", - "id": "ccf83a1e1d918e27052860875f0abf5e55dc14f6933c432eab13264919a60d6a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 553060575959, - "fee": 0, - "recipientId": "ARMwrHG3miRYdVZsGYmeXKQULB6GwMyyad", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200110ca9f26cf5a36a36e2b035b932b98df256209b95465a57eaaf8c2b19c05b6022062f55565484251018cd6e30fe19ae6387d5bff36836dc7b09fcfb5c350efa4c4", - "id": "f8aecfe8e66cf62d917c2c231855f9d4a7a479e918464658a98d4cf5dc03edc7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 559276526635, - "fee": 0, - "recipientId": "AUbZ5CjroZETpDU9rmQtJdVU1ZSBHhxWNk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200c8f7e1cf87873f2035f917f692cb58ffb2e8a5ecd68a13f25e8ad08b3309eb002201b7d7b59ee7e63221343a44e4a04642118e38acd08ca658ec01bf620040f7ec9", - "id": "d94bfd69197aa074ae9ede3c8abf9fe3a67b49bf498d3c620c364197be028b16", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 564299912400, - "fee": 0, - "recipientId": "AcMseyJzDq5Rsh2RYqJsGXYncjQinXDtvX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a5edcdabd03e64a2e29f5bbf6427db67691f39b54ac517fe46fcf26b3515db9a022074d62771c4396e6252d56581e6413ed4b24891272bc7c8fb2a3ae01d2b7d6161", - "id": "a2f8f856798ed3ea34daac7d04f99409ceb4b58e897f8a4a8f05e1706926c64c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 566286551662, - "fee": 0, - "recipientId": "AQFkoubX26ch8tFBy7dm9yG98LLqV7cMCo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206e638a8e03710eb44c0d7e8ac09b1b38366156bc8c46e1a0ee2f88424b86c5b40220247f979fe46dce7faff11bff26d3fd1bcd510747dc857b9cce2c354fc8d31043", - "id": "392e37377917bbe974e3c8ce837d0ecf52f41f1bbb7ec2e0056d0b09fd44620a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 567899118315, - "fee": 0, - "recipientId": "AYMj9jGadgkv3uWMpRr9f8P9poV9HPqr8c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100937f17e1cd7f010b860f1f87e21802ee7c72e826564e9eea6b942b862e56f0ef02201673df293e24e84c2528bc70bca4ebfa5f73102beb49bbf999ec169527e2a3e7", - "id": "efb581f15c255041206ec351d3ee4f0b20792e6f99b29da27f9f588d1a5fb34a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 567899118315, - "fee": 0, - "recipientId": "AGpjXT2iG15ACjpwGGnZuZ1rcEB8WuwLGn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b12dd8dfdfad12e59a4bd1b13e589a882674f1d9ddfd7f91286e10def66a8e9e022054bd93d1ea02976eb99f7d2b12c3df7b4d10db3cd77bc02810332c24a2e26227", - "id": "4aa30ef712b9518114abbcfbcbc962414af90b4618d22288fcc6c9b8cedf4453", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 568012720859, - "fee": 0, - "recipientId": "AGM85ysEstriQdnff6hLY96CNegLmfsTNx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009b5a8d8225cc5c573f92f1f007c19ee1a702093e08dde891385ed3c19f00258902205e1088df5448c3a8aed0cc80a51b16db6759b80cdcfc318ad0cf70bb1eb19ca3", - "id": "32da511a73a5b2077d70a3299332790197c37351f0f6a10178cf1f777126e6f2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 568126323403, - "fee": 0, - "recipientId": "AWkSJ6mwXFC75PCU2Jq35ddXHyDAWETy8d", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bcb4517f522f1c416d6ff3a5112dd22b15de6ac76fa7288c3e53859265b71208022046f9365a91793e947578c86fc7ff612c6f34d468dd6ea61f35b7b4467ca7f87e", - "id": "6de463003b0a10c7372a8ba2490533ae3a485d916b93986d2049980bf3771e8d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 572950085963, - "fee": 0, - "recipientId": "AXQXmHt4c1EcpxFAqkC9Cy7J1ui7FJaTUe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210081b78d77da50f2b94713bb862662883a4e0dbe744ff6d41e14c5448dec5f99ef02203d4ec23476ed4adebfde4e7043cb0fdf6f7a07800d0bc350c4509baa698eb45a", - "id": "2521232eaf12c8a956b5b0177a86b430e4557aa782be3719e3220a6ae1ef77fc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 584622281689, - "fee": 0, - "recipientId": "AXVz1XQf4pdPdXgyyU4VCFjsRRV3i9VrbW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220296a2e02be62e0a620e5b5b166757a449fe5c45829a0d41efad153710f94d065022075f637f41da115284df9e0a3782e73db04e044935a60a90715555949add9a470", - "id": "1f4180750e1f7c3f5c48fcb94104ab167e677eac601f7414f675c984e8dced44", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 585926998753, - "fee": 0, - "recipientId": "AeWy7BY7i384uzceaAbtxT7k7N1RkQeVK1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220296d7c9a6a71dc7d9803a3c95c67f69fc0aef05e2309dc3038611d28a712f8d5022050fdf931fddfbdfff9a36c4a554a7198c3ac011d462580f103b419b6f9a996db", - "id": "f8ac8625237e4d9d263547e716bbc276f536c7dc5612a9b102195f76744e0d78", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 587913213597, - "fee": 0, - "recipientId": "AKEyZbWJ6qKChGPXrq41cHZk4Jz8xHTqh8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100993c7c4f1c17412bc3f44f64181821c60a5532272d1f0436aaabda139274900f022010e72f7d9343ead3cc02fba0e111674f31c2cf573994415adeab4d664acbdcbb", - "id": "8691098d97c62a3d45c08b8193b7cdd5416518973eb699d713bd826f5b978fea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 589063195094, - "fee": 0, - "recipientId": "AeZ5u74dBxufywKh6eKBafLKBnWBX7F39G", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203f7a428270130c3cb5cc77cf307f8f87e3e99da03bd9c673805de883aff6b4f7022062a643143c9be2a68a3c49ba308a96d89920130f4e28bc688a687b05e5554ceb", - "id": "7ef826360e319242f436e15599caff7ab705747e617cbb8a2e0865b3821afe7c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 592519033621, - "fee": 0, - "recipientId": "AJQkUYcMuHxLho6Ag8yLiZjTcjhMXZ9aHB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c2f173878f73f99e2a044fcca1fe6d6e5f116c30a8153f0ede31f3c1963b19500220081480f6aba6fe8568d84a6ecb26c70a8b6e418c60aad5386148455d9d361b0c", - "id": "af49856b1c3f6a712330fced86bbb90b577150af1568b8fc64b024eee487daa4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 597999101241, - "fee": 0, - "recipientId": "AGoFy5eoJDS7wWbNpAwyDQbwGcWHF4iAe6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e0a578e2efa657e551cb1ec1fc7330b08ec8aefcbf4458f720d7ece9b04b655a0220065c046cf75908b41f1d540e33450ecc4506f838818d97240d3c256562bf1daa", - "id": "50a23397ce1b9f9af4bdae64c7658e7a98125d17e803340d310535c11aef57b1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 601400000000, - "fee": 0, - "recipientId": "AYqXPht63UU1HKGMYSTgmgPoZj33zKWAGV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201d214d26ae57304653dbf4deff3a2f4c2b5c0faa6451821a4aaf0851a66f77070220563a930f4833d36732cc2845a318e4d59cf77c2c7808eab9fa80440b6649c3c7", - "id": "545a75b3f45b8d3ceb447f8a573ad527853ea7cbfcce54c2ded8e8add84b1574", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 603323064588, - "fee": 0, - "recipientId": "APue7kzQNznGLbD361AwVKyfeqk66Gp8pb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202105a9341411c738f541af56b48370e5c670710f26375dc77c968e3a3d0306ab02204218af14c3f11038101be19628a0d34d7be466f0dbfc41518440498e23742b57", - "id": "ca525d14c00825ad6f0b1f41b8832ab154464f55adbefcdfcaa9158f195633cd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 604220262324, - "fee": 0, - "recipientId": "AamyoMmKy6vucKTsu13jYFnP4GeBy1RMum", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022032a24879fdefdebcb46b671a4dadf43e6f84af8b4d58646044a13d2dffc19720022052301e1eedcbb6116cc327c27ecf021e0e69345fc8b64d8864de1799356cb052", - "id": "5fc2c74fe023282f5b142ede55e47f0b218daed782c307d3f516b1707afd89ea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 636515419627, - "fee": 0, - "recipientId": "ALPyErixj2dXaoQf2aFKR5qRWrpQNSpVzW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210088327da183772be9161d481fc69fdfeee11f629481d8b564146f45709347775e02207bdac01a99707e5fd59cfdaaaf9aff15b0171bc5c4fd2a8a97695c5adae6e49f", - "id": "783b96a592c50b102e9e73a1d69279352e842cdd8dc7b967b2728f6f15031ac1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 639468721143, - "fee": 0, - "recipientId": "AWTTo6v3d5AwpVu7Z4zMTdzGt7nStpphgS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022046efb627b31d7f8834c4cb53547c86b346cc0a0c52b00d8bed26910909cb759e02206c6e2c5b6d5fdbe74e6a05006fcc9f2c1c0b2e3a488dc8f9baae9c1e4f9f2538", - "id": "b5be81aec8875f631918ea7e3471074c1ead97de2eb760b6367d43360f3d7917", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 649700000000, - "fee": 0, - "recipientId": "ARRLBH2goVLrow9EgBstBpS13mjmKzYwNA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210091648b7427049df79b49484ca0a5881a3b870354a1e74c38e56b83849d49fa43022010008dc0dac24a564eb2d82d51353602ff7d7cc0b01be101aba4e6bd8a3d5bb1", - "id": "f7d43309c24a58b257df1477766aafee1d1f4a41d4ee45a38fb874fa62517e29", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 650982673120, - "fee": 0, - "recipientId": "AeUoiRMhcgcw1BozjW2yesuRNsCaq4WzAt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e7623c82f69ca00ef2b0f988cdf38f53ef71067be47637c7db4da7f010a33ac60220429410215eaf06057c4f8c7d1da490340b0147a3d72be9ab9b1f4e136451cf98", - "id": "f3c675a4dab04c4a3f7437bf4264e97402d36f43bcfe84f5c2a874c089509cf4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 655486679871, - "fee": 0, - "recipientId": "AP7kuQYdDVNaxFpBy1RRZCtFJxEfjyafk8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220157e94b4569a8eb2cf78c543010cfabddffd8fddf12b800c5ae82a6a90605e95022041d4a41672b6f85bb8c2171a033906eea7043a35f377f2c1e07ae09c07906f53", - "id": "8e4a229041acf3eb033f4215adc26faf8bcdfdd1a8f351b1f97bfb96972c48ae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 667854242368, - "fee": 0, - "recipientId": "AXyLBiVC4v1vyXUwVCZywuGPgzGE6fwjfr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220117105ef4a93b506305144a883d16778f4d15fb5cc7cdc4624770e1e0976c1e802201ef7dfaf16ccfa96f2e408f78f7026377a65a59d1289608c47fd6c96f9df8730", - "id": "dd2a692bf311ad077ab9e0eb14d321fd4be930012b9ee7439cee939330063ce5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 668477464923, - "fee": 0, - "recipientId": "ARanutQKuQepLjbaHuTGh3yvFVzDbuoWj6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207bf98de6ba2b128320e753ee96a3740084cdb6aaa13b3452ea9c6dd3afbdb6ec02203a9c6e40cf99cc6694a504c913a3eda844cdcd0e89b594903d27774a78b96ce5", - "id": "a428541fc5455a8e86b42088317a82ec539ec88ee98e2b7016a8aed336046fe0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 669856703512, - "fee": 0, - "recipientId": "ASBZf3gbf2VLdkaFp8rkVRGjnCm89HRfEP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dfa8b07f91d5ac3309d0302898299f4695480d247e75cea9c316aead4eda1332022039a19ddb10f1b4b4471a6ae3af7cc70cc9a2fc5ba9f88da5cb14e25c1cef12a1", - "id": "3a0b61cf0227f3a14b973e4d991b3c892ca9c4b40f48c6b48dc0e12a70939126", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 683962013271, - "fee": 0, - "recipientId": "AM6uMWQkzps2vJqMMMMyEuZLNa8a9HetfJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100aed03caf6374a94b18218b1d6557cc6b24336e14a14932f1c3de017d61d969e3022040ae6f228c81d7ef90a5cd59d57844e7e712c981cfde43b4f7bf5fc590550995", - "id": "1ed84533e28f9b77cf94331c50903a74d10f648d4bb290eff722fafd5ad726b7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 685060228654, - "fee": 0, - "recipientId": "AMwuubQrccxV551wFW5oTmKWZhvbojTizD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204618ffb59c02a2106b27c71d14148d1932968b0c31490664911c343e96e5316c0220313e2e7f2387c05e242f162fbd94bbfaa3c991aadd853ebbe19c994095cc2464", - "id": "07c8587383bcb1fa6cc35ac3517ddbb3ccbaee82084ed71682c81a53db7b7995", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 688309516953, - "fee": 0, - "recipientId": "AS3HG6pG4cgtJBHiai6DrZfMdpUyTFPkB1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022019ddc6fe655b187a051a3d7a0575c8f3fe2b6ddaf74c69c8c6dc84a05e67d20b0220568dc2da3b1d3dbd1a712a4dc8e492500b475a08200bc70f62f73559a10d80ed", - "id": "0e1e914e5931d52145327aecf15e302d44dd38bf5c87756af8cb22a0cc1b8177", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 690823753611, - "fee": 0, - "recipientId": "AL5qEAPm6HBApbNTqVE3awEgzr65w8JZA1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e7ab0f12b99f73cdec3b34ee4b52d3987aaef73fab54b7aada17d807428cd0730220383d3191195bd2819afd54dc40bdc165c9271a201ecf57444c6ccea480800d1e", - "id": "cb4091df5629df4208f1132ad20d8beedac84714e5c73e52e8dd025a2d571278", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 690970768669, - "fee": 0, - "recipientId": "AMRtZ94wfCYmF9eAgK8345Hy9unPa1GKWE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022058163e796f8fac61be71bd7493ca8f729067086b0a850bf233a0f719ff7a4d8f0220126c32a7a178013b1c955dafa8ffc3ff7c785a41f9ed089fe0bdd5283ee2bbe4", - "id": "711bdc5c2a1da2fe4c488397f938dfd9569779581ad321f8f1471b2bc363e195", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 703958360199, - "fee": 0, - "recipientId": "ASCegDxvnqHPGYrUUS3YoX3fMC5B6WqYGx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fbcb790ad306e8b67dad036b7b02185dd3f40e92cde91c942ebf65976ecd98b6022033e4e5eed3dc65ed5264efff8a3f581107557fe0033fc6ef440e3a1622e4500d", - "id": "15c7116a20f1740fd4fa8c6e93c23b5149be45117d14c9c38c36684517914c32", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 706898356567, - "fee": 0, - "recipientId": "AYSpB9rSgnR9Cp9siWsdPct9dKKxbhBnnL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022000cf165386c43ac8d18e3304b5eb7d7a58bf1eccdffdefd3f5af84fceb94846c02202cfa34b89f205b55f7a8c90ba72bdb8698ad1abc18a87dd650fd41c348592f53", - "id": "be6736d3f90b290d9761ab4976c11397d23d0300cbb09a3d24f818c850c9fe68", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 716079847081, - "fee": 0, - "recipientId": "AGBi3EnW12LZUDcyybsqxbeqLzdxzxTsEV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220524fee405d41e04fedacc0e38c031c26ffbbb70cb273e53106382add945918430220549f961152cdbb902d342a57dff174cc6cd7d6b689d5aa49eac318311784b4a1", - "id": "a5a5a47040cb7f878d89cbd7b9e2d4da7f17b686f9f031b9f4ab8485373a467b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 726541178342, - "fee": 0, - "recipientId": "ANkCc7vjQS8g1jo5pKEvYKvrVbPTVmZxx8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220082613e1f512c2c74044df74ccfa0018f5066c27c1027911669e6a393094f22702204cc71514d9c0c72430d6b24dfa6d8e0ddc3e0d9e9877e5a45af7b66e06c77141", - "id": "bf09ad805c4fbe75d9e1dc6d17b319edf13885f5455094ce30e44c144a496ed2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 727897828829, - "fee": 0, - "recipientId": "AeX8N5GFDN6ZBqf3u3kXNxVHBYsz2jLg8v", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200198881de2264fc3c1921b214379e16bad210f1e3d62b289ec0714e4c47f51b8022013d911c9f57820450630bcb87670ea779e7ffed6bfd700f7be55fdbebfdef0c8", - "id": "83613475e1867eb2a82ecbb92db7506a2e50c0336514c42e48dd3868d5aeb6cc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 732204904378, - "fee": 0, - "recipientId": "AbzyK6ESfRUtaVWgiw79Qjx23QpEpBQdDY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022065140a29fa740ed9049d50e848a48398fc0976992611867e87e7fe0afc72e13402202bfab985f83e5ea588c2a15210e1f6751b263eae649c2eccaddffc07746a0a69", - "id": "03dc5b6869f88c387a1e29bc05e963c99e88a3861a8e6b215ea0c87852df252b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 733825657832, - "fee": 0, - "recipientId": "AX2fhE4ybwdvcWwnJKpcwsU2sHZAQqF9h6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022014e54a8d9f603cd2ffb1b589b11e26c1f74d0aa0c3c457b67fdf614ba46d1b1802200ecc6519cfb098463a51a3a4c8791d8cdfd7d0ba1a7841402c1c61c37df556a0", - "id": "57a89b3732488bdcab397c8c52e9cf56cfc18d316659388d49bd59b9b104be11", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 734634240646, - "fee": 0, - "recipientId": "AJdkWn2xq9mfJ1XbsvMmGWgzg1qXZcpaVr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022020e7d6b80e23a91b7ac3cf255416b0cd1ee4b8c99e7905f3f074e0389d41658002206e2056d8cd76a1017caf5657c0b2028f85e9769b8dffc3b55667f34e00b10187", - "id": "a6c0f4e9a9df5bd241fbddad25bb116624637bfc4aa4938c2167894d3b422d31", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 734928270761, - "fee": 0, - "recipientId": "AKBTUeWQRdFCE3Cs2tKVXdSSxFnm5m4dZL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022056a156e3715c7e280dc5ada73fec5140c0d7d58a36a9d5c2d191e141558cf22b02206f1a3b08eb3f4ac221438eb716232c205e5574753ddfe10c215dfab5961af2b4", - "id": "3fee18fd82e1b26fff178afc44338454f947dd5cbfcfb9b26e95786d7a852a36", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 734928270761, - "fee": 0, - "recipientId": "AYcACtdVz7kyrUu1RyfyYmnSxd4TKYCiAH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009173fbf35ca5887ba7e479349557a88564c45c0f9a2c84555ae04839fc96124d02206c2654536f9ff7f3192f2aa9ef23e612f4488263d77c342b81d0ff9638b59f93", - "id": "50743bc18dbf1b0b06b0b85da7831be155da905b66cb4c55061b2c87329b6857", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 734928270761, - "fee": 0, - "recipientId": "AJPBdGiVCiYb2Zo4ZWs4wQwGpiJxuPT6uC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022001bc153ee897c9a8088bd7d83db9b0beba950a777c79a3e19168a280c9a58929022028538f8aa4e9932b2a779622b99bdfedc92e16cc05aca8e835ed647a41419872", - "id": "aa2748fd0d2f735f8937046030459798b03ff7ace43ab7a6858a1d63cf38965d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 735075285817, - "fee": 0, - "recipientId": "AXe51e9NEuXQPFhY6faMfWJNAru1ccTJ3a", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022008f25a62cf8b062f8264aabdbdfd46a3b7d8ac5086031e0465afb0f9e30403870220207ae7d8f21ef03284ef94bcfa9d0d370dbc4e5b795b183decdd446f89b26fea", - "id": "0dbe3c763cdab7222f45d7062cf94bdd72a5ed8895fa55b268e2bc7626198c22", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 735075285817, - "fee": 0, - "recipientId": "AHPpn51yRx8UnegXydxpXiivh9xnuyshH9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203f1447733e52b6d8529e108e9a59259656da14f448adbeccd11cdf3770afed200220236c460323b60286ee1667c51bf1d8a61e7c7ccc88c90195241db585e886ee3f", - "id": "8e61c93cec0bc73a53710fd0d35fa0d4dff90a240513d20964782de96f256a86", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 735075285818, - "fee": 0, - "recipientId": "AKST4cpT1s7nnoP9fVYvyWSEvnKLz7vT7U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205559fddbf5dc0f93eef3713edaad47980b79f81dea309ec15dccfd4e04fb0d76022018c9a630b655ca36b9865b304ac7a256fce6282779092790079d6dc78fc4cbe3", - "id": "6ca20d3c6119bc42cf161f48f4d82c73f005d8e9e5f602df32fa5cc0bcefa9ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 735075285818, - "fee": 0, - "recipientId": "AGN9gWA67X1BxSdVNjDwuQrpJKr6waamAs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bbb6b489d4f1f55424f74280ab00615213d7c008718567eed1b849a66c9c0ce0022008129948f42b4be5837584c37bcc415d343c336d7a6988f1bc7ef69a698c188b", - "id": "031a5500249e80688c627180ecc8d3cb9cfa38c5ed42ea10d2624ecae5342fc9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 735075285818, - "fee": 0, - "recipientId": "ATHa3HMBEGDM4BQ3VcH1rAY6FdviPyDg32", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100948cf4bd55210d34820da658b900d4d4dfc4f2b538b9f8281189abf139fec2070220749e0a1a746c346b324aa393796276ea95f449dd11058d1857bba366fd228977", - "id": "ac676439da598343cd82aeaad1f306e6d1fa15d8db51bfaa967de14572af508f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 735075285820, - "fee": 0, - "recipientId": "AdezHrqbRQdooEMbLf5HEQjStMcWtKaUrs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a0265aa6bcc52dd3ce524eb0902b08a00139f01e8078412acb0d48021bd54100022061fd1f5fd3c0abfdbf11fe56932ed39ae9b25d468f9e9d2fe00a96b96b60e3ce", - "id": "e9d8c9795d3f32cbbd22b6811801dea2d4b99a3b5dd66013d8e1bf75a12fa573", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 743982782620, - "fee": 0, - "recipientId": "AMefnVvdW3xQzRnfy3RqzjpEjUayGp62cz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022046a419c537be06463ceef560a05950552f61b5429f7e2b4a2aa09ea2ad1d3d0f022038881350fc45a81a7c1c0ad407f258b100b87acb496b8ec6e6e62958468c55dc", - "id": "edd887ebf23a14ee72232666b61754f7835b4cfa4034f7294fc9bd9a30ac1f10", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 758597694964, - "fee": 0, - "recipientId": "Ae9YVNX8GAVUiVH4wA3PqeSMGTDTDeUfeC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022009cc850c616124e30c7a173d679234436dacba9fe466a501b2f645f01a59944d0220042cc6acd2cc25538201006a7a1df425d3ffce08a4408b1f5fdeb73d27d7cfa6", - "id": "ae6f8253bd2b564690008ff3ada0199ae1389457eba14431897332e6588e111a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 762146976638, - "fee": 0, - "recipientId": "AdScYdTwx5YAJSZNXhFfCXGqNpUZfgUAun", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207aecd31e853d981b83f6b4381a7e2d6152afcb06e08f7b001ad88200aee9d33f022018b9af36be0290056ded2c835c80902c824826ec0955e53cea50f4f153fc62e2", - "id": "62c9ac8c0b9bc3590849da10c55d14ed79af7a11aed9c88d4820c043655d8d61", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 764781255703, - "fee": 0, - "recipientId": "AazxWnociyHVd8hBNgCxPqAepYe52psryF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207fa864ffe01d95a14e23efbb66ea9a22ec47b0cecdb564c8842a6c0f0cceadf4022054ff2c650fbd053bf2b022422e49977f6bc4fe147834dca0198d5c87f39169b7", - "id": "3684f2c7ebac624519c3e3fa1751718e07c66bb695649cf049238506d3866e15", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 767271583336, - "fee": 0, - "recipientId": "AKBgENszA84HHwQg1eKG7t5KnU17WLUkDN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022015ab4dce082428342f964b9372d37588cfb5be5f916c8c936eca4d7a4457ad440220632c804557c94797efb8d041f94561b3126d0ce2105f1f51f038b49ebe387a8e", - "id": "a7d10521629488ad012d27edb9083ad6dead0b3a401089fe34401af2da0a847d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 772911580976, - "fee": 0, - "recipientId": "Aai89ucFnPch8ZHFvt5NJ6qBZKACJVYTeP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204335c3aa664c18916e6ca785fd3c89f76b790e77511df34ffdaf5094322dc0a002204506ffc0e8c85e5a68417b78c3c231ce5c48a8a1dc417f1962bf65d6a1b5da7d", - "id": "773b21c1fca3bd52438fb17f97403e5f26aeb20693c591ee9d4e9e3ffa748be1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 772911580976, - "fee": 0, - "recipientId": "AcBCaUbM8cS4sp6s1uXELKv3CESzCD5XX8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e01f0a0d0194e905f33d5189eb1f46da9ec86cd2520af288d3a88bdb3096d54202200f1285e4b0a6b4d3036e1fb85637725532a39e4cdffd51c324af87d4d30d0a3e", - "id": "2465e6d3f6b17da590b6f53136e4af7f893b9178bdfd0f7b5cee9f7c8ef816f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 780058983676, - "fee": 0, - "recipientId": "AcuUUQ4hUm4wfob8z2dy2fgzVjMBYBL7CE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022015bf2469739a014eb0002e02a27cb9873058c6ce26bcc30b491dd1c2a783a285022027942002fec0c30c3f73859f03d0b22d6e6b9aa8f2b4f996fe440ba00920e643", - "id": "1efca8a1ea9b00029fc970c5680d14f2aaff245fa0757ad0f1853d2c2b2e7053", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 783225207027, - "fee": 0, - "recipientId": "Abj2DUoYZAET4WYB73sTyr6udaGA24QSaF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207d994f0790b2279a417323e51fabe58f9406f3306b21846d986a3f7146bb2dc40220246635f1f8a5d028f72d6117a7a743410fc596e89722121f94f8643dde175495", - "id": "cafe13900519e38c9e814edf229a8a07f9f521ecedfeb8f2812d9c8a94aa7f4f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 792264143054, - "fee": 0, - "recipientId": "AH6gRrXAUiWeyi79vjBvysTKJHCny88jpo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bfabfef46710cdcc59a2223e554e3e7cc416890cb9d2aa3ea408724bdd11d08a02205aed7c99e38a0b4ae8991c7daf53da01ba8652434d589aab0122d7c043dff201", - "id": "26c101b6192346f37ee639f28da5b53ff81ecb7ac8802389ea6ae9cc9571fc2d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 795355926543, - "fee": 0, - "recipientId": "AL9EsuL167UcKpjH2Cco6zqiFena8LoURK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220601bcb86301d0406afb27b864ff246c6bbfb8e6668be9bdd87dde634cd44b1f4022070ecc8458fed79816df9f30790a903d99ce41bd765cfc92421cf6070c7e6961a", - "id": "b745abc6ac90e5123dd8235aa5b19cf96e2419a8b7710ba03828a33dc796fb11", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 800000000000, - "fee": 0, - "recipientId": "ALgKXQivLiRjcmchi1vaRBvZDPpYcMNv3K", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009ad6dfefc8a08f6337c0fb9283a07fca5b24dda8145dbb06644284a7f91a0aba0220730b2b1411070cc0f9cb67b09d8d4c897a2dd7fdfd49523ab3b9d4f759c9803c", - "id": "fde379bb0f8057769553fbf9c7e49b62c37f33999696b0a426d6df0f1198d7e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 800807608813, - "fee": 0, - "recipientId": "ASNbbDUUV537CWVyJhW4nEBaoqphsfdGoF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205c5f5fbe87eb844f435035cee3b081a0594c30581165ab0c5ee5eede125c99b702202db7822920671f90a6bc5ef5056b6c91599f7f48b1441ce679e8aa6b0ca53e19", - "id": "0d34c831223debbc92140029644e84dab7664c4af7cb70acd1271b023df5777d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 807327846398, - "fee": 0, - "recipientId": "AaJkUNWfyrzaF8VeoujbCUB96bncRBxJwb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204b973689c3730f93177aae4292752724a4ae4f313a98f70b8602a63d2fcd7898022061fc16b87f2e06b4f2adb56ea4e15de9ee8858b079b7783c749575c6fc8e647c", - "id": "784dabe70b09163d177db73a00dcdc56ec59620d25ea31e271fe15a479c31f35", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 817711112949, - "fee": 0, - "recipientId": "AY25aryyTvvZUjDtbG53zbpigrenCBXbEd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e23b475b7f57825c23c18dd4adc239468a8d185f43f5fc35b815f8d9bc04d03d02205e27da5cec444f1fa6941ac1b28c6536b5c4e56c9908099aeedea3af4c8943bc", - "id": "50fd6a37dba73576473f5d47c3a08ae07e39b1c5451d50fdcf5e9d3c1469a521", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 817938318037, - "fee": 0, - "recipientId": "AGTFXFtkqYmcG6wZowdinGwb9K56rWbSjH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c8908d3537c932ca3d4de3b28d09da5561604463bd531024a992be58de1802ae02202b07302aeb41d0a3fe01a7a519a0b19e14d726918dac560e79d575e5c68412f9", - "id": "9352eb84bca23a8932cba246fca96482c36b3c55cebf9545ab3260968c223b04", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 828092442164, - "fee": 0, - "recipientId": "AHNQc1eUKgSJWr7BiopwP3fCLm72NRGFnu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022078d3cd96dfe78a40223df2d36967e46708c8ed21dd1e098e5994c9832b2bef1602200ac6d2c0ba0763740046aae684d542a3332ceaa98cd2d62d0217a75c57411e54", - "id": "190407bed322d0dc2626669ba6de2627c6ed18f86d158bdc4e3d24e4e49bbcdf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 833532097131, - "fee": 0, - "recipientId": "AHSytS1gD6vMjHtcpkQ86V3tTLVbVy4UV7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a35919ee25620b1cbf5f00d83d28329563805282f1de3c6a14c3f3718d5fb671022003b1c6755cc0d0f7451fc3c16e41cf2b5a771c4596063a2a3458544bf927dfd4", - "id": "c36f7006aa4ada2eda35951c1d8e88194598959783880832ebfe42357baa5661", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 838975285818, - "fee": 0, - "recipientId": "Accz8foPxAgb53QZq55r2J3MHyhFJhWjyN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d38fbd8b4e808a943dc3ed39967191c50ab52d5049bcb95f8c7896232ad9a25102204ce41e68a375a7a77d12ac92c4eb5a32a87dd2646d481b4dd6a4fd9b28e0a8f4", - "id": "677b8dd3b694d5ca4f4a2d890d99038f0d3529372ca65f21ad0669eb2a703d4b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 843238131545, - "fee": 0, - "recipientId": "AVWJBw2eQiWU9dhYYiPZk6YDKgT8zWuJpr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100908c7959593e47247fcc045d6c5d14798668266960532130f43c8f03ee9e4e3802205da61bf5c85eaafea399d9a0f6ba0002baa6f011fc6c10d0367197768d5fc7f1", - "id": "442e380e1881f46f13924593a158c58ab704e76686258a9e1b33b645c9f1913e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 861005612452, - "fee": 0, - "recipientId": "APLNgrLNdVJUcAg8n79DRo5d8krXTS9jBP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210082d6450fb65d0734f8c6d7d7ddab2438055b37ba420a3283b73b7dd236cc855602203663d2b76b2c2e681e78f016bf74a300587259281d73be10a8ae1841db720bbd", - "id": "53462cdbd50f84cc911007ef46be4622b31fecc18415d27f7937b5c36a9c1c38", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 867237104113, - "fee": 0, - "recipientId": "ANnnZMq2TBZhCoQkrXtsuhKYVvX7ii5RKG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220054a3b0e8eb109670e0fb452e1e58b8b82048aaa531c4238a011ade0f3c7662d0220711ad6eeeaf8888a8774cd572606e7ba0613d737d3255367a96542b95181d5b6", - "id": "e6c1a70720897231182fbac0ff131552e938891ca01be2fcd96afdf3ce2079b1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 868945860370, - "fee": 0, - "recipientId": "AdWni6sVGmYL1iWEeVAt5erYacKrNE6CHY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022042ef637e7595b772dc0f2c6edd1d1cdf05decd33f67e595c94abc1357baab2c702204e8bd0a77224dd1bf329ead41f7d03ab79b61f3bbb4a607d9b1ec06978fe59c3", - "id": "93ea865c088bdf14d07332d702448dc31dceeed8f265334e3df8b859f06a048b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 871419919728, - "fee": 0, - "recipientId": "ALBGoFokLTX1VeswH5LRBtk3uEUitoYy5U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022036757f788fe0baf560512809ee624ea8efa84ec50dc22f8c8d14bcf6daaae4c7022011760ffdfd5f5c283ae6561b017036027b62b70a1281bcfb7cf9fee718400800", - "id": "75dd4fc72a01bbd3b6faaf4adf4fdea8c88bb2a6708d15f124005079cb9cfc2f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 875407840383, - "fee": 0, - "recipientId": "AStpm2Vziqqgtm4Tc3xw8poVh9XUBGpode", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206fd3e896284765a4a81713111ed9bfac25e977e14b70b2db0febea67d8e14893022055d0b6b689b94312aa2f360f5930c43341499b6551f74cee2d2469527f5699e8", - "id": "85f1956ed442417404c08f274e8b70f65b139a35ce77b85bea78f9b913e4e4f6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 877995100899, - "fee": 0, - "recipientId": "AYGjRAJcBQokKnpxnEWvxsLYohpa3R22n9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220563f3bacdff499019a348d4c9feb8c965c3aa80385328d1ae7fe2d1abd7b133a022078402ff3128df4b1bd885b3abf5d79c3318d6255ef03f6aa2c4148b323569a7f", - "id": "92ab5e8f2f16a9e0b3e8a127101142184a3f2da044a47bc9ff72da0afdd5c0aa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 879811609594, - "fee": 0, - "recipientId": "AN3odZjMnK4NY6oHHG5hKPySNbH6DvfcyA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022076f3867eb0610cfb8c5ce5be956ac5b2c37561fdd58525db87c5c89e26af7c0002203635be4d862d33fdca979630ae2f08f43a8375f35632af2ba59b148045bf1f24", - "id": "ad3ab65ab8c782d704ebcb4f1ad69d54a225ca2e669aaaac22724b946b244508", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 881943327924, - "fee": 0, - "recipientId": "AdEnEMDqFd9FGjuTAdcLgVeSYB4Nroj5qR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022022e4869dc0dba943de4d58bbad7c4252e5a7e29a4cc415940ec3ec947b4f427d02204dd4c32404556aa475eb6eee346ca5b408e6541d7078cb03af4c8acf72ce87b2", - "id": "7deba86a243c8bcf2c222986ba4fed01192817061e1721c1d42e55b04678c85d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 882010152950, - "fee": 0, - "recipientId": "AUNsfAkq6Pcqeew41Qb7fMMTtsjKMfLuBU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203abb628561fafe13dcce54fb723168690a9a9e766bd4ec3a250a5b496abdfb33022017a450e3a2f31be397e2696b79535dc956c2faa59ed503ac81d57fd8c3078c23", - "id": "55dd0e380d2c6b884a719461ceb712f3c6ee0518841ba10f81f1f3c3cd044907", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 882090342981, - "fee": 0, - "recipientId": "AYxtKAvHGgKmjAyszFLeeUdrBrcx2T4HMj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202dbbb29509d4944ae73a2957f94bc821480f99552f77b7b2576b63703832fe9902207c65af1e634a4f3f2140146658865b577d8fd0ab88563d943ac6ab9f5c2082d8", - "id": "ae02e0d07ada325ac33ee462c57bbfe4b175943bad966e7930af9b51a6a83888", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 882090342981, - "fee": 0, - "recipientId": "AQ9dsnRjod2zb1jYmQouSeqMECCjczHTe4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204ca7df8131d2dbae7148bb7d600fd2ae9504838ae6e08a36ce5038898dface6302203016472420df3880b64e341b28ae44d9ba50a218a1be088692613fa69ab95245", - "id": "ccce88fed70924e2c19f5ce8ccde69f7c74377cbb3f698fb678dd6455cb74ae6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 895174683068, - "fee": 0, - "recipientId": "AX2ZAQGv5aZfuB7uEMrKVBJixqGjaD7QTk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204000a219545ece7dfcabaf8d2bec29a6ac6d360046298e81f1fa847dc260bec9022004193aacae42218212effa3649334b8e5e865ca8018f4cc10a704e0bf464aac0", - "id": "fe23b603d883fcb3e0f2dcbd203a338e6402e95abd9c2d32ff36352b707ce12c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 895341745634, - "fee": 0, - "recipientId": "AKgTzN7RNMLvv4S8QzRHqvtP5Kx4KFeufC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210086703eb4f783d915efde3bc0521c44c2c1fa7f09ef0fa0cce1b83c0567e55ea102203402af173407f63130b753f8e503acf6ab9272d4cf540ca2da5f76c5da2704a8", - "id": "e61c6ec80612d5006059f9551b9f34e928aeb6424694f773c56500bf2037e776", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 896791848697, - "fee": 0, - "recipientId": "ARyJArFNkcQUDDpQdLGL47BF4ZVXJ3y3fm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022053fde5e97462ffe6d69adc37b4923118269fd9d858ecbca3b7ec8f9fd88e1cde022047da7a3a640d20856f1f267e2f06178d1ffefee2177e8a15b2957f24f83cfa0b", - "id": "45eed794987ba92e4d579a7aa21bf890bda28e3028a3139be7a4bae6ef232ac3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 900000000000, - "fee": 0, - "recipientId": "AKJn3FRweuCdDcNkjSGH36vXb5M8ZBjbcT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009e08602bf0177bd5ff9b95c0f173333563073c193eec6574a25b8a4c7453a15b0220778701ddbc1a30c3b0366dd7d7ad654b0066e4db8f8a884a668425c95dc57028", - "id": "5d3beece4057da03840834c3035f4b580872492de87afacbe44d540ed75453be", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 903190511125, - "fee": 0, - "recipientId": "ANN62YesPBDVEsW12iTwfwRaxhgTQGC6B8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cd15bb91ab56975fb578938b0ba8ec406c684015cc4a84386d93a1fd5660ab18022017b0b3c53b8d834a2a830676ccfcf346d21610f55e96c25eec771585d6993c87", - "id": "f7b3f239d399a69d954994d12753be50a09d71241516f3bc49dcc6fcdefb7d44", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 912865546892, - "fee": 0, - "recipientId": "ARuGpfJJNMi3BFFbsgS5BeHU2c5ASxcE3x", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d232047da315e33fe38b625c3bebad7400b03cbbd0b046aa0c38e972d5843f20220261e30aa301f0f4956d1f95cf3e4ffc50f0a6d6dcb2f0175e6e589704eb88023", - "id": "e2c3a809125902671f2e0242d8cbafaed3fcbe90c659a782e9de638bca44ef44", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 913029661747, - "fee": 0, - "recipientId": "ASycZbKLnRNbYdYgek8YJQzvyNFwjcQGAt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b27e1b308e009e6cdb6ebbe5b5ad0b81140bfb107e13ee7b044cf93d3152fd8802207b01d59ad8c5f909c37114ac86e763ad66c5398601d99fb1338e5944cd8810fb", - "id": "0a2f7813b32d7506cd8f633d7938b81c89d8aea083e830c65790ebaf4c6e7795", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 916154978619, - "fee": 0, - "recipientId": "APepu8ruATApkWtKxJhiU5CPYcFMfH5tfu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dba1440e8f6a7a3d4796995d180099e8abef17fff1f9d1c5d8c69cb3132cd1c10220351bb3f699b4dd023ad2ab75172a2c2f4d0854fd554ae437d11b3bd573f8aef8", - "id": "92f849292f6fc6ccb43b4f0feb73d9a875024a8aa9b32e4b4023936a71e2ce95", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 920325036170, - "fee": 0, - "recipientId": "AaDbKSFY6xA2UJgeBSE8r6n8wX65Py1feQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ded1448f1d4b184b39b787c38e550308b2e458d19ffd23b403c8d8c836cbdce1022002d021095c60fbe111946539cbefa09e5202a7261d44df441cd1996c01e3e2d8", - "id": "879e49379ae26a5f8120f6a4535400fb01357eee8b4aae9322d51e149fb9a227", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 921784408416, - "fee": 0, - "recipientId": "AJGxTV9yTgqZWNiXifF9JM8U2vdPssLyEz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201d722c68e0a469d9bfc0a67bf0de5c63c4a969b2942c30cbab883fccff7e733e02202d9cf49d2fee94fb552e356cc09291a1a0cefd9fa5ca98af311a951433c6b3f0", - "id": "8e0a8082e102ee10405d32ba82988839770f5f8a4736b556ca64ea6b674d90a0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 922643224501, - "fee": 0, - "recipientId": "AbsrMsgKck1shqR8NwDm1ntDApWxGSZW3e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009cd6e7355be59b5624201572983708b730e673c00b779c20273d94d4c34e9e4802202685f9d0798d3e167b787234885618a79d7f9967adc21786c7b127edaab39687", - "id": "59d8c7f34718a9845f8c670513b377e70b9f988c86007aad583f6318680be534", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 928155414454, - "fee": 0, - "recipientId": "Aaj14mdqcmAtZH7LnQqdWT3qf9BFmyR7hG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d10065682321cd83ecf696a90d06022a557d0e6f64864be26aca6d3e7958348d02201eb4018574d93c83eb706107bf2108a5f48529782fc2b03a4c55482f49cc4ba7", - "id": "65e3b5466ee3e56a4646686a8bed5a279368eea489d49ef0e093d3c6848a8ddf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 932790490195, - "fee": 0, - "recipientId": "AdQDEejPjoMpLg8cMDPJV7tyG2zLQmuDjg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e668f456c6c608cae80c8c429f20cd6b38690f069c232a8d8ecb57def6986579022015f6758e04f379ff651fa51c06ae5b9f64b48203dead52a732f08d88fd5c7ac0", - "id": "f14cf4caeb5874d8bd03184939da80ee11ff146580438247ecb81af6dc116176", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 939660273449, - "fee": 0, - "recipientId": "AdGeDcDSCeYBfS2DA5Dd9VZ5SwvgmAgCz4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100de0fa78884cbade7879b174697493b0d54e6810a8a5b1e9675b04d5a98668fe30220520492f5cf1c91d22bfe2d63edec163d25a09ab5c74338f20f3dc1ccb3a80e0e", - "id": "e11a97c65477cb5de932daf09eb0f377e14cb233813ddb680ffc030661dcd44b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 940453697923, - "fee": 0, - "recipientId": "AbfshU5wgXcrhfWNDjrkrvN52y3EScpg5V", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206cc11c0bf89c528f9efc84688be77e19339a847328790b7cdd407bfbbebfcdf8022057b0752aab9c9592f2387078df66d2ac5e40e49928df72a53b769b942cc40c1b", - "id": "1442d74ed4130eb43a84c53baa8ae6db3fc088d2ac900423f8b69953aa61a84b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 944039590123, - "fee": 0, - "recipientId": "AKj53MkBzDfkDf1SLXyMf5DnkC7Ra9cqug", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201edebd2222f9e616ed293c9c55f35c7cdfd6f0b4e739a588d912dc9761fe4dba02205b7b844d19d075b195a5cd94799d40c3292ca05f0b3bfdb4736739b7869e9d73", - "id": "8f981304ad1db95e27e2fca8bee4e51a421d0c8a406921d2e8aea3318e45fe9f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 944903185065, - "fee": 0, - "recipientId": "AUTtCwMqXuWDskRBVkdvDjCdMPz21Qqn1B", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220455ff666f653f5e990e60a35c440270b625b844ac3154e4d1a959e490fa11a1502203980c454fd84512adb7659279c031f72b23438cb3d5055238b34966837337856", - "id": "1741b8faaca3c787f7abc73fb5a926f1669b4c7ad363b2ff8db9c04f084b3f07", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 954261371043, - "fee": 0, - "recipientId": "ASdMtEgrbnpJzNg8ZbcA9wec7QcvgXBio7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202a01b72970b65264a10f17e18db74bff086e4908e01ea391baea9f67528abb4a02200a63fe887e4aff1fcbe7235e72d58a6ca6cef97e3f0d7c89f7792afdba1cd41c", - "id": "cef4106da76091c4ea826c2f1cd235a182f20634765bf8f6183a3c767fe36e55", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 956921007076, - "fee": 0, - "recipientId": "AU3A2mNv21o4SkKrPjjumxAMSx9dNSJkbM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009fb21352f67025aa234098c53b941d99b9afd9acd26e9a66fa08209561bacba0022067f9ec9f3b46d62bb5c30915f80b0da84b9efa71e6b9f357efc7089827803c40", - "id": "6f0e865e6a5d3b5564ebcc4f2e2c0aa6618418fa561284db9780fef7bf553f5b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 964800000000, - "fee": 0, - "recipientId": "AXUg9JM1fE7XbUPCsDeC3UxXCMv4tx8bgv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206fc92484602f29f7b50a5ffc503c5d1a231dc0e54802d9aeb373ff327602325002204efccab14943708ca58cbf02824c6d1590dc1090bc0470bbfa7760f14fa629d8", - "id": "a059a31470b9a196a3894a864cd22eb0c92e159e776c7406f20cb92db7a54dc0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 967490707524, - "fee": 0, - "recipientId": "ASvJ5NScpJtLy2WQV6AyBtzh5FzKxP5N4H", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100873f68bcdf9688710966b018209079b9a4ebea3c7b49e281b220391537b5b6a7022033d3d9d2463907307693dcfdc80edf4b864cb92cab31bf89ccc3c260ba72b91b", - "id": "c996d17aeb1aebda108edbd8af91fae13aad80b959a5f6cd1b2f8152cf4e829b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 969700000000, - "fee": 0, - "recipientId": "AQinQgE4vAbgTXaUKCj1QabiQbpsAT8fzD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022041619c75759c53ae76620fabf4c133b4bc2b8cf890f3c574a4ac706436a6e84e02204386f909f08ca380e03cc52330fa553bae5bc73ff9cfc5dbef030be47a4b934a", - "id": "ae59ee72e6a0f70f2a9ec115af54c17664b2515e5af10f3c66ae0ec5687fe005", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 984780889208, - "fee": 0, - "recipientId": "ATunEsg5YC1zoyYSuRap3zJyWmpzQkz9tT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f8ceeea56b0a689d42103fafd6a58cd236a14183201ede721f666309aa66769302204c345a794bd8c7aa04e3e734bbdac62cb1606cb1375ac66d4a405c9bf515c5fc", - "id": "5711b79607001f91ae6452e19d06b65a23310ffa4ad08acc8a30b24ee67bc3ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 993823555585, - "fee": 0, - "recipientId": "AWUKR4R5oo99dRkwRkvTvTJ6ffYBNv7zPF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eec0a2a2274589c7b1daa03e60382327c2a429daf422815f969a16a2cbe3bcab02201c2cc1ea66118960117cc6c00d5cbabb878e48783e37086bb7fe0e3ad3acb30a", - "id": "a71624227e6daceb049d5ae4cb83a6a6e9a04659f29e53a1fe30f4746f3dc194", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 998232238139, - "fee": 0, - "recipientId": "Ada2uLW2F4xsEVqmYhvk2y1gnjbQbeEGny", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008bcccaa6dc17a49848a90005e91d25cc5a2f3c50af365fb9e84bbd18f8cf19530220582fbdc99507556d418996f4aec523965f88c88efc5248d779526deff09b1460", - "id": "bd15a7f964c5ccfeeff073d396ccb099886e5bd562af5b66717f93dbb20e9bf5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1000000000000, - "fee": 0, - "recipientId": "AV5UwZD1wxVNcDjjXi4K136dBPjT8BUSMp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022034080e7de43853b75340e97ad6a2823b8d08290c7050fb0b4e7be311d95067c0022035815502cfbadf9851555f9bf30b82544aacfa1c5eccac34377d86520b83d873", - "id": "494e41b53f142601472c50a49c966c2af768701a283d28dac2b09492b902f9f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1001660153985, - "fee": 0, - "recipientId": "AKmat4frPHmBjrfwuMCk4uBRTPp2zAznfu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203cd5409d500d5b5fbf817476f4ea709e7e09cdddcb93ebd2a1dd7cec3f46dd0f022051cb3f390cf2d0d9162d5409aded948a48fa466b25186498de1e35a0d360ea24", - "id": "6a948937670927c06e5c1358667bb2f7611dd7387a405bc429b25f43e4e56827", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1004564574086, - "fee": 0, - "recipientId": "AHnrwSwuuXKTxxL41xgqAwAjHBc7nsYCDU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d73919bc931145f7dc02745832964a7cf2091a3540eacc25569f6fad1c954fd002202ca78633ac126f1a38818b7712572f0b17887e187dddfcfceaa4c92521b4c80e", - "id": "18f0983e59f657e62c56a4b3257d3a14cbbc405b4066f20d687852162c449a43", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1017708933348, - "fee": 0, - "recipientId": "AJnv4qX1hnuecj66hRM9mgLXfYUdEqWr8p", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022012492f457272b7d233a1324f49e7b1e04fcd80d555d91a05b1392b6559b62dcd0220530e2dab22b207a45dfeed314678a22822eb0118715ef7a87fb7bcdaf97cbb93", - "id": "5caa6ca9681dd44dc6bc566caf1289fa243e6448d9d9c5d19200800341f52dea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1022309295002, - "fee": 0, - "recipientId": "AaZ88ShGS4izM2RAjdRb1v2XBDjeWNRiCS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022022def44bc8637552e51371d00a3a39b5cd77cbbbf0166ad4f3431933ac94181f022055884070dc5ca41b51cfe9c8f363b03209c76b97c6dbd02476fe94397c1e4bee", - "id": "c09ea42b2ef6849ce9724fcb6e560e83c211df8c31bba0eb5853c1dbdfc2c9ff", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1024417009424, - "fee": 0, - "recipientId": "AXdzLoCsCHkLrhLGW7SbqQF7ycQMdHj5CW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008b4844b7dd8265790126f46b21ac699ac941d6b9e657ac3efc52ed7c874535e102204c3ac47d179cd180370993b8e7d2e322b584e3848b2d7a0ecb86f8f74169a08f", - "id": "3ad1bc54683cb14a70774636d880c11c2aa3a236ee3af42c524dfec3dbc7bd20", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1026165099001, - "fee": 0, - "recipientId": "Ab5PAShY5CX52trT6YmEB8Xu25t5tM5HjJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f74a6839a13eb8a3df1b72407585ecddf0893ed4fa3ee32bc30a66f7c49c609a022051dcb39162f3aae1ba1269a4496eb62db24ed3c176e488419bf19250fef3680f", - "id": "fa7af6fc72bd13847f83a7e5b0a9c689315429725a6b6f87101f9f5210a67330", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1027972402698, - "fee": 0, - "recipientId": "AXk5qyz8GftcgVTEzUDhpfWZL52ZJMcN4G", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206a1ff97dae426570695a4585f2e9e5734c742bb8be2981a757890cf99a2d9fe402200f7e67d7241d9fbef45f4c0f51f7c86827649cb337ce8a902f242d6367b48ba7", - "id": "ace052f0c133b5875489f3a7397b48ade06a5ea9465c0860a290b3d34a5f927c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1028958385088, - "fee": 0, - "recipientId": "AK5NYn7AJdvYWy3DyESmvQb2SDf7PxRa5J", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009fb2a2c3952a7df061783e1f6acc7d7ebb1db208f242c9db13e82e0ae515eb8e022078dcba5d7c2487202d4a950b66cb3ba4e4cc8718bac248605405ada6131e617e", - "id": "e1230c88023ca1225588bd7a9dd7ddf53bfc6f93c0610c3dfdccf630f943ac09", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1029105400145, - "fee": 0, - "recipientId": "ARAkS27JzjnmAGTBPMQmKJgSwCgD6SNUDi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e2fc6163e74ec9cb17eeda9bf171e5b4789200831d5ab73aa00356cb911f884102204a9b214977eedc6106cb9b6c416ee8a14d000e854be082764d65109f88ec2206", - "id": "6c8816c6f0b88e43ad7b4b790bcb539d7be48a61a830b9f4d32ecec9c595552d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1029105400145, - "fee": 0, - "recipientId": "Ac78t9XdQCAVc3aQVwrvB8HTUqSoDqiHAq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bb701ab9fe8c4f9dc7b1cefa260c8934b2bc4c2aad31fd607ded1c768526aeb902203b1b477789a7e8a69c02fca7bb5a8d348785a975e097b4e6cea3f2515d68ed82", - "id": "5005846db55b1f7f32d13b95ec2ed595dc8927902d1650fe366356fb6cdaa5fa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1029518225860, - "fee": 0, - "recipientId": "AWPoYy3qsD94b5k2kbNuvUzZVh9SnQjBsa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200c1463b83dac90ff2ce43bf2743c30616939b4a9a1c3dbee8ce27ab1015ef3bf02203c474948d5240127b76ea1f19ecbfff55033dcb3257faf741c0d73c0c4fbdf83", - "id": "fa84fe86327ff3ff43663f888df8c167f5567aca4f61c02157a7c4403af4f29a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1030548774634, - "fee": 0, - "recipientId": "AdPywJjNoHDnCzYua5irL8ShvUx9KSHnc5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202199eca47200eb76742b28d1b0767359f588e9c2e6b19405a40ee32816cae9a402204d9b9980c2cf193f6cc38e6ca8796b0e192b055bec5b0c52eeb31b95da466bdd", - "id": "c65d4ec488a9d907cdbadcb865f4732ee6a0ea2edb8baeafa1e7e76c37237c2a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1031249725970, - "fee": 0, - "recipientId": "ANLRw9k91KW3snAC3NCPhhmzgg9cMgdg6j", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022052dd06e5866b27fe253f1b64dae8d3e1924bdd4666062d7e9384bf77cad2b86102201cea424f053cd156ac65ffbeb703d245b14bb2ba0575aa90d9613a7e8fe4a7c5", - "id": "5c7bcfce2963fff9eb502e79dcef563274a4c409a93334c3937c3019cbf55185", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1039396454147, - "fee": 0, - "recipientId": "AaAUZ1yWFqde5BYXbbUEWFB9FGGpQCcScK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008bf00e9e9954748def806ae413bc38629058e7c2daf5f56c34991b81a61fb7e0022015c587a5adb9e7cc11e4da7f2b6e5f1bc1e8f4f36c99a3fc15fef28390dd2c46", - "id": "7dbbdba5101c67507ff51e5375318d0aa8a9e3f25de019dd16af2fb62077d6d7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1040719589660, - "fee": 0, - "recipientId": "ALHQ1oorGCEvSPsFNWdYPwRyk3rVvMj2B6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204a48b4446d976bce24fe26d4e666947e59567c29d55254c4a4d986ed2e0af1cb02203147efd709c8bb850861b19c71997a5780e606889ccabddb70ea4f5beebcffeb", - "id": "b06a6b4aa2f8d71c39b5ab05f1535e521cea4d1a6cd304d56fcb02518e5d37ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1041975741930, - "fee": 0, - "recipientId": "AZVgPvHxJNtfVaG2YkJn7zPdFdUtk3jL53", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206c33c5318130b31313a7431a249900519d511ee8f2a43986ee37ca6d534aeca802204b833d1bd726a50e0a76edd62600031e215239c7e823c556e53aa225cf5d0691", - "id": "f9bf139b97f3dee7e627fab2bd00ad2ee7b2ca61ce259c8a9da4c0e33f8e6680", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1049727197915, - "fee": 0, - "recipientId": "AQrP3AzVXbRbps1UzCfjDFoCCnc1f19a5o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220333ec0cb3e0cdf420ae2a89997b32607a748333fde1bd62becec1e8231b5a98d02201a424700aba7467ea4141d1d847156b83da94c26acb464cef725f4f8ab36f651", - "id": "69baefe519ef9cf9c530931b69908efef4ba290f89a014f2fe7c93f7d4f4d3da", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1078361899572, - "fee": 0, - "recipientId": "AXhwWcy8HhMYLbiWhUKnZPw28o6HfGJjaN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008ebdc6ea8c2c50965088daffa9b2ec09f1a4548729284ba4aa2e9adf38cb82510220524ce025795ef5cfb044667870375c205818ba4da875e0c3cc4ad6283673d884", - "id": "fbcced1418f85f3aeb54a66bceb7bfee00708ba6eeaa3d5b7aa85cc841a14536", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1102465913669, - "fee": 0, - "recipientId": "ANMuAyS1SD2CfJvuZSi6RQb261gvFSJt1X", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205e2895cfc0f9708d2af9c6008451ffa63461201ccebd7037c81196198f3c25a00220151c81744a95f78670811a8b66995a0f2229be460d5dfd712a7e744f5d56b706", - "id": "9c19d49856030d283b8772771d575a16c5356413501e2acd042835873fc02cbf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1102759943784, - "fee": 0, - "recipientId": "AVVhvtdbiqHm6zuFnUYbCQCsgfKs7GxJxS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a5a5702c73261b34e2ebb67471524f509588fa51756b267c861b3cc8737c9b6e02204322f7c837883abeaa450b02e6c51e81c90c5a0d41196f046dbdbe0827e1de63", - "id": "734294bf0236a11b079ee873079c7c4c9f1b85a53750c0cd97fedcb5cdad0074", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1108427216593, - "fee": 0, - "recipientId": "AYnxyQgq1j5i7h7AEd1UcYVWeZhip2BPsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e3c2842f21cc80db48cc3a7fda70e84cf3fed43e68ec7486d2b6316bf94e85f2022040fb1c566bb45142bccea742c68cab0d9093a823c43d623ac6038062393cf171", - "id": "81ba05c7bfc196047ec8955ddb0ee07ab51176ed9884beac7e59d49e117e7bae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1109838235948, - "fee": 0, - "recipientId": "AJvzuyNaWbYyC8UqDu7hPVBLtB4yEm8ovV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d0159ed2472012ee33ddf9230004b427c7f696bf22eccb67f3ef6320e657a33022000b1dab519c3bf58067af22ddb3fbd1df9144324ed404f0a3f67d7fb2f64f7af", - "id": "08c1f4dac6b650a1d2e5ca059fabd37ba23a2a8718ae034fa1759e32f0ecfcdf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1110036076121, - "fee": 0, - "recipientId": "AGvTatnwQJR22A5dDEWXvWpfLCCFNu8tof", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205979870c79c60800f61d96ff866cc18b494945f2c805678b9d55650548562e6e02205a070cc7341571569be79255018a01cbe86a1330c1e2b02ad4aec2ab498910aa", - "id": "9541a811866acc0b84756a38bbf327a40cc96ae00eef7e20a540be4adce3ca69", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1110492080313, - "fee": 0, - "recipientId": "AGfChYyMy3ezANVDo7JBECGJxEYGeefPwV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220298aa44e674cfaed265a12495d1ecd96299c34f8ee8beacb597ade8a5b22429c02203b640535bff8f890be8292c5ace7085658b1e71ed361a467945f1d6b97f5a223", - "id": "b1887110f49e04d53f26b0182321f2cc049877bc774784dd69a856ebae16d260", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1113211377847, - "fee": 0, - "recipientId": "AWhG1JweDmRpkT4jbojz4d8QshRZnf1AZr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022060139759d9b0b550c2b108cf60902acd2da2cfbb0747e2782124c478fab51d1202202d51d8bd860b50a5822448e21f35eb97c39deab8cceabff01e30d7865a486fb3", - "id": "dd436734a516d0647539b60be1932d984dec023de8ec0b3d963bb48e2544da62", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1117612928727, - "fee": 0, - "recipientId": "AaTpLPFKjkyMqe1HAzsQvnvVDAPj7pyv6K", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206a305e6563c1eaca84abbe8141f6d16b053e58796933355afd0c004e4b4615f9022022de5f7a2dcb182a421cacb118d5606b3180e54397e535ead5fafb3808640ec5", - "id": "d946b6d8a8a3a305eaed80a1c5c856dba6b732d0a6f2976d2e1830168d242783", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1127012726593, - "fee": 0, - "recipientId": "AdwPm6G4tnxBBgtuB2UCX6kUt8KYv4vm4L", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201527ac126261312ad02383d5481500ac4bd030a800d2d1e8b0eb2e26d5986df90220666b7179f7e9eb56b0a91b43165247d2743083d37f61c773780314fc18d811fd", - "id": "228725e2039844ee5584f099930f5ab6c1c0185f78e69fd5d418c74abe97e512", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1133071775570, - "fee": 0, - "recipientId": "AQsoWhRqcDBKREvizPBDehMYfUMwpfoyts", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f4c04ab56643d9175bd4c8179c2c3efc5743bb2b1149c38a182583c72b2ba1a02206fc46634fdd5548d2ae01950b9e9696ce6b880dcfc59abedd4c62c3757d0783b", - "id": "71e16485b6b1168dbedc07642c4dcd4295c7d6073b6b12d8c86afddbc392675a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1135911839174, - "fee": 0, - "recipientId": "AUw9RAfTKkMqFky8G7Q3s97cA1NMwsxsK1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d29979af23b1a9156c2a0223a3e5a76ecd62b666452dbc0931590730b0ef15bc02200eff2f5a2cb906c959ee855bf3261db9240d7ddefb5cc52bc1d70d295856ef99", - "id": "dfd87714aef9d4f11ee8a5383fd9cb8a467e3523a3206eaf99ec66d943bb41d6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1135911839174, - "fee": 0, - "recipientId": "AHkybDKEp9psTVmKPvHzqj47z56BY5UcEc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205d422f18bd80adc6a12a4c8bf2f933fa5a49328137e696e1738f1e5ad0f743e602202522cbfbe59ca0e6d3080cf64011d7eada1505b5984553bcf353b871c2c6513d", - "id": "7500e3531f8b71b5f4b7aa57e9cfd47de4424a8b7eb5dca89e2f2b9e6f5dc34b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1136025441718, - "fee": 0, - "recipientId": "AKwY5JoLghksohAZG7oEG2JmgZuubyPGVp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bf6dc08b189c178a9201df3e5c32c3e42b534a09c7f676980d47d6d53335848c0220531edd5c8315583016cefb60ed96217a578c14dfcdc4d6ed703c052cc2c2adf6", - "id": "09bb287a691f3121e58b6f0d862b98e2fc1e6a703594897dada3f2775f8ad9fd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1136025441718, - "fee": 0, - "recipientId": "AdbutZprjH2HAM17xxtqixENbzXPW9Wys5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100df1312b79544c50df8309c5c47227587ffd9a46eacee45787cc56e37be4c0c8502206e7679edcba8ed6fe9bb634fdfc96115005803a8c12cc6292445585761069d4c", - "id": "d77a533c624889a76d7135695ab9c70217f78ffea9319af6800249ab846800f1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1136025441718, - "fee": 0, - "recipientId": "AJknkVe3o8zMyMVvhLoeoHsgMowWV2gZj6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220090cff705808e2057c9375917aa0e29056143e9e30a4a631cf32be7e9d8f7281022001259193a883851281a9978aa198ede0dc910db7215f1da0aa40c6eb749fdbc4", - "id": "f0813945e05653ff3b2f3e0f7be0a392e5183f5805efe5fba11d23386cfa310a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1142768005049, - "fee": 0, - "recipientId": "AKjKD5TuG62qWVFPpeca7JGeg5ZSs87F2t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008c34f0cc07ecff34844dc8b3c59859eb986e4713d2177923de88beded8a00e4302202dda4d34149b8269f5d62b28e18b3c0eff841acb41c7d19a657f673d8f2ae735", - "id": "f456730f13cabf7d2b355c690cef499f35613b3d9943769770a371d68ceda967", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1143514373646, - "fee": 0, - "recipientId": "AVzbLnDifGM3MgLLXGJokYRRaJtCTSDtJf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e5ac125df4c653e04fcfa0ac241245873a2ab254eeaecf65b2e55a9974627da5022026cb84adf8e3f7b3b3acfe181df6cc1e4a1d64f06e9aeed4ee3dd2fc809da404", - "id": "7084d48327cb27c550dea563c70084c1b113aac2cfc8eaee8da2d2dfd231e7c4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1166230353463, - "fee": 0, - "recipientId": "AdFkX9y3WiLxaeEsdHgTBr3fTUHSzEynGF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc010d5be3aa4448bf37dd53b39cde7efd9112922ff340bd4271e6989ef0a6eb02200a155c4869414d9feb7359b3b7abe7a44dfeb329f70dba2f06679ed5f61943bc", - "id": "331e701070f52d72d30610165dfc9aa6c0d7077f7fefbf4daac202ed64563323", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1167544630860, - "fee": 0, - "recipientId": "ASDGBYEPJkRm16xDXW2uBtzegkmtDX7Fs3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202e7deb619dddea19875e03f43270d03e73f37e48efaff365eda36e2962f676e60220697fe1276d816b0e5b2d6a55f06a0aedc12f76d6f228b07da41213d79d85f2a2", - "id": "4a2f713400914d0d3a66f565c2d1c9cc39a3f19c12543e2ec418110335a0b596", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1172864247614, - "fee": 0, - "recipientId": "AWbSK2Pwu5USnhcQxaraoybLz7XSRRM4JX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100812581fa24ca31fb48b100b3bd5415e16727dd8be600f0f604c03408010055330220032db4b8659cadee9a1b949add6b4ac8fafaf0fca6774898848f3c417c36a180", - "id": "7dd6f8130f9d3f76512de04ba1c3473ca264e77b0613a2c8a86f3cd791fa659c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1176120457308, - "fee": 0, - "recipientId": "AaRaJMpnL5vKt7ADUsEAbVw3nqnLY6CvMg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220379c7a758df66f5b8e7879119a902aa415fad87c14d36d7288b8565c90310773022074efd40722d365288b37f2e883b4a9c3aaa8dd211762b496649c63e477bf9e48", - "id": "df44e3d6aea679f21c528cb1e0831f1c2c1a92abf61ba046e05bfe4542b5cad2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1182001059596, - "fee": 0, - "recipientId": "ANZWuhTZmksp11sRpxy1JdT4mymjkdz2ys", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022004f1a606b87c3931b43aa9ad6c39c1b7a4425d7b9463d3fb1165ef64ef3515250220614138bfa85aa722ed98ce55a3a8445c39fdfea6add75b0d780e47f93a45d933", - "id": "fa762596e4127da262c6663483dacf38836d6efa88c818c9bef23d3c8c265cec", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1185739919799, - "fee": 0, - "recipientId": "AV7zqi8PG6BfB3Nud74eC2N3BQA3zi9zCa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220246839d23affac82057511a02b479902a17d163a2df94b950e8cb7757436cbe402201fd4e87363021f4a3d341f8635d61d38010aba3ea51df5e1a10c9af1613db6c3", - "id": "d3aeecd07fcde727f3e8e8ba0852c7ff5b28adf9da80024cfea7c51f8d2c961d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1187780964457, - "fee": 0, - "recipientId": "ARyuySFRx29xHYMdL7TnKZv3xvBgrWRdep", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201235cf2bb5c1cf1c254fbe24d2337d87d100da46a7f39c211770ebcc937765b802205bbad34509bb0b7f86f8a2807e08be016273cec72bef1ab9bf5457939767832f", - "id": "b7eadc91dfd13958e6a36963095361b289c1c37c68f0aa3fca1ac4f648440ba5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1218554570073, - "fee": 0, - "recipientId": "AGqGgDyDQdj4stacZznPKNDRbdeQ7z4nfz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e43b353214830892288bbb6ac6da9459ccc205692fc81860f5551b4a6fe8f325022040f1567d23489b76539c272293702890658f1acb22ddaa3e43ec2c50ed293d14", - "id": "971db6562dad82beef13c4ec839c0d60866e141b03ec2d4a9d63c67e2fc46992", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1226105576744, - "fee": 0, - "recipientId": "AbumjiTo1eDMprMELVf5RnCwY4jFYxu5js", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205873b0046921b459e29f8227a7d59181a7abee5d75860c5f941c79a9618351dc0220795169efc3aa3651755c0563b623945bb5aa8908196cc2aa0d1fca2e87f4c6fc", - "id": "f22ec22f00452a8c00256018006eb2fd665b84932dff56090d95faffeb895a7f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1241085387065, - "fee": 0, - "recipientId": "AGNRsF3qaggSf4N9i9gpmMuGkHBKnLCYqS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6924dec0957658a213b7e04eb24a996018344513ea1a35163b4977bb1db709402205b8d978e83d7c5051c208c33617bf8ab611d63ab376fda7ab8a3ab6917ea0ac2", - "id": "9137dc5ad77a4c7b7946f24e7e150c2622c15b6d1765f3efa552a386dbec4552", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1248321295924, - "fee": 0, - "recipientId": "Ac9WFSBF9MPEjCD1f3G6vWCHtZNT53C538", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c9ff6cd56b1723f0bc7d5e7623bb66ca39e4561b79ed3f6b2e3fc541bd08787e02203b69a4d8b2837be4fc097bdbf174cc41c1b968626d125f497b106c30c6b8f5b7", - "id": "24d7adecccd21860742650572bf854e50a79d8edd7f23af74b7a41311b3d9917", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1251098136462, - "fee": 0, - "recipientId": "AZ8GhCcmNL9HGhLejRShEkbPL4fsDcB6Cw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210087e8c65ba87f0af0fe5966e9d134ba62000972b7a4c5caba161f634141b2e07002205ec3411d77ffff2f5652d2074f622078dee8b8149b0f14b8dc779164ff03a3df", - "id": "912bb48bb857b0da7007fa875d86644c803c3acc59af672692cb0abe4fd684b3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1254056287561, - "fee": 0, - "recipientId": "ATRX4J1yKRWZwSsNyW2rEHwbgNxaj3SaDG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009eaceaad92bedbcf6a3137ecef0dd7272072662bf6158c82ee8242dfd6a9265d022032b614fd8ddb333c1b7135197649a09e7cfd3ebee617f4b08753fe5011121330", - "id": "c9eb7cb21ffed293a6dc750757d50b459e5bb98dde6080ba44ea984bb5a3bb81", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1262124265749, - "fee": 0, - "recipientId": "APZXrDufYPZd4M6KK6mU3N9i1o5kypmeFB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202dd40b2e9929255d731e525e3f2bc1d49dec2151eb3d65a58424d346b6e1ebb7022050c87541ba7b5da3e891df8b9148b9cc7f84d5e3c185172367b76afb8544c34f", - "id": "9c0e58750a4580a5efa8c5dab95ec4948feca82e6750647945584406b2b777c7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1267980213012, - "fee": 0, - "recipientId": "AW5XkGBc44rDNynQgVwVSCSyUHd8dt2ap9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202fd98a801d3baf19fa8a77e2e0e5b8d684547744e26856719c37f37ed3e5af3202202b3f071b8daf7f5e57ea7ff8c4b54e46dac73204a72f2ceba6e5de7121f0de82", - "id": "b0ac53b52314830e2fecf20cac13d0e3c1c33795c55cc15fe575db1caf5e59a0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1275943681122, - "fee": 0, - "recipientId": "AekYhbhVD1qNL6NmWL1tUZTyopAjEjq1FD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc37876c3e04ed282fdab28d5412486eda4952dd02d7cb625cc642409fa0262d0220263503fb14d31513ba463c6fa2bab58838daca4a19dda7604c7708002d576073", - "id": "17bf45d5a938ff3f0e910d5a75c731196d39553d66152b74cd4f8687241e3c49", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1281318617272, - "fee": 0, - "recipientId": "AY6ZLmZrpunUg3XuQZZBSXQCqhUqwdV7Pg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022033eb26e2f3bf589d76033dbb4ab8a726bf495a26068e54782aa26f86edc83cb70220639b4a7648c43a4aae0e4d3cca922b7274aabdaf8096cc1f99aecbb93379cf70", - "id": "027298b8d2a4425c3b04df6e1200a87e504f79fa7ddf9f8e1453be427d51b5dc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1283452028431, - "fee": 0, - "recipientId": "AZUXnt5f7UDcqCTn8GLMqrqxFumWzc6xdV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022061a17e5dd43b0fe5b0bd9f3a10dc22a004fa2ed372e4668ab3fe162444a4a354022025f7353468432e788a5859f589e4cd840f107b908d496bce9e08a0ae8a48f81c", - "id": "fb90e727a1fb40e9f09caabc1bdf855f85ada79315a103e6201f4b05b9bc1554", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1290283529798, - "fee": 0, - "recipientId": "ASxqjAvYhLchFzgx2jwju4GsUCSQEH3AEP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ee3204265b27ca699266a811730b567a7cc018f07e789156820f4b55bb5dc5a6022037f1b07874363ebac4373e04aa81cdacc8e462d0c8615dec4945254fd78bfdb3", - "id": "ab1293aa990a9bcf79d2c390946ee548564abe1961227cf01b6ea0e4f99caae8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1305366622447, - "fee": 0, - "recipientId": "AKoaU94wDMKcvDdon237iQ4ygpqVEP13Y3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bf1fcb56c3a81f457103bc035df6f52aa8f37b342db0d24b37d31ab09b345d85022033ea972e903998fd0b87d127f49638d31612c25ddbf176a8f28408da823c5382", - "id": "e33edc4c6ae458da431e7b3fa0c62bb9f04b900c071887835f9e7168cfe57076", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1307153045859, - "fee": 0, - "recipientId": "AYbyHKD2741E6HT7dM2YFnqM9mMPS2yYTd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eb0c6b3da5ea62ad2fc5d1d80b88627f9f88de9787c2369ad41d035fd4e984db02207b78c129b934570c69db9b52b5c47e7e922c64d9c0f6d05beb8b93507e232e3b", - "id": "b791858333e83013d9a2bc47ee3f32000e2a4b5425093c89e3615af4033bb90f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1332727985890, - "fee": 0, - "recipientId": "AMwPB8oS9vsN2mfaDVXSBZpGPx4cos5Mjt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ed8b29399b29160b2317c2eb075647fdff0f477844348d0c2419f6a99eabc5102205753d822e07f472849206cef2b0bd4e55f7584e793eaf40db6b1a1eb478a65b0", - "id": "393c15a74a9b38eafdb1b8bc58171ba17bfc61674792a0443abd4a4706674b0a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1353732126985, - "fee": 0, - "recipientId": "AeWDk3U68wyXzwYKrG1eFngLb1PmjEJ4NU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f99a88f1106de48bf260252af1d7c94c0b128bf3a1293b39fa6aa3b39e855671022001c6a107ff261eaf289562ddc495ae5431f2c03eaae98e02d31d186b3891044e", - "id": "677d0d56c62618704636b51d569670848fdb4e838c1d127a643ade064730d7ae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1363961613487, - "fee": 0, - "recipientId": "AYKGjy1QdNyw1QtuYMKEQiLeeim1PsMGse", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022072093aca8550c3ed5c93ee467e06dda194f02c5434da8b51e8ce1f425f4d6a0e02203b3969a7a176fde24aba664534284790c56aa157e3ab8a6144b2f82e14c328b1", - "id": "7f8fdbf6de9d41fca6389436ffbf660e670356a289f9b823a335e0c6f33a5fb2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1368486431867, - "fee": 0, - "recipientId": "ALqNN4gFAQ4i6keQYWt19VGVdf6srRw3fu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203fa40db925f8076b84cb54c0442f866c882e25b67274df69d2e668d1713fb702022029b2294f08730eecb92ac2080c7f116ffd3b587ce00ff6907d0ac52b544188c3", - "id": "0ac5811bb68f5a2b9e1f9516414ae78b9b19d68c9d45abd95a83b45ee5b8245e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1373148336615, - "fee": 0, - "recipientId": "AWBBsjmyW9q9Eu6Esu738Bmhm2ZGZiwqmZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200d6fb95c47336c8107cdda4217db3bbb5c7e6068e82d0e78c158f422d44e473b02202b2c57abf8045984d6a695a1e7396507b47c0e735a38bac8b4731217a618b922", - "id": "0d8c957621281c5dc06c45fd28fd69d0ae5bebd809f991f077d061e692470b25", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1414800000000, - "fee": 0, - "recipientId": "AYsi3kyJfxa5LvhTms3yLbVjLDcwZEuQFU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201f2578fe3f6799f09c4e21ceb01444643676736794accb55e66065b5e2c7823d022057adec3762d37b685973d42c40d03b2c8ec414d3b4d2bb04499816845a358312", - "id": "0cf6ba6998cc228d806efa6663b4e1024aa417a15e46b5f64225dd86b2e1a2ae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1445037440539, - "fee": 0, - "recipientId": "APNYtJ4DsTAPr7UzmPoQFsdj4GaBT4cDNw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b55aacdbdafbf73aaa8ffdd77638cd52c2e572f68c82cce5a21182d8edef856402203605dc48ec3545efcc2ae67e9dfbc440a3e3fb89133dc2eac0969c65e9d7ea89", - "id": "a0f5649db4efa028d6eebdee0636a191c1981a80829cf6c2948c0cd3f5c14ff5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1445600000000, - "fee": 0, - "recipientId": "AZ82o4tRxNgRkjzV5GyBDkuaAgnLnUDMTx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220713958c68f4b3f560c9592fcbda9dc2a6be93678f7e08ade6e96370c4eaa26d402202c862c934e996d1dd75dd49ed7204128b81b54b78b431076c997cf97e7747cfa", - "id": "2fac0236a0c0a9a6bcaf6401293611d1452f338213641627f8db909d47bfd8e8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1454528664622, - "fee": 0, - "recipientId": "AcCh8h1ZmQN5BeJ74rFJKE6hG9KZqbesYT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ca4227b7da265016be665600bc3e7bb20f75d7a65d72c13da8c8d9ab4ce383e2022038ff78d1c5e601365c28d9e6d2e4337a7d9d7d0dbde45c4a4023638fa07e87cd", - "id": "8ae411cae067cb9b9f15fac37ff5e9ecf9642a135de60053c3ecddc38a498f77", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1466807483534, - "fee": 0, - "recipientId": "AFqgu6rcFy6QTXGfN1FxC6sUHY2xCW7uc1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b2f227538d4b7706be16ade8e17139e739a09afe03282f64cc5c545dd1e2dafa02205597c6a4d88ced4a5f1a2773b27b256a9138310ec40c62bde619a99f6b4914f6", - "id": "2f4c8dab500b1105cc9545363586a417fd7946615170bfad8928f68b145e067c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1467651315664, - "fee": 0, - "recipientId": "AHHrxPEi2Kj5VP6W5ZKegVh49PCV5n1jYG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e97ad561c742835e8d25dfe5e8417892e5b73a5fd29200bd36760092bca344c702202441a37b935457517e87484b64edd8b4d8343eee4e6acfb4a231faf29a079c9e", - "id": "806e0255fad866d11ce328cadcbe4002bd5a496bb152984bb09344e890d566e7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1468250973203, - "fee": 0, - "recipientId": "AHBAMChcuEmR4GEqhyx8Kwf2uSadixGJ63", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b2484f242f0e10ff6a3c0a6ce4b32c308153788c8d844ea7b8b6f53eb2bc6013022009b277ce199b03c13cee24d7e1f0d0a519f097e783053ed8fb1e85ff174b5533", - "id": "e024d4271d53d17eee174f952a3577ce724d9f9896036a3c2cd3dfe7680b6853", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1468680421064, - "fee": 0, - "recipientId": "ANAjpvQ3YXDaQBdh3uq9UrCzpgdKbyyPec", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220329b1550fb0f30e6e506aa2a818bc28db4f5f83510cffb3e92c5237ed612e3ba02202b595447789bc432db35f5795a9f37a41dcb7c610dd1148fa46748291a08ef74", - "id": "04ca01893a22c45fc969173c7ae1d0b8f319b6ce02a52cd8439fd40bd7092452", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1468680421064, - "fee": 0, - "recipientId": "AZdWJgmHDvj77EmtJeV1WvpBP2iotiGAm1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022022abe78cf54b66aa2968b866a5f2c89b5ba498a10716b87a4f5b1b22294268b002203cc887e61beae4a9b5d1472a25fc5e9e19fa21195710159c19b433b9004fb33b", - "id": "e1a59241b4186c667e2092a767cad17bcd1e348c0e7507e57783ecaddd8b59a8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1469630945593, - "fee": 0, - "recipientId": "AJduNtdjCT13Xmn1HbRG7DA4H4g4Q6BvaV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210090c7c64fdfd574b1a2e65c070b231e9c99a4fd26d4ab2046d985076c42fc21c90220481c1d8d1458f1dcf7adf39a52fab40a3f3d25830607ce853e20767623fa691c", - "id": "8bfc8aa67c36db1b6c8db6f65fd89687b83bbf8b45223be0984c084d2d507621", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470003556578, - "fee": 0, - "recipientId": "Ad4QYPyJaqhXRoMKbezNvCREbK5XPa3zhy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205188ab1d4c98d2f7c02b3825701e03b310655fbc44db5931a89432b666ffcf3f02200e1eb5e6220abf42c61477be919ab32c21626dc74dcd14a48c5d6078ac155cec", - "id": "4029404e82dedf7fa4e8df1e90238546b5fd8009d9cf6457ad8adc7504a1ac00", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470150571634, - "fee": 0, - "recipientId": "AYF11aoSpagfi9wzfzY5PWdsYw8erLzi5N", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204973fbd8f15de9230d6a4cb6f9f3bdda3dc7e1b66abb0d5eca95ad2ab43b4d750220132d7876e371bffb26b076bcc1b68ac7b8719917b25d9f09c597ec58e37bfda4", - "id": "3112c2354cb4f918d6bab6f765a16084540663423d129abd46a7f98292f6e781", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470150571635, - "fee": 0, - "recipientId": "Abw8geGxqiRSNtAZubYj6CM7NPeWK7VU8t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220532893083e7e86ced90c28507bc86376998b7fe227c26d44835905dc2c4c3fe20220784051c6f98e86370580c94789326635a2737a3f1d9cbe553c9af5636e4c7a15", - "id": "508f69085efd61c4ac263c576c0da0e037ca716874a848a3ef14d7cd8fde3487", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470150571635, - "fee": 0, - "recipientId": "AHbf1KCCkWZZYww8dBFjAbSK4oi44ATaNj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207409532c4f13e05ccd6236cc3230bd8c4d8b9fb3335023566af2ec6a22ad545302202dbdccf7e42d4aade9dff05a5a16c5c96e9b8050dd5b2437f3471097f8ad4211", - "id": "dc3acba04a593a641812a6390fa5b0e63837447f08914576425715f8887c02a3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470150571635, - "fee": 0, - "recipientId": "AP3uaTgqBZiPz6TFYNJ8zNNcZiNBidyLEa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d44633e422ddb30db8bff2241ffec1290600ac1307393452dc7754e7c0928177022024cf31e3fa2a243218375cc5e66c8f0dfa1df07ec5480196884309b54a4068e8", - "id": "9273a1dda6e37f1e98206d1c5c89f5c36d869a7a4e59d1134875cfb923b9fff1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470150571635, - "fee": 0, - "recipientId": "AGuHXDf1v7Hwfobh3P337B2579DBPpjSVs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220278be19de5f1b004e82a31fbb872d3bc14f516c26b56dfd10fc69fa5c724252e02203dddd4e05320b906a2a3945e52b412e12760c0be9e64c9286afa2ed1b907a43c", - "id": "6dfe84e6247aee21e6df75a25030ac199eb74a7079b01df62095fe507c0f3b53", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1470738631864, - "fee": 0, - "recipientId": "AHUF2TeEq2KfWsdKJmKkyb2r7rn1ssxVTD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c5944532bc50d544fbe393d8067585bac4c00239d96d2cec920b16b5e58be22702207e0594792578262ddd6c2ed4f443078a59b17412b835168837f58ea03268e8f2", - "id": "4e4ec69496a82528dd81743b7121fa8f8337d95a8c960e035ca863c9e9e945ed", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1471620722207, - "fee": 0, - "recipientId": "AdDYWwFY9CHymn4k8dbnShiEJZyF1gCTbe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202febe57e819afd13b956afbf988130d00fc6894a72128595b9d3c5c10b7171ca0220127aeee9f9e67b4ced063e09300edb638be7001c3d823bf4873f2fc07c1a8052", - "id": "c3efc99b197e710b0db3510f26d78676fccff157f80a0043ff70f862b99bb48d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1476719471690, - "fee": 0, - "recipientId": "ARPtgH415JQHtdLmpkyfXm5QvUB9NWWZRY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f9f6c9667ac8300ab05a51955046d8653cdbf93d28678be87e4892502aff5bf8022045b45cc9ba1a05612867c00cb08e881ceae4dc43e0369cad40789edc8dd3ea66", - "id": "5accfa3b6f5bde03070a900439418bfcf6e333eeaa78a55dd38dda5281f72359", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1501867950683, - "fee": 0, - "recipientId": "AaZMdjLc3oc2VU79SWzjaM8N6QQxD9JXsp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a4efa1b07b43cdb06620c1ef7cc124505d440620ac4533bfd20d609d038b0018022050a04b85c435ad086578e698d11bcf35256cde10769c69945e144ea5b369a5b8", - "id": "99e166bd676b85006a59c88b96bafd199145e0464e120a72630a18886fe93347", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1521031173923, - "fee": 0, - "recipientId": "AHGrL6VPzSfw2kGfTZWFLN9hJxh11NLvRH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022009ce1f620f2978fde9d02d346f69178a5339d0d34d229c9303a6bf3b0d390c7d022052bd984b7abf23e6e1f71f6c532a94eb92fa99a1b1a6683249ca16c93610e06e", - "id": "cd80da60be2804c472b529b4e41b3117d2c54b37047fe32363431735f21f4771", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1538544082336, - "fee": 0, - "recipientId": "Adse4g6EQzevtFtxCaymFKNTXfiqT8q3i3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e4d2f7d9ee5804329bcb4a16f1a925878dcc4f35d2462bbbc0e5dd63d6c5d86022039db901cb2fd1af4827f7c902cbf9d3758d56abaa3c2673f9a36eef29237bc4a", - "id": "16d0f95017cd07ec7ee5fb6b9a34cc55b4f3a43b7e551a8a2117fb22117cbe13", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1543246790015, - "fee": 0, - "recipientId": "ALreqLXn9X6tcgYfw7LD1WYYk7Pt77kdUd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e5d85a41b1562fb3238d3e5fee2f4374b794aec61600ecf1eeaf745ec90b699402202b87a4c0574ee646af802ec7f4ccacfa7d6163c70f169b03384bbacde592b398", - "id": "b0078a99ab5fd8889b072e2022171a7eb345aef853d5d097a78b761989a260c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1544792613177, - "fee": 0, - "recipientId": "AWuvoaPLVWvzGLAdJhSAY4gdLnTB5uEJdi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a74e828d11fd810da6ce5be317d67c6795cb1bc02ebef637ff8d0f9de8c00412022073c5c9da3fae7a882d34d29ef370cede0e7b3fdef0ee054e72c659b0dbb25b71", - "id": "eb3495c47848de6b51b7d60f0918727bbd598a58625a3e308353dc27cbe2987b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1544843298795, - "fee": 0, - "recipientId": "AGWDijB8AcjiwxYTB6eybHiV9qeaFXPmsq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201c541b0daa00bdb881cf99ccff32f8c04575e14f55e0987ddaaa9e83079ca420022042d986fda9b4e961d5487e602f78aa3c982bd4e0f9601ce33ca5b0be2cb51332", - "id": "cb64a6bb232fe4a160ef8a96b6f34bc76e8d1f1355edf969e0e1956886384aae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1551677103335, - "fee": 0, - "recipientId": "ASqxPCEkpFj8ziJ6j4FY8pC3AmMXXT1KtR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ecf1d28c1dfbc54be58569ec5f273008330340ab70df7629243b096e32357ff502205f9b0ed9cf1b7d7ee02b6e7a06799da79566c831cbbbb663b56b52b56f2f8632", - "id": "18fc455a58a3d42ae22c76540f497b5c1e74887612b6fb6128f6930d466fb6ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1557630114327, - "fee": 0, - "recipientId": "AYi6DEzFoDBwrRjj7sT78hXJGayRm3tZMm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008ce7aae30ea9cb91237a4a646db6408a52917eef521c4575bb94e8a4a95e25b80220356cea901142679c62987b9d6f36b82029860226a46fc3e056c8120588658144", - "id": "e76471ed188cf048233c7114f4cf26d21b24e77b4e4c4e297cdc3e02645ab56b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1590480695140, - "fee": 0, - "recipientId": "AWevRPHEGJfM4rn2BRV435fKi3AAnZZuxP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3043021f73eefa8d4dda995031746278feb82015905014306cab9766abb3d428d82fde02205852d35a40e3cf3a0113295ae54c76b4465bfbe46b67eaba24b2cb2590ff4b7a", - "id": "03d82bd53b9b6bb359d6773a6ef3fd340664e1946f8bd02cbda99e53fa35ba69", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1591288549068, - "fee": 0, - "recipientId": "AUkNoFqcTkMpPbvSx6FH5HPssnuWeUN1Wy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6ed9dac47067252fcf8bdaee46e90445a60b818d130f96755cfef8aed8c03830220139448b9a28051a55cf48f54f3162f7279e13e6a987a15b020d3d96a7e9cc3c9", - "id": "cba7cd24daeb4cbb94a63f729b6f4628bba3c878b09af82c6d81dcee220e8bd2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1598561495493, - "fee": 0, - "recipientId": "AGwteprikJgukHmkfyLepCefswocmkeJts", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203fd3b520f78acab6d4319657c1995b3abde676c85271af58fd8439fa41ac256502204add7191ae8d36d8028151d67956c891f67bf80b1cc3762fdf0871087a2ce8b1", - "id": "296c9e5c6c5f2fb5d7008c3b6d5191a02aa7410fc8efe451f8ced624d60de865", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1603639652925, - "fee": 0, - "recipientId": "AGQZLKtm74nXFBFvQX954zsXpBCVz6Vs6W", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eae62745fa946e1d39d8f12c6a0f965c2dd95e709a728bd2b4aa800b8174ca0b0220529f3a59374eaf4208b8f6273d90af22dce3d183624fbe50b0ec49fa3dfc1519", - "id": "fca0223018570612e9d70ae707868624c6c7b9b42209077bfa7eedaecb136b86", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1605551439283, - "fee": 0, - "recipientId": "AH5EBGfkjr5UewU4Pkdk4X32FZUacp1wib", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022020d4c8eae1b41ba868e1796068e109d48cc208a5eeb374318c448b2aa21003d70220323a6f9fab9bc56481e8d6ff37be611370aad7957f8b0506688e20084466460d", - "id": "069a53a5dfb9ee6058e1202769e25903a014b12df9e4a815874bb4675e95ab37", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1608266337418, - "fee": 0, - "recipientId": "AXsBCKFEyrq1TmAyBHBFX6Fs2iLfg7qYsB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200fa692b85aabb7c9cd1c23288ab8c3c51707598b7e4067ffc018f27a2428e37b02204dd58f775a2ce3340d1ece51a2133b628cf79e26df11dc75669c2f44b2e58df7", - "id": "399cd9f4dcfdd54a47fff7d51568cb6525e54332251a623a94c8497b228a88bd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1612755177084, - "fee": 0, - "recipientId": "AbjtH1c5sw2cPFu9D7cCDNU5dchyWERKV6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f78f0db05853c6639d8ab877bae34a93f65ec3001b62a313d0790c1f08429b9702200133bc555e10405721c43638af4b77eaa4c57b73c64f786ee40df2f4827d9f7d", - "id": "fcf8f52e8b0a6ea3d181ca56067bb44d93f744e28aedbbcdb8e06ffeef189db0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1615695478227, - "fee": 0, - "recipientId": "ATQZen8h9DzHAt7HgPAwzcWfAC6CaTH4dp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203b9df7a00baac5b1c3b61b590455967d9a38701ef39fbc4e3e37d1c74631f0c202205625993ab0006b2b2d65b0b28871765a4345a6482cf2aa42d0d1ebc503f6ac93", - "id": "c13207f4ba76d22fe47a1d538019c73ac6652dd7b6351a4222ddc158950e9958", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1632724842016, - "fee": 0, - "recipientId": "AMbioVwHwrkTYHt4zqt1r577Z4Y5tgWwJH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210084ed4b704b13b31aba705cdfc9df95fa552f0254e97f89371e9488d672aa337d02206ca6ec6c2cda84cc0534d21a4e74d8782f11aaa5690668ab17768bd97abafa9e", - "id": "18ae7a152adcb4616d7f396119eab66060b48e66553a4562737e95244dc2cf8b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1635164836036, - "fee": 0, - "recipientId": "Adkm397wm32bW55AE1kQTGyk1t6NCz7sfP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c749b79487813d5201670106a0cf10f990e795c5405801c66e3fda6b7bb8515f022012a06e81726ef353b47297609051d850caba385828b5d9a4e044f1e5edb32471", - "id": "e583c7aa0cf2bb4da4d9886b55b893ce1b1052ba6d79ee510e7f58c273c8b1c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1658329844804, - "fee": 0, - "recipientId": "AGvf9xsh3xgCMSnoCYLembrzthHqGJdwbM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200a16ffd1d50f85db3ea81de649a38892df46548d4b69ac6ba8e1cb610bca9be6022005f0b9d13821099d92a8fe5662389f439c232e3da0a024dd43cd5eea7ab4fe21", - "id": "aa4d7b90b4c1fbbd4297a23df89b89a84c9c912f03af1a9bb4dcf77dd52b794b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1672675483771, - "fee": 0, - "recipientId": "AMW2gACZ82N8CNgjFL4q5bzC6dRwpave6f", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200fd4245350f29e79b767acd442c893689dbbe3fe5c6d0b1bb40d3d7d8f4e0dbc02200f0d67c711afabfc3d38324236f051384229ab00f1f9a698627567b8f73460c2", - "id": "a8870e6e74479438af8e464c62dcabafbe785b01824e078e1e121bfff01e880f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1738465434518, - "fee": 0, - "recipientId": "AKFT9sF88rSg7rvy3gvMEJJ8LaYsbEsYH7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa6c33a957f7b2b26549f37208c4ab1d968161f531cc2c681c863acaaa90627f02203138faffd5b376854a9e6d7080b2850d571f07b1a2c7c58113dfc105c6318f54", - "id": "b416130c8a04526c058e7dd56d79161c989f0cfcd83aaa798e6235ee6fcc3c57", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1742839839455, - "fee": 0, - "recipientId": "AMXgwGKJbUt9NT5qEMwY2dxUVQquQDBrTv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220537c4ad8333d6c27ae2d4b5e3822242efa493084a984db369f4538f61fd55cb302205a054469d04d99e8d3e57f47ce237178014ea4c7c33c7e05f5bab627901b1e20", - "id": "d5355bc6a2601705af7f537514f45a838246dd11a95c2b33bd3a3bb2461bc89d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1775549503277, - "fee": 0, - "recipientId": "AWGk5PUpJCLBr9GppUnNY9ryL5RhbstthX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cd7bf83928c227b964f34cc5d55483743a1ccdf17f778f14bde1bde676a7051d022034e2bb957bdf212377580f3b6a7ac3024179b1aaa0dfe57e9cf76c32f37ecf6d", - "id": "ee657127722958204f85897e7825ffec69af1504e20ade4076b07aefdeb263cf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1812187469576, - "fee": 0, - "recipientId": "AReskqQAtGi73GHG6tLyGxpyZKAw6kyhko", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f7d097824b01b8a9d290ac4f7c80cc8bd5f32ef07b22768ab289ae39048b2b2902205bff5fcf23fbcdfe1f60cdd017fe790153ee783facc851af6a65b7e2cde0cbfa", - "id": "3641ee56bb7b499407222f68f9b0432c80b7e6a32a8515f8c5c08ee38bcbdf8c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1847900000000, - "fee": 0, - "recipientId": "ALCuA5ST57RnThByiyCGYti1rFFpy2vaPy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210088f32afedbe40f6ce661f0e828f6815288b96d78409c0067578bc016149d05c0022018dfcd5230d3fca401a478120a60a153c027212107b380c8f428e3991775d067", - "id": "ad9154e66ae59ac9f80f524568452bca4f00455ecfbfc25ab89652ec84f26c08", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1849124693198, - "fee": 0, - "recipientId": "AbpeUAgMG9Zk1XsLfPcNshwumea7mkekBZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022012c3db569ba5f74749d21752f16fe382493c639d383aa30d9bc85ffbdb3dd1c002200dcce7a238a59263badcb59d57e531933ca1264161aec6f04e44e5c87a6767c1", - "id": "f5615283d55e13b2b690b281bdcc412f9d87493b212d2b6b76d529e54d0a6b32", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1876941234807, - "fee": 0, - "recipientId": "AdBizdK3H7zfp6cnpAsAi9uR2fvJNQSFd9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d595be37c3d0bc9d54b9746e84d92ea5d28f1a39df6c0ead2ab145191f95bb4e022020c5ece72829027083379f47cffe67674d4f4ee5e98f9eab80ad16b2fb44a4e4", - "id": "8bb71be1a515fcfdce9a10be706054e87bfd64cabc0e2ad958569ae7a03068a2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1891934510958, - "fee": 0, - "recipientId": "AKxpcA6iVjFtxKzcW8H1EvQY7nz2qrkGFT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205e4a8eb13b197165a479dd3c0aa6ef0d06a011ab5fc429de4b25021fed9024ab022061c25125f58c392d7d10d9b060edfd3a55df16009b3b50ed6b7825be84e0c375", - "id": "06f4102c8d21139312432f58fc981d0e183a97c6726506f8ea1ea71e5957c4fc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1895283091114, - "fee": 0, - "recipientId": "AYR4E6VBw7Ut1ygyvwLxbwDYtsLHrq7T7Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022012c4b05c360635a515a257733ed66d9f100fd39810beba85b4014c21c068bcd30220090c79f001b853f4e38c3df7f043fc8c7c2c683fd2f2572a01e38d11f4219d88", - "id": "bb54551895feb42ba8c85355084adb36d7dff452ef87dd411558a07356989cae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1903697975211, - "fee": 0, - "recipientId": "AbT1VtooS3gA8M6KRT4Fy98tFzaTg5N9Gk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a905b417aa0cd3b7f1b195254e15bbe303e0e59d7e2f7007889e0615bfaf945b02204050e412101f519179944471aaa65e6d32045138a45631fce10b18e31a05748c", - "id": "3f1f30abd5977c35300930eb6a8af9b16f78ee602cbfeabbc3870149d0144c57", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1909607394110, - "fee": 0, - "recipientId": "AZt4XTwHPBLEhr6vkLEYX1b1RKmAN8Yit3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f5fcda2c42c4756ee68e460366d4ee509299d67559a4ce904dc22adb03d8422c02204be79c434dad458962e524da2e99ab10116c61374ac17bc880dfc5061827bf61", - "id": "8c4192958e2a40b7ac6886d39d6324a17d4c05c9c7e96da6bf6316742f2a7b7f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1911195743126, - "fee": 0, - "recipientId": "ATd8DeNiNgb7R4uiQGyGNLYkq1q7FhikGk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e2d7e0d4153794e41d90c9cfdf873867f46da8fb73d4bf5df8250f782d4a4de02204c2c78faf8a805d97119e09e77c132630f7d525bed291fcc40b019e71bc85490", - "id": "cddf63350558f510f3bd2bb86009c71bd39f75d36ea1bb245bf4b3af761f2e1c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1940598754559, - "fee": 0, - "recipientId": "AYeUdkzuGw84sPXgFBdLjfjXtG6SsJ2Rjg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220621146cc0eb8596f176a16040fa8832112c2fc19b49c5a4a1ba830de141df6b202202f1a8232b2057dd529355fc4aec34ca4825630d203182f02880de2efe342d736", - "id": "00f4cf49905ea8c652b71c522fb8bd19ddecde1d2e8feb1ee2961fcdf7034a04", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1941422708032, - "fee": 0, - "recipientId": "AeVN8SgGb4mUYCjHVCCmWfw8h7WHb8LApw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203d2d268ff5c91f54eba179cdc53b51726f6eaa3c1dfc3598be7e444c12a5575a02201cd9dd296215e16ebc511146fbda0989c8a41051dcb07d62f9fd4622bfcbf27e", - "id": "fb2a707333b3286f4d44b61db19a2e617bbe3592de77da45695444b62fc0a5fb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1946861217599, - "fee": 0, - "recipientId": "AGueDNGxUQRDsGUa9aSPw53XKNgNPjUAia", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a8a10c8b659087d985de4bbd6166a9f2c5f8cd46e28be26b2f9babe3a2d063c80220460ce29b3f0327359a4028ad600d25bcb25634f19bd9b3d8e4a3014b2b9636f8", - "id": "e127b243ad79cbf966ab4b1c77b45e6657fbb5f49e4cc3a96406887e2ce4637c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1949845628984, - "fee": 0, - "recipientId": "ARsJyHCQ8YnDZYm8v2sE51e1qtGnK3SdMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f82068304b6f191b9452c1160caede73592d36d75a00beb74a9af9f1886715eb02206b38b09daf8e9184cb91130165227fbf05e94b835a7399d4ba07ff8bca6c1179", - "id": "6ed82d8c0071b52aa28e9674e012ba28759081f527a793da1161fc32d058bcc5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1973042309220, - "fee": 0, - "recipientId": "AML7Pg7WEuKaEWAM52rAUfv3RWE8BAbp7i", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204806c034f7027b71d645ab6df4134ce5c2861e479b9ce8a608669221fa1f9991022052ff1460577ba1674b6a15ccc505b53749bd6fdb8f0d1e3dfa596ca91bc956cb", - "id": "5d719b936209fc04fbf9e118b78c688ae6b586d0d9ffac1cf88225f47e2c37e6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1973887957498, - "fee": 0, - "recipientId": "AcSTQCTesrVHnvoAK8r1bwZ9CpDqiPmRK1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201ee8cf1f90aee896e63feb920df725b6897c6d4cf0723198abe4dcb65fd6359702203e5307a6831a54072cf564804482661789e2c78b9c189760ef1b33579de60d56", - "id": "5b82367f3b78997169ed9fcba1e4eefa8054c96b64d2bd30d12f18f2a364444d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1991239524027, - "fee": 0, - "recipientId": "AGhLhXKXAUWDZRS3skaEVMzpcyeqCyE6as", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022019f81fc2e04573a9ec9ffcd4c6504da8d206467a742c8d9cbf96d1980ab77ff2022050b0746effd9c253e61dc8f89b015b4256764292860db88eb9a6832c33812b66", - "id": "b62ecd68c562fa705d41d59b3f871934b7a5594a89354cbce472c9772e100393", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2000000000000, - "fee": 0, - "recipientId": "AVMX3Y79qXSHBk6tUrcLAeKh9C25xtyJVL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100edcdf6bd19aa85b0c2078804a9a021847592dce74d2dcdddb38a0ece549a080c022057316f1c9f36d89f08ad9020385c16fed5bc088f12e588166fc984e9b0c6fda4", - "id": "4a26a119361e812cb7fa868b10ad2c903dcb681418113abe72c610afd4c1475a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2007149179076, - "fee": 0, - "recipientId": "ARLoK9ABM7bFmaqLMaEBbfTVvsXqyYaeXV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e924d9016e8ed30d3e06fbea56d70c05b670b6a9d6999a039b93b0d436357b25022015da3def5243cdac0d0baa92e8f695a5018e06da5155d17f7f48dd7814341b3a", - "id": "f40dec591c02dae1e1eb176583476d7a7e291ee5f6be1741b9ba3c0e97013f1a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2020119321574, - "fee": 0, - "recipientId": "AL4youLTt6UXwxXWmqzAxznH2aPEekZCpf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b4c556e5c648045ac2cb50aea26bfc158506a494c66bfff2d22941ab8a3f6af902206b6560e4d19bbf8b770c2ea0cacca973f94e683b38df2a00d81727d4bc0b41a0", - "id": "394ffc5b4a91e6dd81621246ca0a1070f3b451aaeca3f1ad48c4a4186409079f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2048474511235, - "fee": 0, - "recipientId": "AGsx5dwwQju71REuRJd8iv8AAo6wuo2Hhj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210083dab3dbab91f91a9f7bbb3fd91b16c5457fe84e918d475cb7e544d09466c8380220504deee2a01460cc9dfe7be2531f92ed2b14a6923bc6bdb02ad1131803fc7b28", - "id": "0c1e0d578f3f6afcdf8d0406ea5478ade1c52d87b31153fcde6c6345cb0db9c2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2058848207492, - "fee": 0, - "recipientId": "ALcc4t52pgbFF1iZ1dntq8oK1TjprrckKT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008ebec56c81cce72ade2152091e9771c0c9b46c411f17c49272b1647466b4d1bb022022bb7763cfb7af70ab4d97d6301db9a96046225be25b50234a5e1bc868e978e4", - "id": "e3e18ce2031b8052143296bee5317ba5013b6df080a1a222fcd41af5a3263398", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2061097549268, - "fee": 0, - "recipientId": "AFpxF8SMNEMaBjhw5NKdydhfU9zeDByEnK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100df0a94e388245d9cc205bd2739d9e17f02964f957707a88a51aaf03d029fb58f02204fe22271ef442b7364541ce62a5d833bdf061d8c6c3d78809a243e26428b2f05", - "id": "a91fab928d30719452a7890dc5d7b4bf323642923ee6e99c7e12aef8eb5ed821", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2061097549268, - "fee": 0, - "recipientId": "AbfyMpZLZXJukJ1jCLkQaDdxU8hRMCroHM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203b386391a09c598649acf2575dabb917adcb14572a60f1ea8cbdfeeb4557549902200d3e0c146cd9d726ab91b215b817dbe68adc25dd8f506dec264445062dfff1ea", - "id": "0c1c747f9e680eb8199584b1f2a9b3b855db0f700e61ef3041f4b9388bb0c826", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2066612412189, - "fee": 0, - "recipientId": "AMgiiG8wXhd16NBjazW2je1hK9pfanLyMV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204c5afff450f8735039b04fb34b6324136842b66fce3e1e87fb5cefbd23d1384c02203364cf3f9b63c10e14909c7ba9aa6d442da124bf257c707277a8167a05bf894e", - "id": "142834112cc331aabf1f30dbcec6c6a538c9b47927eb71ffc30083f8322bb224", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2109666070297, - "fee": 0, - "recipientId": "APJ25Fek6dNkA3jEExkyG67UAzrDRvPxf4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022047b3d2953eb0b6e1834bb77053b207444fdf49669578ae639e062eaccb721c9b02204d142ead55d393dc0557b1d4b417e71cef41ef65623ffb5bde6e56665da2db91", - "id": "439911c5383eb2e090858e43740e9272b7053d4709c455a5fdf21ed46569a591", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2153677032409, - "fee": 0, - "recipientId": "AXtyzwP8X29Ddr5KUy6GuuxWibW2eNypng", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009ce7072f8d3bf7866b357aa36b475f3eb102e31caeae5d8d3c6a39d8f3c39a4a0220636f0f46dbd6a477194025d39f2db234449195b53798fbd03ce3e2ec3c86edb7", - "id": "dc9c7ec06d60f03495f590e1e46a6283db8cf62dcfd2186fc65249f4ddf51074", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2163834169022, - "fee": 0, - "recipientId": "AaJxRN92BCtogWtzRUrHJC8TWqSswRJGA5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207120a932e96c536e2c2f051546d173f3c58bfc83946fbddb0f991431da9f685a02202ebb91ef9238d05341afd2012ba2bd456a54d2bfc20d22ce3d10d4a618ebc5a7", - "id": "fb426988eabaa074956707a42bdf4adf3a0c10cd719a6c4cd6fb2828fa9b7c53", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2184588569625, - "fee": 0, - "recipientId": "AXKzjecNQcpJvwpm72ZE2DDkf3c6qV9FQz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204dc5f6f772f7843dca653edee6da99c709d3efcb585092d3b563fbb58ef9ee93022011ad3b1c4226208c88fbe189f9a40fc22964d8336fdc290d3051840a20006499", - "id": "7853c4e80e57ad1152b9b7c8f7e8e1f6d5aa849ae1f0b5a3732121ae11b929c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2191161363601, - "fee": 0, - "recipientId": "ARV6tjuLPdAAJFhTeho2KbZ3caMXNChvwK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e9fa2dd98b2518aaf44488865877488f3413caa3f38d70d690b8693519f96a7f02202959afa89b20dfc55786c82a636edc53260290744beb427150b506b666414da6", - "id": "0e73c449c0116a23ef5abb5993be907794353557601e0a39e96da882a7c948d8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2204931827339, - "fee": 0, - "recipientId": "AaUvDwmuhLvZRwmW8epsDmRQfLh11ufUzH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009ebbd2c288878988a5f18a448ec7c33adaeecc203084620f2f98b3c74960ae6402204014eecc985db4e8d94b92e94148453480f526521a604c2222b3c04b90fcea17", - "id": "8e95a6db2d145e700e36e08a413e0d0f2a6f23d0640f468be65b73b56469ffde", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2205078842396, - "fee": 0, - "recipientId": "AJuvjPUQHDYkiV5R9QQrAmKQqMsdCiyW9k", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206083b54d4879edb8e02dcd5828e6117ee97624ac9f1381da575ca3654d7da3ad02201effb3b78038dfbb482cc8d60a8096c77fb351387cbeb5b1fe7423d1aac34c67", - "id": "bf0dad6cac85409e568de7d2ec72bf9a649a5a78e2f2d49c2096800e05dff8bc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2205225857453, - "fee": 0, - "recipientId": "AdeDz2SgSHtNSS49qk89UQoosmM9nSn8yW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022060fb58e2b791d584bb622cbfbb9ae89489a68b63cd292445db2bb82c60031025022065952795086ac1cd075d4b2fb58356579b3000948628d8a0cd91170eff57a83a", - "id": "7acf01f865d9e3b0172fd107b066a11d10e561a233af85c325a1891ab695b94a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2241213029168, - "fee": 0, - "recipientId": "AcRFukzbp9dcnpAfhUR7tKr2T9TP9ysr8E", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c9efa61ad9f4ac61a62a753d773e5f5b07c68348d41f651ce11b4381403dbe9202205397961b89303df542243d52407a2f3d1d1314b7f419818fef1b6a5a172a7b9a", - "id": "68995bfad121ad8d25068bd9f6a1c7442730220b39cc002a41650ff3a38221b4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2248243233437, - "fee": 0, - "recipientId": "AeZ1HHc1d6eg8P42i6fVcJVKsXw8Eo32hT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022047e447051f960a6d1e554a72640858c8557099de95f891b692f44b15ca6b85d102207a4d3e483e26de4de2de0c337ad2bbb581312cfcb77f806d320d88103a1aa227", - "id": "7eb9c09a76d966babff5fef1db9a160af8885f4545fa1da2cba9baf5b64cebe7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2262177091195, - "fee": 0, - "recipientId": "AJzzwPzzyDCiDc23uQ2u7AMywkfzaVCcWt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206772491d6df99e5475ed8609d3a35a646b77fe85b81aa0cbcd5a79ea725a0f6602205b19e267638b99baa4990e16ccdab0ebfbe2a9130618c83c30ff9f8fd750eb2a", - "id": "fd90da851810e57219244396325da5e2064640daf9ba6114102aabff880414ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2266868808469, - "fee": 0, - "recipientId": "ALNvcAUuj1GRHaTvDcEj3J2LJrpbigMfTd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b0e14408162136a3d9a103e113d860643cb583e260bed70db986b548167bdd1d02201e3cd6711b3a5a47b07cc79a2a0fd24d701003ee54bf4e96c34feaae11586bcd", - "id": "997f9a8497ee44bce5675df192fbef70e582563d5a8d6774a10a38c1ffc870e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2271096947063, - "fee": 0, - "recipientId": "AKU5SETsMF21C4QNxSAFEuMtucEVZbHrbT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220405439f374319ae7818197cd605c29f981ec33ae5e5d8ff63daa6c28b79a12fd02204afb1feecba0339d1f6858f00f45053c4fe891a041a810df553c0274b8da8837", - "id": "625cabdf0b3e0ec7461e182b037d5050566ed0cfc96daeee199fec7372b428c9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2272050883436, - "fee": 0, - "recipientId": "Ad3NrB9ak5ASf4k7u8UsMUw62onx7t7c5T", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210093ef0463dc9e57cf30e412631760d1f8c9546fabac8d02a81d4450c1b0225fc7022034d6e9612a01b53cbca18c08c95ded5ecf9e16486772e910e857e32dd7fcc8d6", - "id": "56ec4d726f2f6eb3bdbf96201d7bd4ec0dc76222145e4aab469a5a8031bc0897", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2276774784522, - "fee": 0, - "recipientId": "ASdJV1rBGrv8hr3n6PAzyEstMMaeKRC9r7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022073089b31090a9893b58f7d356e2ab39709f213bc753efe142dce1028a18fcc85022027516343ec493797a210a0eded5d162f612c19221ee758925bd0bff3b9a38a4d", - "id": "b2f66b94ccca70109adff57f5b0327c01fb8b1cf9266cc5d34e67c1078619b6b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2287751179042, - "fee": 0, - "recipientId": "AQG5AyRdBMK1EiAxjtzumHBKTz5fQziJaQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203358a1b09882d61785e37ac06243189f37c429a08c245dcb9cd302d67bc23a89022036c0830ea03b7fc64d9d673d83d99d5001a39487d5207983a09a6b2bb45e8b70", - "id": "27f99bcaff31983a34701f2db5614995f09298ec428508c0ef310abca3a17156", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2318734742927, - "fee": 0, - "recipientId": "AHWbM4U2eC5G8GqhEXjKjyjQ14gE2uz8WB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206f65c7a1ff3c846ba9ecc55cc3485c498ea01d316a456d7044e801dc910edfde022057487ee7c70aa9f4920bd4a32759569e2dda24d493d427c1a1aa2cc92b6f860b", - "id": "726d10604f15f9e5007445f0b8f829d8811952a0b9f81895fa673d45e579a8e4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2328424475356, - "fee": 0, - "recipientId": "Ad9T3DbNPoF7SnCuhBEMQwHuiKjEXmhTFn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220725c7b21c8cf49ce86093c81ff5079c5f3ad6a7955529a52354e01c8b93db64502204463fa21a31a3cd7fac42e62f0e304ad7861d71c7eed6114e1c64cd37ade9438", - "id": "66ecd1311f19281f119c8bbbd34d6f32daceb58f2c747c32bac3821d1b22bc36", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2338875909420, - "fee": 0, - "recipientId": "AH5nQnHDEueDsMA1oL4AxTYgPkGgzrPPv4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210093df608b5fc963f1724fbb3f0e3e3c52c1642b17330988bfa45d7e28b143f7af02203b04d3b802038a17bef863ba2e2e17f1a288ae1764ef61e7410643017643f054", - "id": "1446fe4a270106969f516ddb59b0d145c4df5ef0c58aaea92e16d4e73d4edf17", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2356684778844, - "fee": 0, - "recipientId": "AeNyCDJoThuMtgGCqo36Qj1RxiLzp2wKfX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c7ae57e20b74727add16c03acabc3f2ee6e8e200651ed2dfdb973f3137cbad9d02201c33c46ac75f9d1183ce4a60bfdaf72021415676828992f5b4bb4a15bf8b06a3", - "id": "8e4d777e325bb6e75f240dc86f0def43fd27a1d7db6efeae8af457fb4a0e5a7c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2366781260994, - "fee": 0, - "recipientId": "AGog6GdAzTgGhYXuifWnrKvK8VAPaEhe3U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210097204cf3140ccc71aa07a31cc969dd5b532d8ffbbe936581b08914982e823033022063df68f995e5b8f76d391cf7faea9b34fdb2b3b26db5ec42dc94055efafb86b2", - "id": "250af0610058d8455a4efe16382bc4b0cb0d3206cbb288333ac3550ceb44cda9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2372499252388, - "fee": 0, - "recipientId": "AQRccct6eua3CnzgReDsYVdZhjJEGa55fM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022048fcfeda36ed20495c633c7b1e671a306df8229e2a7d4a75547c81b8af509742022066b8f68d2adae68654bb0fd01d56c817b45f1d5fe686c1a180fb53f9109ebefb", - "id": "72c59a55be7083859a7fcbf1e4b9d5e3f39013b292c812fc61a83b07d44aca4a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2381643926049, - "fee": 0, - "recipientId": "AGvdP9o1xDU5HB9ZfFEBKTN9Td97rka9GG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a95fbdf4d29b7d8d00bf352f5c0faae88fd0c6954119c1e7de8624c539efedd702200aaa95e4924771e70dafc892933be295c372f3d7614058f34f6568955e60e3d7", - "id": "c4be55dbb512485ddcfa1a44a64b77f72f6a623ba28a1ed74f7e164759921b69", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2411046937482, - "fee": 0, - "recipientId": "AS4BtWoaap3AVmgxh8cQj3xkcd7dkWfK1J", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d2efa679287190ad29fd7fbe91978ce07dd5819acfc9f08b12e9aca27c5d9fee02207da29d7168e36a88509b6d73344e461635bbb009e1203bff8c6ea31068f8b2c0", - "id": "dca8904b7776d845555706aa639fbaae9e6ae4d6d912c796a41e793dc2030d6f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2421793988476, - "fee": 0, - "recipientId": "AGErCuSAoSpZNQ7ubeV4HSuHrwoxGQoBbq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c9947449ab96e6ebafcae96a6c080669ce3045f711e0ea1bce651061cb054d7b02204bd9e3e1fa0bbb0923571ddafa6943c69ca2096fd82dde9a0fea4e3eace6bbdf", - "id": "c36be7fc674eef93fff38d284a209599ffab56eace2e569f9f05db0ebbc98e8c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2424820646199, - "fee": 0, - "recipientId": "ANgyLRoKJC7a6RLUJw4nkkrL1Rni5eYs5i", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022040b3f827c57219cac01e956cb5aec623fcee151b57225ac06edf7fbea1d86521022021808f0d44f5a50c7f35a25e40c0034b7831deb627f8c2922ded0b406a00cc63", - "id": "f3f300b5fb8de1f81c863737617f45c4b67f80fbfb7b72cb4a4e0423718df75e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2440155918800, - "fee": 0, - "recipientId": "Ab9oevZRRtixxzvZjVA5jJrjxi1kwWhUN3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201a6214822f6166833f7fcb539cbbca80debc0e0d01ca91c0f87a64a5dbe993b002205bfc9366366bca57d54bb5c389edff2e0b90728fdcb58ceca89984ba9aa12e66", - "id": "a6fdc2fec0e799ecc73e8310e8285cee66c1420f3edb9d915cdbe7414c23b824", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2471141445854, - "fee": 0, - "recipientId": "AcQgRCArM8s3iCzXmaNa4LFYcPwPUqR4Xw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022014be56285973e8fa73c89a4de8c1d5f06c30bbdf6d69c9286e19e18d3c00cdca02203a8777b4f3cb0a28cb42af527b3a13d284c8bb5a8c6a97b1f04c5fa3b7d42dec", - "id": "8dd4e91bcad96169a7d3e4a01fcede8898b36e45797a4aaa6c318f1c0ac02889", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2481166956848, - "fee": 0, - "recipientId": "AbKSfhS4rZQXKJjnF1egbDqBh1fnkoDADx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bd942f63f791c0cc5f90d50edbbb6837a1445b053c599abd9cb8bf89e933d96e02202e1620e229f7657fd1d87fa97c076b42a5a8a4b5eb6992cd2cf1308a4d2585a2", - "id": "cc51d42bf081b422663bcebf7c9d4676a1ec5fae1e4df76e7ef63bc9184e7c50", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2484636536440, - "fee": 0, - "recipientId": "ANTd31Ts3VHYBkd3BmbAv1FHdrYjVU939o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202aadc401035be55c7a644fc4204865a0b25c61c8ee7557c35c8d6908ae51748602200eee3eb04aaadda8f210440e269c03868d46fad3f80850c81f7f526471e7090c", - "id": "d5f9faff17330dc56236696dfb08548941a881159948c6aa100136fd416ae834", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2532393949328, - "fee": 0, - "recipientId": "AeZGayWNSzKRkeNxkHJFzHEygeXsh8eytK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022036bd56eef8476da49e4c226f6d5bff50a491a4b0d077f0320df704ec731b3ee9022057b53f23797d0cb167b534f88cb0004961c2ac0ca25adca7a83bb0eebc541cab", - "id": "b6f7fad4820bfd4a97c845918c3f7d39a9cce601db5947b30f0a8cf515346fe3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2576371936586, - "fee": 0, - "recipientId": "AZMvWheiHokcgXW1UBvepmCiSjtLESNoid", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200923d3e46b3595c3f9c728deb6f8f34cb5dc49e32cf5e9668d0d74e31181d5ad02202d6079625ff697cb987da8782d44333768b883c10407af365fb4cc9a5135aba0", - "id": "1768864cee1985d3e4440b710f7cc8dbb7bf095f57f61af22f4fe9c1cef4e1a8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2584344277365, - "fee": 0, - "recipientId": "AUpLiwm2WVWh7Pqeizt33LgjTfLxaeKQZM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d3cb2ef1d65dfefc87c26a36c625d9c018924faa17876acb422727fafab78c5d022054abdebdb5079c36789fe3f7d97bf736ef5a314555aba0632ec3511f6b72af7c", - "id": "644c4cacbd139ed34ad275cc8e2c5d147a4d3a5e8ceda34f93c7860cbf3285e9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2592139332838, - "fee": 0, - "recipientId": "AWEVGHr3x1e166sLRYWjub9tMuRmrwv5Rz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dba9d478044f5f59fe1c85f8528f1e0fcbe8ff1fecd0cffc13d09ea320a180480220086a1fa2d5f23c120b0b8e49753d8e4c9a1772f8b2449700e9987f7679acbdbf", - "id": "27a27a46a268bf1af2f7309c1d6d4594513c1e6188d68216eadb573ebaaec3f0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2596699852052, - "fee": 0, - "recipientId": "AVpBNSkQsqp9VtK1hZtf32ZBszABP6mPxz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201d4b144431415a853dcb795f3ead4eb9947a0780af7303c7f8f1bbe95f0bf470022025539723eedc56622cff150b1f6d65e5ff10ee29a39b5f29b77af51d455616a9", - "id": "d6adcf3c3c7307e26318198bb541bfa38f3b12328b4b71a87eb3a6eb760baad0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2599032418076, - "fee": 0, - "recipientId": "AaQDmLECcNXVzALX99oWP8ed8wrz7E6vQV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022052078242e033e71cc764c5e6cdb1f240fa434c674d8bbcf5253f17b2e4bdd95e02204a1095961b33110272cf1d944ff3cf147cf784f02cd37794205369f75aefeb09", - "id": "06f66303e2b00608900e64f219579cccaf060f93a50eb1ffb83a6af3374128ca", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2619541018549, - "fee": 0, - "recipientId": "AM1WQJyB6ENVcBM3qumifK1iyGgmxAtfky", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ec33ce506f26896c7cb3f5730859b4da1b52fe9ce4fddca9c17976e7522d82cb022040a6c88430b555f187b83324a99cea223f56be138e5de4d860fcf2a3315e91c7", - "id": "105f4cf709516db516b88e985518ccf82eb4939a886c1b8fe356ca85b2444325", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2625323003381, - "fee": 0, - "recipientId": "AbYapv2W4GTvn1XAQopCFPxHTmGuhZ7wrU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cbe7e43ef7a56ddda2308f5cb60204611e24dc16d1cd1c681a4190bb109ce8ac02202b41f1a599c6de7bf37d94b8105c618460729edb0235d3070948a002d408a315", - "id": "706cb7d860d44fd5ca836fa5667d81e60dfb86c0e8190850a1ef042da330e2fa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2646271028944, - "fee": 0, - "recipientId": "ALGc47qvCBw7dL6y5ruLrpWXdghQSMjRX2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204756f3e295f5dad6ffe44c0bc09e7a8e215acf01bd7fbe85babbea5e1682971802201cd1a6af8e4d6440405631ffaa0b77333ff31a4068675a4ed9e32e345e812c11", - "id": "a7b9cd14e449f0809494ef6a4e94e257206e3b339196dafa6cce9a5911eeccfd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2735324506659, - "fee": 0, - "recipientId": "AW9ZtarPjeMUH3abFtVvC2ECNTkMS4w9ZY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e1b443ca71f70cefd6dfd6cbd2120881c7963e60b9601d6793d46587bf64f8e102205519f08774fb4a7ccec42ee49cf10cc1e60c8bdd4b8f9b0ff3d403cf009022c8", - "id": "26d5fd2ff3c8975785fcbbefd6ad746f51727c273f70f5545e95d3fd683593fe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2745083256244, - "fee": 0, - "recipientId": "AcWyTqpf7XuNnptSCJ4cTyw2hqc6PhDufV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e1fe873519ee311dfe7114f754260443d272cb2faabdc67d5e13b3e22dce40b902201b2372a23b827ea4c3b588b9e55da4cb09f9f18f201293591bbba3791f1fe46a", - "id": "3ae55c8fc62086c2ab577dad2cc85e9fd025d8e035ccdd040857f25f3215a29f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2764221209306, - "fee": 0, - "recipientId": "AS2NDZSgogBv8K2PdDNYwWcU8RphnXARQo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200d7315132954c4ddfb3a09c6204a1dc0be0a415d87b5243093e8ca01c02057f70220377bb5e835854b2a1372aac469e103ff59fb023acb7b05abb259083ac0a695c7", - "id": "41220183a52980e1897442d243a14f13cc5f6e3a9356c1fa229b6999899c152b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2806744646340, - "fee": 0, - "recipientId": "ARsQwGzjkRCscuwe8dQ2MHPHSYqmbY4iSU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f71fdb335a27b7d37d9e07677d14e546e91f1bc8be8c5519179853be3f6ca9d0220712a67657afe2b13a1ed42f7d29f662c625b55b577a87d032e5a513d3a33e875", - "id": "5a83436df7e6bcea1e1221846e05315cf36f3c5c4b06d785344e8010e48cb64b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2906876974000, - "fee": 0, - "recipientId": "AT51Pc6EAJKjK4Sgr2tf3i2X7Wp6L1Nmef", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210084c2421ebaf0f1cf5c9ebc09454762d5e6b47291c0b88d8c644cef634c92d4a9022000b3c741497725e2659358254baf0cf87ca742dd189ed5b29160e72e9a7e8d94", - "id": "5ef7d8921886188473d15c50c752fbcb5db7d094efbaa8011b23929409a7e642", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2910898131837, - "fee": 0, - "recipientId": "AST9oifUBT4nv5kiNZwFrkTneZEyUshbti", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be3ed06ba98f260ce44e0049f1ea20d5efa532ec97e9c286a3dc6611f72048bd0220562050948f72a8747b5e9407033c083ecd60aa78621b0f8d7c57283eacd286f7", - "id": "f04b90545d3dfa0567f8b277d172f32c9cbd9de645036edd4d5e0e74c77267a5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2940154128213, - "fee": 0, - "recipientId": "AMZXd6g2nidHGd1pZFUqZVpJnNrpzqtCKF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201396009e8dc5376c341ea496e7542c2b96514819dca5aa23ca8dbf8efc1175ae02205a6083d4d66a15d511c672cf521d57ee61ac55bc876f494ea6450d2b7b5f6640", - "id": "46f24f882e0b0a1df1eeb6e2b17ad3a37ec3b9eb413d8aeb2af5827c1a16e286", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2940154128214, - "fee": 0, - "recipientId": "AHwkTdboEC2Nm6NwXoURbTzoNRPmMVXCqc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205f1d63f3ad6bb0ee39797711bd1939b72f6081abda7a3249ef9d690c4b2de86102206022e684106c6e47c8441c5cb2ca2e583b3e79fc2f8db0d6885ec2590cf88c2a", - "id": "f868bf1d98ad88b9fa263292382355e6e21513a7614bf35e0a413e94602b4f80", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2940301143271, - "fee": 0, - "recipientId": "ASBzgywUJ9UtciVtbYc512CDrgTDhcMYng", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204939d50212d6492e23e7f3ff583044ae1ad7f01b173966b8fa83491e2c94b7ba022033f1a5538b4790cc5cfdf0d52a093bfc9e579b4da08643c3359d3ef0f39bb34f", - "id": "202834102dabee749fb707c8f620ca53d51c5f92aaa4113dc1b86d7989433ed5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2940301143271, - "fee": 0, - "recipientId": "Ab2DLnDBmKqcwBuzhQUa6wWdCrECZ7MpTo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eac45b9e454362b7fa3852c5c8ce5d2ca19db0da27428f75543d6ef075c91b4002207a49026e26676748eb003fa34f5244e0d9ad7cd881e90d9adbd158ab322e82cb", - "id": "f608e70123ec8b29777cf6904ff2e530f8c150d8f59aed648866acd8bd250710", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2940301143271, - "fee": 0, - "recipientId": "Ae7NncFsoDvMMmVqiJPiTmibiB1aW8XYk7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207767c2f983946bed560b7ebb36101222275a8bd64f0c0b4f8b2fa2727f344b0202204222fa85950d3ba68b37be153d77ee9135a12d1f3a4ca7f8bf201d18502aa12f", - "id": "56e775c567f7f7c2c7e728221167d54bdd021f722acfee6aae640ff888c50801", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2940595173385, - "fee": 0, - "recipientId": "AaYk4acmLnvcKiiuYbxQBB71goY5Tn3Dm3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a0546e807d1390df8e97606cf30028b6145d287a93dfec9f5c7b9672d172afb102207325221156e2b4e32ad04c80691f073e1dc6402b61fe2aa7296cc7d424d584ea", - "id": "89d8e7581c333ef01ce0ded3dc08da5580c358e02444a6c37bb2637b53e747fe", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2941331203166, - "fee": 0, - "recipientId": "AKJdgCXk6ymHHKaVoNhkgwtsS7zeSHg323", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220172d555b9f5ecc5268d9af740ce9aabd35ba38a1e011b0ab05783148bce1f96102205d4491c92d5bef700518e4ba909025b6e0da94ac550af7f33f14a2a47189b72a", - "id": "09b3d72c0f9d6051e30ec2f6dce7512820a02c0d95edeeaffd79dcf90e461fc6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2943241444414, - "fee": 0, - "recipientId": "AeykjWcAMwZ8jf9UkS7NoAEX7W3kJiBWUZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008260a61c32c694392729d954b56d02ca72468167ebfe0c2ab4f96999606656d10220247bbc53dfc30f0e6c0267e7cff73f44f3794a77512b483b3a16a78f95cfa609", - "id": "524c4e3f8d8c26f3b78ffeec170648af3410bcd1c4ff3dd91b783cd9a2f229ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2975619527639, - "fee": 0, - "recipientId": "AGT3E9aU7PuT3qF5dGVQFwAG8zuXTd7uAp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220051f525cc7336031431dffad29a63ea2aaba824790c6db30ca40d529d77d1ba402200601f5acc9a39b41fda57e49a3d2e4ed473b323336f82b8c39be54cf72611de9", - "id": "dabb12f952a645ab435977dbe2353cdd2609dc6ed1a672394d547f0880d93cc6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2982935509848, - "fee": 0, - "recipientId": "AKU1FaKbLTBfonNqSAUidf6nWS7fyaAs8d", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a399bcad2f889782a418d8c4f43fd50545a9762a5afcc2f1c0a2584b9cd9cd99022033c4e961ea62a4db1bedf6dc894b402dde87328fc5426bf7080f8181b0220316", - "id": "ce7293fefd0f534c30b3c2845c129fd297d3fd3d4ed7c24d3e0ed490801eb0ea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2985301143271, - "fee": 0, - "recipientId": "ASgKLrpaJGxArbtTNuKHTQNVWyGitGGpZC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202de43275dc393daad998ce1dc35038299d94e82655584b05bf1ad3d88ed4ad5902206722a7a5753f18a2f280180f411bbe0be380d0fc48d95007ad5381f367ee3be7", - "id": "9a99bf84368d99e4a83fa780867596c6be71a181ab0e4d80b99e843956435c4d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 2991168353049, - "fee": 0, - "recipientId": "AZePtHJzYCXrdG4tF9SBHY6k6R25R8ZBn5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f5b8cb767a75c487bb2d1bc078baf5e9bc1ffa024177bec52cd34de5c87306f7022024806722c1dfa0212e49d7cc22c1251ed59d4c0fbd9963bca459aa0c0f64f521", - "id": "a36430d45917be491b50a09e86615f3d1a67af148173e7867509d704df2b1367", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3029944047194, - "fee": 0, - "recipientId": "AM8f4MdYwPFB9XKqvki7wt3MNBXK6d1EtU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022002187664dbc4b4203779ce0717e3efef35f94aa241477a5e326ececf4897e3d0022029c1e31d1591c2c54f242b43bb27575b94561bbb82fd726f3c54738f96c9025c", - "id": "e0c8f8e46c5377741c86b6854281d0056919cbebffdf4d641785826709879a51", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3030774378583, - "fee": 0, - "recipientId": "AJVm2UANQSZBH6WhTLM8ZBqTNDwmYf48GE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100988c50ea74333df4771619f0cf7396f7569ed020b6d02ec3bc3ab1b2e93e186502206adc99ee4498664293176a09208ca5631fb03147f6fcc330d9dd28102f088e8c", - "id": "90090e44f39b81ebfd82a3c3c68e11c81c22757c91460e309879913aac6e915c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3043211683285, - "fee": 0, - "recipientId": "AJi3yet6WjHiMNx8j41pbwNyoKfZtFbzwE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f0d5d871e104cb93b22fe9937872219bb52695f3be7508144d260de8fd33cb43022064656b7b33df040d9a49839e45250c44780cca7ee302112a123694fd5ddf2a81", - "id": "71b958d40ddcd3e70236969225049f99b99e012608253a52102926154e8e6eda", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3055154128214, - "fee": 0, - "recipientId": "Ack5VMNG38f4DCpn53a8rXNoijKCpaEEqq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200fc998d1c31efc5222de1447a8284cd94cde778da61ef0f13ec333196318fabc022049cb07ea8df855b9712d85f334e2c2fda9ba07a5d7065f978b677bd900e0f899", - "id": "2c42da9e75c92d6f66144898ef06ff9fcdc35853a8036788f3a52fa7cbfcf033", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3059124967808, - "fee": 0, - "recipientId": "AaFaBMRCVAF1ZBfJx9wgpZNxxANpJdJSYs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205fb4f163f5d2276083b58ce3c7a5739f74e751d86050c6d9bb10b2be84c343870220407388a647c4d12beac50154d72dffa01125a3a2ed5f4c26fe20fd385ad27316", - "id": "a05319b6d84c6991ade6f957ebfcd3193aefe5937122e7afb1bb7768eeeb4057", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3131025807748, - "fee": 0, - "recipientId": "AKuqp4G8AA3XWNe8JwMhTdNFsjhrWhQ7ZF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009c9c994a2e54b52e7dd638337ba112c43bc2150f740f470de9d5b72b852947d10220050af097f8ec27f51f0fbc9d052e5864aee99e7b6082a61e3f1488497febb899", - "id": "e521e3dfa3379d083020dc9c8ab5856bdac23ed0f412e1568d9704ebd73f4114", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3136161306270, - "fee": 0, - "recipientId": "AHWAQFqCmJ5RrKvicKzg4qEvSStWN2f4PF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c2673236c613f050e22ea59478067f803201b9c88b51a4e0215270ebd6ce17f402206a586fbc5b2e0e5b27edf94bca44e9b136fd35a7be96f5d12c2c3030286a8196", - "id": "08a2b9d852bd06e8a2158c95142c88624887ae9ec667eddd62b02036993c3d48", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3141084221939, - "fee": 0, - "recipientId": "Af4K4T6Y13ZyZi6hQxSuFwWHVRZQEhP1D9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008aa3a5f09a85a7e94a23beef8c95851670ebbaeaa7d251941a92a3f44420951e02204070c008e27d95e14ac9fd50741a889efa464ae4c740cb44791edeeab2815227", - "id": "391d96b95f7d84192a1f319932167a2d2457624222afdb0dba66eb4b3ef9cd69", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3182675621655, - "fee": 0, - "recipientId": "AY36AeEFHXi5uevkukSSY63Qv29xLtBNzj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c502e26db7ca217fb755fcbe660da9ee6c29047ddbcfa3962e6cee1c15fd213002207ac54ce32e991a0c654e5b3c684d0d2efbee9d2967f4edeb80ad823f4b19f3e6", - "id": "f5c46574a98b4659092e4193d2c05405973cf80be9ef391e824eadf89003f116", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3208001085999, - "fee": 0, - "recipientId": "ARtWsVUYotKEXTXxx1ZxCNvQUrQsnXaGSB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203d060f9f1a25f865c48d773b58228dd2ccc8061b9903039940c507dcdf895f5c02202bb37da139cd3d4a7cf5f677f1677202e28d0be38468601e829ccce2cb925263", - "id": "8a6dd2571b665a50bdd9aa12e5908b8b7bef290dd7e77365593830c0c3504355", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3234184242541, - "fee": 0, - "recipientId": "Ab3SAMJGK1fTrQAqoio6uxNiQqUPCmrzxj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210083d0d329a80d6ee339c024b6a8143dc1ccd2a4ef9cc36e2a2290f3edcdb37afe02203a76762e23f40fc99ad1bf9684a19d3e5060ab3de016fe025acdab08546ae83a", - "id": "ab3ddb2107eb42d4b92b1ed42c5c60d6ba432932db57b3f250c5ddef2e5f0b40", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3234331257598, - "fee": 0, - "recipientId": "AYUmyzDXiqHcTnFjtKQC9GHJzFQb76rTjv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022017b50c51bbc0d381d134b7fa916d7d2e3ff07819c217144c1969a993af614f4402207aa346585c21d6fafd6a31eae748581272ec2ffbcc4cdbf2cd12f258a113340e", - "id": "47d84153cbc76cece88fe01fd661a4fb388a40c05495fd2d4bbe8af7b650e1ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3240854690572, - "fee": 0, - "recipientId": "AKdcJzAriRFVftFqfHkrdZ1kYKtSajjVGe", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009956a1473b920fc46270c1b8b7b53ee4be22aad70d53a9e9059305f1a8d3107b022035e82d31b43b221e7a7da617454ad1c27a504ffb107cb4fb9efe88539b886e4e", - "id": "a599896c8dab5a4251366d83c44c108d21f2e82922a01214d24857c5d965c9a4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3264823516954, - "fee": 0, - "recipientId": "AMnGLCrRYXtxmrnzsx7bDzEx3G2W4igcx5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cc08ed2979526f2e9e0876e177871deb20ac16bfb094b240475efa11cdf5642b022005f4dddc0a80d1c9df8eaf04f3fa4dff3c5b349e5b4315d6f037f80ebf95aa74", - "id": "0b33f80be4c0145ab42eb74dcd63c93ef5dd8093b8cc2358182e644f12287634", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3307427638563, - "fee": 0, - "recipientId": "AH66WwUgJHCYvEbX8hTFpuKpx4UwzoaQEz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a243814491855ae7abe45fad6ea7c7e939903f198383b224091a25e22fb357f002206e4849d25af6dc5bb6e002f30ab0cf7f8cd939528fa4780256abe3f9f38f4419", - "id": "af232d8be6ed5cceba703d618a040d13db05286235b2610de8c4cda5668ebe8a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3324317837586, - "fee": 0, - "recipientId": "ALTNeLaT2mvuvMohvSjg8FjYhVDNxbeMEu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009f8ac3837eb8b6363c0ec7788dbaff01a07d9cf68cccad905a67a8016ebd24ca02207acf56e23a7485fffa201ebbe3c515b99bb4121170da3b0884193416881a23fc", - "id": "577010da36ec3f0cca45de28e26aeef46a3bb7fd932b755609aac454e9626fef", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3332461324329, - "fee": 0, - "recipientId": "ARLUcoV2hyhKCzfy25Lk8Y9VWix6deVAYj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220724da3de0142e6139d36004f68e5ab4eefe84b786c5c80d0374b319c3be8d6f402207276502244f50eae18d9d8cc7ef1636936809fa9f6d72461a5bbe91db9c8c04f", - "id": "65306f6058181b91b964039ad1c18f711fbe7b7d9f318d33a02bc1acdc7618c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3333461562845, - "fee": 0, - "recipientId": "AHFojRfYcbsAhEqvKY8MXMsAc8MEkxwZJU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220179bbffea812db28bea6ddc3e3863b6798af30cf9b2789d5f827201fbca974d302200ded60e75ebf3d9eb1f947bf5dfb216e8206a4882eacdeb7269c728fe47c0bfd", - "id": "705f8aacb1c7cca2391a4d432053872f3dc302d9ec36f1f399377e1b10ab1347", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3337241797612, - "fee": 0, - "recipientId": "AHRETqnSboGYrrjQ87Bnu5ovyKZBvfuS35", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009eefca56858fd766265713c444958cccb5d615b012747c011c6f5f355955c60402207cab37ed16c6f328ead3ef60c35257cfeff84c0e36182125822142f2f4a3c471", - "id": "4f16617510a64b071be9c9aeeda35eea4947d52625610d7c8af2d47dc144632e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3341104284118, - "fee": 0, - "recipientId": "AXNH4NZ1wsrkZ1PPFfSQFucDGwt4sfsGor", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e443d4e4a9d3f91f9fddccb71e70555b5376d5928da292c4466f0fb4bfa7f12c02202b083a62b3e4d139c3bbc09c162259ea43c256fb0cd25870b724cddb4d7b074a", - "id": "7664dfadec3af8d62814ad0baf545d16c8aca1be546758a259a05a2115837cb8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3359667184998, - "fee": 0, - "recipientId": "AXDfDrajcMoF4ssBKAj72AefFrS32A6ErV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eee29af59dce2e725f1441b665b737c9bc22090bd4b37a68cb79c29e8a041a28022038b10296b17315ce07bfcecd521104a62a7c1598d07ef17fc397aebfebb094ba", - "id": "388dfda809f3802e4f7ca49ae7da2409e195be0b231c037c0c0ee139227b5e74", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3411370798936, - "fee": 0, - "recipientId": "AMg61zEWGdPqgRcebwuY3nABTXcEf5ursg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ac107612d5b3790a6640e52d8d5cb277f32c7ce79f3d9881641b8e7b613213010220353d4bd4188fabe59e5d8ac78d4135b57faebcd5e8095c1c7ab8d9c1cdbb6b41", - "id": "a16f6f3d94d84c9e83cf9ab117cc17257632d3b7548fe779f21bd02e0cf3db90", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3431641346204, - "fee": 0, - "recipientId": "AV4uwLo1v4g78ZWWEfZWt3zssgjS3HUUK2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c6ca22e986389d94fa3da2973a1bcea26492e76c8fc8ecc47d2f754ee21e0e65022031cb3ffe69ddd0d63c0c40b089fd445748c9b0527e71d71970f2c1e987670320", - "id": "71ae8506d64400ce1c01558b7dbf6eadb9b915b8576f8024ba54929f56273d8d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3472495650202, - "fee": 0, - "recipientId": "AREUm684gmTkURsYWAb5XFSWjtUpnkHXM1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a6977f3021aaa15636b427ecff64771bfe698cac3e6ab1fcf1113e927656b44d02203bdb670754598eb986329abbf9afa637afced4dd83415f117ffda3a40d7c2da1", - "id": "e03a176681d276f4c1a44b84923d8ae4458333954e8917c67cea9b412d743ceb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3485679678910, - "fee": 0, - "recipientId": "AeUU182Jnq996yw8TZRaDAPj4khgpxBjKY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022006a3492b8a039c733c6860409ad2b6639d288f4638863a10955502162aecfedd02205035b3a64ac33b34cc81b07bdec6d74dfd14b28639df434b97cb55c1eb15a9f0", - "id": "aa9f40571467e5776f3ff8378598356aa77cccd4ae6d20b9911472393a3aaa23", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3485679678910, - "fee": 0, - "recipientId": "AesYuSHWM25csWzyJE5aiXD6yL4Vb6G1Vw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c4de585a2f48a005b07f3cce3a97ca0ad829250496930b3bd2410b0740b8ae2d02205f46e8dcb4cae8879992e1352a3bfa5c947706c36bb8d288516169b536f3299d", - "id": "0116ede2fa14adc70cf6286d703f9dac6d7d29d2121b62a382a197f2e0c9bcc4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3528361371925, - "fee": 0, - "recipientId": "AUxFccM7Y1rX9rgzi7ashGogxk3Am2A5px", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220750199ce472faa98d3eee8da3efcc0e4a381473aa1d48ace4731d7dffa78fa1c02203c91c647378029ce9944d5b647776989c67c7b3e153e7f3af0f33e709c9d0954", - "id": "8911fe01cd1cc981963d244e82226027890ff383183acadde7023091ff3ba196", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3531301673068, - "fee": 0, - "recipientId": "AeeJBq15fTimUFojH6XpR4FUAZmDubP43u", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100933a2422b0d1ce2180e046a6071652758838311737a3424a23ace27474eefba602203e72e72184db462d5af7d862d1d2d20b8eda36d9d5df6cd9cd308b6868a7604e", - "id": "62f6c02daf915e73a4440f65838b9bdc7cd4af1cd3361d7e3ece2755643a753c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3535124064554, - "fee": 0, - "recipientId": "ALfWJY9kKj9UQkPG9WpmaD6ty1xzy19MH5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022056e64e74a7762a00139bc688a97421ff9ef58d1052019cd594855abb279de55d022075b33e73eed827a243c96a46f97bb3ad1c3998407d84a8d557f61f3e93fb08e1", - "id": "785e5330cca5c0a53d97c3746427ad1dd4e72d75e64567ebad677d00bcb3b8b4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3556280867780, - "fee": 0, - "recipientId": "AccgnHy72MjgSSByZcWQqmeRgMstvJcuYb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022063057051b56093a07affaed97bdc4c80fcbf7e3e9f1ca81d68347176e39ab60b022054765fcde969a830d58d67dc80f1e2610ced4e6275d3c261b44542add4ff74a2", - "id": "fe4ec15b065062d5662f9d352d1e1885ff2f907f2ba0c6a1528e98bebc2bc32d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3557938075605, - "fee": 0, - "recipientId": "AVEz9XVtNWEBn7DKziwGAYRFUcaXetU2y7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220603807fb9a4ae361dc3743551f4cec9f78bfc9a5e6bfd00a7576196f8970a1230220586ce37ff801f638feae33e0449cf80c4a389617fbae8712a50e03e45b6e11ce", - "id": "f0fff2d878b1fca856116cebb41cae5a5cc0aff75d6b1c986e302722a81feadd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3593815899623, - "fee": 0, - "recipientId": "AdWUn8FcTbrtck26543q1oGyaSjZFWu6no", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022045025274c42fbda104625bf2cf9320a01045aee16cacc809074ee6d315cc17f7022036b821e1e26e19cf0a34bb6ba82a3d1e5c164990f1257119677f81e019bb231e", - "id": "e2ecfc0a1f1e5e47ef99d7de7963e51d2af78f5a3671859487a835447cdf9353", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3599965360713, - "fee": 0, - "recipientId": "AGYEkwK8grFmYGrrQkNSsEbQTRxSv1QGBH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d44a62be1cd41baff04021452b670b11fa4466a50b19624e918c56950c0326d302204734db4e45f8c2fb42f41fc3488cafea5187c051a4b5c21820b864e46336cda5", - "id": "f851283dfdb3e819303fe22d6accb80c935a06112ee8ccd6da917eeda8d9e2d0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3637230969298, - "fee": 0, - "recipientId": "AXtwLLdXbcAhX9j7YadfNoAbEAwmzUDGe3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202d65e277703ac1505ec5becca2491ae4cc63b47af9e3c9bfd4610601b14e17dd0220659e5031db6fe356fd2297cff629fe3a67f153f05fecc76a37d12ec81c7f77c9", - "id": "737a7570bd435e95df00dd9aea0d8dd9ed6a2d6715919814d240104a31cc29d8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3637230969298, - "fee": 0, - "recipientId": "AFtvTj3CU7Vr2C3bDwNCDXDpbqE5TAR7UD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008d5661fd7a233b744682e4df941f880b2a499996700246100d94b196db0297e102202c1086cb609d5d24d960307f3adf858268cfa85608ac887b6579f48fdeb2efae", - "id": "8c2a012cf5e9de46fc73d5cf899a51508af66e88951d8d17d9bfeea7b53cbd7e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3675376429087, - "fee": 0, - "recipientId": "AJqdLZvhCLntP4asFsVa7BfxAzvm5vL1TD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c69c239352a791354a5be8627cfbe1c5d266c11bd6113db1d7a156f33ae5f819022053aaa645c823507cc6bab83b87fda3f3e66bab8e69b4ca2ff29928be6a7b7a16", - "id": "5b19f5b4f515679b9e1dc6a3624a2dd740595f48dd236076fee85848ed65dfa7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3675376429087, - "fee": 0, - "recipientId": "AKrejHN9mQwjGALapuuf38wjs5TTDZGYPD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022058f35b0134f4ddca74ab3e3d3494946276c15748ac629b4949c1132bbb754dfd02204d6efde7d964d9f74327077b2f693642b5652fa5dab7de8da16aa5a587c04c1c", - "id": "6388b60ea1d6fca35d97d365f06309d93e211362aecf97d0bf4771dbe192006e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3675376429087, - "fee": 0, - "recipientId": "AXH4SRRS9WQqYW6tr32eDScenDgfmZvn6z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022078cc3fc5584871fc50c9df131c162cae857088ed96cdaad082aeb2bf73a7237a02202175cbca5a39833cedc9c394e0c896b5eb5c4369748e021b588af40539c80e1b", - "id": "e64991d57370d9e29549f930e3c72ba1d2963fea228c69d07da985f20889992d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3675376429089, - "fee": 0, - "recipientId": "ARuZqMpJcTxfyqs9FYwN5o4vHrH9ofLvnm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207c4cd8d49f9a5babd6d545162f31da577ec194b1783c1cb7a518ea922c589b78022053d81b6610d827dd8e4ee4be48d6ad9f397c28711d8225981027ac283aea127f", - "id": "bbd93094994a8367c48099c29dc4619dcc5da73cf63caa53b1e529395b6b9a6a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3697295037611, - "fee": 0, - "recipientId": "AZE3bGd41tQLJqtP7kmn1SUQLe4ULUpNtK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b7153b2711d4d93933b0d9de3fc66ce2d459464e57fe7c6d502d8a0ca6c8dd3b022027915527227c59c301929b18c2f04b65d58d6dac510d9f9720689fa3bf8bd180", - "id": "c8cf09babe8f3fdfc44ba66a538154418bcb3c3b4c960ddea6d710e893bd4337", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3706851016327, - "fee": 0, - "recipientId": "AXMnw2SyrKVtEpkos4pYLqLyKS6nzBjWL8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207b5080ed8fa34486c079d94794804e2bb8fafab6c643507a322fb8f1266d4f9702207d80e5d261ec51e6c371b2fa59926163828d91856d8c0b482bd23a03a991e32b", - "id": "8dc1c97b5fd6cc3002868749b97114243ba2a950e9b2beff032840548a875f13", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3709028616718, - "fee": 0, - "recipientId": "AWjDzWr3whEepeRDHMcLSZzfrYykBc2e2V", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e660c72942d1e4722f23166e5a781f5ec8ff9c264e6841695d9799934a9e859b02201a2e7e073c30e1efdf4b1fd17d3ef33914276e7931cde622c42dd3a90dc3f7e7", - "id": "4e83f8b8b7acfea0f43dfba0f8cdf8a8809eec7717d4519f8ba762e7328bd338", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3822391486252, - "fee": 0, - "recipientId": "ASf6GkdyPRoxAQ9e6wNu1eTcGzdCxTwU4F", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008980846f5d043bab9bc69d533094c47209a08b6604052b9097e97d3fb884828002203dd82abb96ec6873149fc3a1f274b04792c30dbeef84d15d274432f91b53ebc1", - "id": "b25ef9750e5e136d27cd99248823856666cb496d15c45af37ef77850791157ae", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3841576429087, - "fee": 0, - "recipientId": "AdJqCvp1xnvHCJKRBYDm6Pps7Yn1Ly4gnJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ee2c9b676bb97f604b5cddff9c35cd2995b9ed514d71eec031f606fe249cdd5902203cdadf32092513c27107651f170ec3209e52e2fdbf5d19fbc7d6cc222ae367eb", - "id": "4069a73c1653a4dd78863adc651caf01c46b1ce2c603656fabc4bef4e15cc243", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3912040733393, - "fee": 0, - "recipientId": "AR4SrANMGxGvnQNzBtKo4PMiW6gpfnWvrS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ac2967b732aa360a24d7f5562b138970d165ff8f5bd6c9e8aa8e45e69ed94b0a0220360bce61f2bbedfb4fb7f12ca83b1d9cc803a1e39512b1fc5fe5667f12ac740b", - "id": "2eedf249fe84cfd6b31f3090424050ae839909a12a3c1e39c777bc2e98897b7a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3918939514059, - "fee": 0, - "recipientId": "APwMLxdB8EHM5XXKG3gfT76Z939dLz1JCz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d3efefdd5f23c7d432cfd10d2519ba3dec0cb225c4df2cac9435db8a71646dbb02202e46cc5d7721e3dd8ea325ca859b01899359a3a36657fba4c93f202ca58ffa89", - "id": "d51629cab0261f943ae8bf7c72d8f33220cfdd1cda45a5f4fa5407919e9af420", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3937230969298, - "fee": 0, - "recipientId": "AU412AFph2KBNp1ZUNbMr6UQP9o5NSf55s", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d38cb807e50f55665d84172ae9f51ad7496e6afbf2128f7d79ff6b245b2dd3bd0220315d0693ad470da6c87599ccaa083645aae0bff6f5ecf9f6c540f2b333e0dae3", - "id": "04de02148852008a6b34df8e5c3ec16c21d4d84acc7176ac5a4f828bc14e16c2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3965251953261, - "fee": 0, - "recipientId": "AGQ6aRNRA165aF71WK26Wd7WBn26d928dC", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100996ac1644a8e8fa34351933824de6c784aec3656fecf58b00ecc73058100037202205b82dd95182ecc1b5933392cb43106c56a93faad5da958145e55cb066d055b66", - "id": "00887bc4d7329a1cd596625f9befcc07758c39b425cbc173f70338a62455f8dd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3970035705567, - "fee": 0, - "recipientId": "AcvUN694h6sjffQYucMLacGpPuq1ZAupLp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fb1f077325d32c64f6047e8bcc8c4110e079af9fdc76c171d1f8a0abfb32c43a022063f5bd3195cd0985a836b925f56cec34f3331d1fa0a9f0c0243583b7f785ba01", - "id": "db7c86a83a6e9fba3775696e6d06a5051cbdb20b329cd9877358d8e5bf3e1545", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3991462828751, - "fee": 0, - "recipientId": "Aes2M6fkCxfwRyguG6R7K234a8aB3SiD93", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022059bcfb711cd879f837a28c9ab64e342750b5a3bcda836c1aa232ee472004eab50220609448b1560201f56d821c2bfc180e08de802a8349b0f132c111cc5ee7d0e6da", - "id": "c797c9e6646543db243df8ca6f595651745b1262062762393342dced20eaea82", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4000000000000, - "fee": 0, - "recipientId": "AcomwPvjAGZoBiCx3u4e7pnHaXxeLnn4MD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022024236bc5bde500949d5031c98e8b36f823e00fa915cf01773baad25e20eb77b802201dedd50e48b684a7934b341f4e961bcb703cbdaadf3047bc3a63179992c80367", - "id": "65636f924d10c01ba4289d064623dbc2bbb19b7e6f5da944635edc38442aafd6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4001132109901, - "fee": 0, - "recipientId": "Ac7qjfgoPxS7CvKMsGgPY9qPjy8MRsxtFA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f4622b4a0e92ef2b2e120d951f547a4bd22afeb383700cf21f66cafda92d752702203d815814d38b4885ecd59e747d6fc0131f3aca76ad6b8f795b8d7d05a13fd8e6", - "id": "4b66a47cd8eccc4247e661526a5010aef5156eeda176cf8e0b1471be657790e7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4015989936987, - "fee": 0, - "recipientId": "AaHvdU5zBUEkqUebphZdJuRQx4K7QxGBc5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f1843d7b8f388f8fc2130ac2f5b5721b98e6e1905974cee0e5bee71f7bb5359002204ee81e364ba06b6ae5fdb9ff4c3772291dabe596664d64cd8fdc7f9041386b9c", - "id": "cf6bac6a58bd66f9820e8b92a2b7a093d3c874d652f81cf3956c11d785133f82", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4042914071996, - "fee": 0, - "recipientId": "ATBpKuXuUDFEo3uxRDJpZYCYvAuRxedMQP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022036b9cc8a055c30695316e968334a488c1daaeb796f189549ca5566ca677d4be102205df0433a63bc80d39594ec5aada62412a8776468c5d8996d7c670669f0819070", - "id": "356ddf4e0040d24014d624050eea27429e4ee5002b07977cdf08878e93397531", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4048316026508, - "fee": 0, - "recipientId": "AWd7KCkz7PAP9K8noozmqggx1qdWNHxoa1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022064ae748e8002a9118a3e4bb16d56dc25a8aa37260cfad3b5ff30bb0fda6031b902204fa1d3f9faece8ab66054c47647ad8a863759f0ce3c3c36edcc1e33162e6bf80", - "id": "d37818308a718d3d8ef3c3f4364a10b354b63af68840195c436dbba2fa18e344", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4131665330066, - "fee": 0, - "recipientId": "AMuFbxddTGnj41RK9QRCVymbSbdkxrJBoK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e2de836477641275c567bd1b574bd277216d355bbaef3574821b70ee55affda02200e01aa3604454ef602992243324c66746e0ad04b91dd65f0275bc349f4329faf", - "id": "d9147524e74c26eaca62684a3a6a1876cfe97e71cbb85b95dc5490dcb1ba094d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4131706645547, - "fee": 0, - "recipientId": "AZ9hZpKCaaJ8c5GyjvNqWVxKoiHj8fUkxj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220618744db99463616257c71774ccbffbad439cf0cb6283b556a8a7fd98e54da2502206456d52a8c767b60e5de2f3280f852ac049b66dcec4f9e389b8e801ce00aec07", - "id": "265d72b2909c0d5500b9dcd4346f030c32be27ee393d755a4dd017cc66094e4e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4153588271639, - "fee": 0, - "recipientId": "AK9RL2q4zGWGbLtLViUHHdrzGbY19JyVWp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6adccb21f415791105482edc5d6a640964148330fa534d8f7e2e80f24b4fcd70220246d7278626b4b645018240af6d11d558a086e9baa7be5f4c02e47cfcd28043d", - "id": "be1d5b2d28236cb2e1551a702bb633e003582007cef416286b54553d1b09f13a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4209041086592, - "fee": 0, - "recipientId": "AQqyqd4wPnAceW8SNg2Jwq7kw7oJdbu8jX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ad4d36816a2710a030966ae3dfd7964a36e3d6600877bc12a5787877117b160802207c3bcb1abf4acadc60182ba7c5f242cac9276382fdfe31f8cc5c1d34128497e6", - "id": "0657d38310031c46d2207d04760de4dd9768f0f88643c189f234cd0f09fcf33a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4241270774064, - "fee": 0, - "recipientId": "AQ77PDtC953vqib9FNM1QxgHS4c8hTKbY4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202152e05672aaffa4dd6728794f48c789449778ffee43732fd04a7daf0ea10fa502203d3b1e6d34399f84c96363adbe3e486c4b766e402387b1c88097794f5d681f47", - "id": "faf38dc7c24d054c7b542ca52119cd5325bfa8e48ca24488e259594c72c4b6a9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4335474035753, - "fee": 0, - "recipientId": "ARNrAn3AagtTUXwwPgfNMkG42om4y4tjXp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049deea24c707fdc7650d34b049bf69911105a2591b93789cef09ccee23386f1a0220693f7fc8a1bf4ed2c42fd89dff05b49ba16efa2b607dc040c99fee81fd94fa8c", - "id": "4321cc2b30fb9c56a2d3dc0e68cb711fb3706c2bdea33bf06f8d1b532bb15ba7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4362372912987, - "fee": 0, - "recipientId": "ARqijV9qpJv793nYJpSuzhJJtgGt7F5vMh", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220330da9b78b7aa2553cc5b832e4780c160d6c2c9177a2c07240f7ec4aac39f31b02201b6a1e5f6d7003d1d2428eb6d89c1b4dd896a083fc37e136bb7a030322027d2d", - "id": "ef30241456071ab5739cbc01b77c888de52548ce718d290e17ed78dad3ce9de8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4395705996499, - "fee": 0, - "recipientId": "AMZueSSbjv5eBjytCubTZy8gkkFov5GQJb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220201fa8fdb645e5a6d8867dceaac2c15a03323f7df042a1c32d2cc0500451dc9102202980c35218796eabfe7755d019448caca47b7892d756548585cc2f10803ff878", - "id": "b12f8bef995ddc79c9ab8b62294b59ca171e59af53daf5b45173758fef8314b9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4410451714906, - "fee": 0, - "recipientId": "AeeTLvBUBeVaWNW6WU4dQ5RaM1XkFvAz7Z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e82506b38a0e8e95f0bba2735b71ea2d00ec3bdb4a1e11519960933acb202f2402207e93347b0fdc6263d6a0725cafb8a15cf6fd82dca3033a5a9520408b37dfd3e0", - "id": "0f41c6cab1711f15519c238ad3913e0f99f45604f01181bf312450fe142b42f3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4410451714909, - "fee": 0, - "recipientId": "AeVsSShGJR7KWQg1DHKEqev1PDSDREiDfy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a53783236e53c4d54ae16e2172b9152cb44d78194a72a404849ea9d36e783d39022030ea460212b5b2ad93542eb7e27806e6b6215f9d005863297e9a55814071bbcd", - "id": "41666cc004f1e16061027115720b89eb3fded76b0032c3db25e90178fa158ace", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4411611467971, - "fee": 0, - "recipientId": "ASp6DK4Fcrji5LHRFCwx17YPG9wdnXUnq9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220456e45ce3c5fc8ac7ff423250d7ba6248b29edce819edf5e1ff2fad55702e70102203f1a313fe9dd3705f217ca2a2515cc7e26e316ab7bd4228dce7e600a54f2d98f", - "id": "0515f9a1e521fc8e692c197a0467ab01b300ffc3a4780bbbd5faef682ce9514c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4415891272021, - "fee": 0, - "recipientId": "ANR1PwuJc4o8Wp2qpc6uUk6wWjX5QRhX47", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cf11b29ca14aca61107d25fac12d9a994c965392dc0ba3e1177c9f91559132ba0220266d7dd8d55b8eabe70e95190c53e9cb606c82b3d4c4f4b578afa933873f6b20", - "id": "ccf658c1b13a22aca678f05fff271fb533459d73fe00e361d1b1b96b64fe2d43", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4507507351910, - "fee": 0, - "recipientId": "AVF5GuU4tJ9qd6YewAzDbN4sZkzFDNFgUi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ef1c683d99d3c0228c8af10864c6981991f0cd493262303514d907e1a2dbe46802200c087f8aa3e4f2aff028e14664e5760873d753d7f846eb0fe1e9e30fb885eb13", - "id": "450de1b4d45c627fb948d1badd4cc8a70c89f3436fa5e745b795545b1ef06927", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4531651714906, - "fee": 0, - "recipientId": "AcKGYwT1xwcWveaQE3dBqqqqTT3nnfpKbg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c71f196cf735ce57f667d19e98ca7296c9dddf84bfa634318315edae96a67ba5022062cb8e1fec6e86e0f79e50b9b0c52a943a772fda7f5aba0cac56eca7cbe7066a", - "id": "d679cde12bd6e8ee711e0979e8968a1b97d6ceb9fe19634835355733b0a012ea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4535436795104, - "fee": 0, - "recipientId": "ANPpeB8JWX4gfskbcev8a6RYXZSSaMs3pv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b86b575d515ed5ba323eb805882031a307b11d63c65249dbb52d20bac8f41f6702201ecf99a70e608349febe6ff6d33da93336357df5f9255757e9096d1bd1ab0743", - "id": "d496570ca0432b0f7d71fa40c94fbf7f9388cf27d7bfd1c18617e70c96546be5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4641265354653, - "fee": 0, - "recipientId": "AXyPubrCRRig234zKtDMzzSVrPKoTR2vNt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220312e3a6206e914414d40c7011162de5061089520ed5dc02ac0f88e20ec1a52e2022069011ece4aa149869d180c118870995de0280b5343791f0152705094f088cc4d", - "id": "afc269e369f0a701457aad91613679e5151e0457bdaa3ca386359882cb3ece67", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4680692119983, - "fee": 0, - "recipientId": "ASZgEyR6XZQ9RXFoUtuyXY7gqHbC64vkU2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203c43b599138e87457b214b1e3d1c1e83bb699d06a01b01e6add07df59b4c5b42022079f8d76bcb6bec406e86a5acb5cf2e926af71349801d6ee20ba89287c432dfc1", - "id": "121fc9d4fc8e2cc2f95508394b6eb20ce3593cf6c297fd4e0cc7d48d852719cf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4691051871764, - "fee": 0, - "recipientId": "AGa6Frh3DWJMmAsd3Nkn4bGt7n6bFwG8qw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100808d7e940940c758d49ef8fb12660f15efa0c0cf5ccd391805a490ef877322da022005388d608bf563be295af2d05a993b2b1a87c93ece23ffa0f29c1bb412104b85", - "id": "0ac956b61b69b38a04280abe2c372042dc63a8267b67c4a6f23b7870f88a978e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4697862065215, - "fee": 0, - "recipientId": "ANQkpXR4MwEdPpgbEdi3BaxF3qQMTGo92w", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220522fa6db9ad2b0d1a899e533b97002774bc90d96495b2493c200eb777a52da32022049690e22275a24280cbc7df79b99c92731c8b9b95261a07ab01e02c295f2ebaa", - "id": "fc6e5363cc3ebcb775be0446dbe8add45d7642f58ab984cba460ab5d966116be", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4807392369248, - "fee": 0, - "recipientId": "AXYbSuKBYev8pU4TK5GkcwxWXWsqFL1Vvq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ad8d8f2a9ecbd9f954853681c27b5a46ac43b0db909544e6b23763932aad2f4022075e36c1e9e5260f5099f0fef9748071fb3e3202c82b0f685f6e56497c0ed929e", - "id": "a0b9708d51039b38e4285f869cc4ee2e29f620f84837bf3147d09e7dbd91295b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4900011855261, - "fee": 0, - "recipientId": "ALZLdzs2XH7Ma87nS6qnGBvipdXCwXEcom", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022055e239d43d618d29581141d520ea39e940a627d113d417d39b5e8600965ea1fa022013dabd576ca157cf2aacb55588570d6abefa793db90617f9b52b5f8bdc5c0bb9", - "id": "bde6c2c3a29e7d20ef59a478faba012f38555663d3468c12748c1d0d5e70e292", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4972512330701, - "fee": 0, - "recipientId": "ALT8t2gVWLdEgc516c1QTXbajnsDxTvrMS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c4bed4d0ee6867797c4c8e3c3a15d169602302dff9d0f60a683881ae0a2cad7b022067e97a1f63f524e3037d8f29471b30007579fc372551e0c8e02f2924d8f5cce2", - "id": "c2f507727122bd6a0d0b78f12a8dcf1169d9f16187877e64bc24daad9679e0d9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 4977195555049, - "fee": 0, - "recipientId": "AeNTL1HCB6ZBvKLHVMpHnGqVksTDktSCfx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a0a35c51f045c7b5020d616cedf80411457f899f160d1edc5a326f827b886e4e022009084b2462322f577fb92d07b220eb0d5f2c17e52838faa7ef0dd9a0913a01a5", - "id": "c0d6bb7397e069bf8fa73f3227860c1dc2f262ebb274d8ac79bc70ee5e6c5129", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5000000000000, - "fee": 0, - "recipientId": "APBJH5q5UPjfekwkaMemUzo32WsAGPLgC7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220668c6951f67ebbd0aad2929ce9b00210a7d9f669fdcc1de95d713264a0630d420220630ba108e8db883d532c16fa21901ff3fd2a150bb6473d083a859416164fc862", - "id": "595117b36fc9609f6012d0afb10f3c072301bde2b65ee67d4bd5e00b9ea5b456", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5086720977858, - "fee": 0, - "recipientId": "APiXiBgZ5Vk47zL7r6nXbb1feEcJw37a3Z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220532e792b6107cc509119c9761433c93ec9ffda5cc5f4ebde8b0add5b84d9960b02200ef235d5bfb86c9083ba1917160fe33ec880f547845b80c456f4a2cd2ebdd380", - "id": "2e02e34a648a1136e0237953a75deb9dc336bfa2851b764d3b419dea851b3688", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5145527000724, - "fee": 0, - "recipientId": "AQvzCAZgwfEerwR8Wbi2jtLRZe1xckgwnz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022046c8c7d959a7e4a01250674d44fcfae8d2b1a1d1beb302061f98fb3baecbcca30220795103528d96a6b86575d359c68af22b864bc6e1c412e4501bbf80d55690164e", - "id": "29e47540c5c0663a2a8612e77717256275a82a29084a03ec6b0fdc940ca3b792", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5145527000724, - "fee": 0, - "recipientId": "AUNQekNveCH4o9EGHfuMAc45P46vNAkJWQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210096a8e326a4c0ae121e4ff4428d3eaf00682d0894367cf354ecd68ac2063fd14b02200f21db687cd6b747dc4da81857fe7decae1dbd7d1e1570c8e609a27b6008d17d", - "id": "fc43db98ac4c5b9f1b3687e91da1f922a1328e4a33b3d5fead709911acf6c810", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5152228598785, - "fee": 0, - "recipientId": "AbFfKLj8cbZCpD1dFcnx9M1quKbGyvY72S", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200108edd763dc24457372d2be73c2e0bf08c551e81646221bd02c298222bd3f6d0220304c7a242cc0b1c97956e7ffde176e16c89bf6bc024ad2f85edf0b531686b235", - "id": "ca1eaa9b36e044f32ce3d08b3d2a25bf6fa71d52efaddff1fa1712eeadf76413", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5165668063555, - "fee": 0, - "recipientId": "ASxsKmBbAojE934PbqhJ1mhXXpkdrEMGi2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220218d87057621c85e71f76523b123fab334c76307c5ac2030c572ded289d211b702206a80d562a887ad92a6ff0f5ce69202578f382e55a79665526ae3a38af5908f46", - "id": "fabd7438e73fef81feade0e11ed6da77e2eb6e0a4962b9f945cd6288139c37e0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5229284067939, - "fee": 0, - "recipientId": "AV5Ap4mdBEJKpbfUZQYZWxx6Q66yJv25ne", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022039474147574e5fd182277aaab990a51c8254d41ac8d8740b646dc26c2877a2d6022033eaf619263d1b0e415c5badc403f76a4c9851d2de87c35f7ea0c63d35840a77", - "id": "e03816fb6db7a11b0bd2ade86f9a85765e3ab7a94e2906b6c0e30e7d8e0b5fba", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5292542057888, - "fee": 0, - "recipientId": "Abos1R3ZsDqgNa3T62XQ2gsyE9ogYs6fPi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ea11ad93fe457b16aa68e3b89751a8556aecc737d0a5472695c8731beed5821c02206fce4f51c29956e7c996ce0b9bfb75702c90983b4a8a59a3fa3dc75c06a20ef2", - "id": "05ec9e7cdfa7944660f3468045a013182795f23b066e02d0706be47cc49fa88a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5308261029410, - "fee": 0, - "recipientId": "AGqBNsAT7ndLy25CcGrsaRCrXRzwhR3gLY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b2253589018b40a1f81e3a216adf75f1b6498cb9761023f6bbc0f120c21ff20f022042596e6765c285aa0905e8d173880704cb843b1ebf97de0e92ac285d972bc2a4", - "id": "c0ac0b134286a830d4f2a84e8e391215336a5f1e46ef05ce9a1b7f47da0729d0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5312153866513, - "fee": 0, - "recipientId": "ANZCH2mAMCUAA9TJByBgt1R9PZp8j2dnsX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210092551cd132785e29add9309d136a6883834349f2efa084138f259c7b500dc5f102203a39a5ef2dc305e947758bfa72d6d5ebaa3afa3e36cff9b17f3aaaeb0079cb58", - "id": "c646cc3909e6032cd9dbd8df7d5827abfecda82eadc0155d7f2667f33e37207c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5357843265955, - "fee": 0, - "recipientId": "AQJwwgFQ4F8NxNUqn44d8Kx6rB8AmJFiBN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210090279d7a8f908e661b3651cddb1f1690868b9906c0744724356747ec26e1d4db02200942dbfcdd9e72db4cf2ca3fb4d20b6b94133a781fa221ebe47d964e8c03f977", - "id": "aa45b2bfbff8221f7b5809e7ec6947f39e57754d1866a25d2b855b8b54080ae4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5399929874642, - "fee": 0, - "recipientId": "AeRzn9WbFJkeExpJ1ib9bcqPFGBz6fYha6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202c2146192848bd5fe02d0968ed6629dc03b5531b945d8e7fd344902c46074a5c0220677a6ade625198cfc73d9e60d9735841111dc5fce252a8ceea6ef5aeea325337", - "id": "d8396e0dc6eff0baaf523bdb9ec0a0f683ea01b2e7b481edab37f679959c72e7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5452743873172, - "fee": 0, - "recipientId": "AK3xPfpuLgDxvcUgvHE8G7nzgJ9TikonCM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dddf3ac76223dc2e41a5bf9c19b0e4e448e2f90c1e9970c97c4642c13892f68202203e5e2ad64cc817c835b951212e7a65b09e565f47707f030ae39b770694fd6f6f", - "id": "96fa04274bbbb82e73be6c4baff991523c86cb022cc8f4b0ca267e8f2b5fcd29", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5454583384732, - "fee": 0, - "recipientId": "APrqARoKAGvFvSd2ipxmhWt8LMNoJPxHDX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205017cfe8817f69a0383d53361483393f92c9275face9004b127e7d3ee4730ecd02201259d5100eb0ed196778b34bdef3e90a59b34dc0d61949d56890c8c1c994e400", - "id": "12514530353f15e198fd07bf42693c050c1bbf92f1094b9060ec2a940f1bae19", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5534027654283, - "fee": 0, - "recipientId": "AURkidZfCN8uin9FX8ByFwCpCpqAs37uyK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220752c29c7102b3d0967a2a369eb8ee0f8fb09bff978cc00502069626526a6b7150220351f29c9591b208740088abc640391b793dc7fdaa56a084b1a12bde9a3ca8ae6", - "id": "d39878fa44b4eacd96c1dde902d6ac74748525845fde03ae3bd74bcf99f0e14c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5596708324890, - "fee": 0, - "recipientId": "AW8dQKTqagJnXcRfDyYXiPBJgtY82sjSoG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100be71df263fcfb94fea34c0ade0751d3f7b3fd25406d995555f2692179b1100ff022000c3c93736cd0bb38fb59f5a4336dae6486c26f643c8cb8fdbff1b06d8f7bf5e", - "id": "5e016277b51da7b156b41d5790536d3f643297f005202b5e504d4ea588451d31", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5613302182608, - "fee": 0, - "recipientId": "AYoqmnDvAruDtVpKpmJY5XGHVsdtAsfYxu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220171cc8f28be49906b2b55123b0b5e1dba8a0a5be228aaf517c3b5f03d1202d2d0220559bab26591db1c5348b69ec6b801f607ec8ba8b84030759b6edb6827d3ac0cb", - "id": "9c1b2c56282b48749139ac851a3a991ef9e4ae3926e600cff88a4182cf9b3e19", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5623070875236, - "fee": 0, - "recipientId": "AGtJTLKoLnx2oVgTpfi9briAHR7SrT97PV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210080d87159f97654d34b9cd2ccb64b5c3b44824405bf464462184bff3cce93d49c02202e72887f5a73adfc0101cd5e4f0bd019ac66e52c029ed1790890389395c7b16e", - "id": "66ee1e6db3ff2989dea803fa06d2cd01981d1833a98b960c79295b63c653d3dc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5648505606296, - "fee": 0, - "recipientId": "AFsy8pLNJnnq1R36WeJmQNrRygxPZ4TBv7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210087518cb76baf699d909ef78187be4e5918f47d90bcc88b64612201fef8d672b30220668295a8f17296c8992a8dce97ccafb840b1a939660c7793f76c55ba4d5074f8", - "id": "1eda98af243f8de2c3cfe8cfd2b2de565e5e5b8af91998b716edea0da85607c0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5648906556452, - "fee": 0, - "recipientId": "ASjpXv62BcY7wLgJLkos781E8HhDjfPZ4c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206f1c839e8b95af5b74909a9febbabc652f35bb9cc6afed4dfb1294fa3a57cabb02200a3e9a2ff90d7d72808c5da906d873984a07576d593343a520279d5d81053904", - "id": "450f9e10debe655d9786b3e3a181b79380207e54a5460e21d27c64cdfe48bd6b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5667502986102, - "fee": 0, - "recipientId": "AMMB8GyfXAnSjbEQap9UV1KxfiQD4SFhnZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022041f56e22efaa29c67f715270a3ccf5f1fefd2b2c9a3a3473afdaac2731542df802200d42682d5a7f7d578f3d8065395662cca264afcf6c8941d558a45caf3528380d", - "id": "3eb6c19696d81659c7bbd979cd9d926a6473f86263b262aeb0ee10ea6c71a57c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5680013606036, - "fee": 0, - "recipientId": "AVgqPjPJ4RjFpw2bqmMkvHGHhTUnvrT7Sm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022030881e7cfb479dfdfceccfabb2acc03ffe03ad5e515c79fee770be69e107d75e022071b7098cb80f71d6a26cc8aa3b62df4d1dc56ab67cb86541c3a217ec9737554a", - "id": "9571f17bd17068f34b02419e07be1c2e1a2984ead5800a4b666b38ca8b235816", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5880602286541, - "fee": 0, - "recipientId": "AMjo1KKaWVXTKxgY8f7h95pBGi2wjX9kuW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022077e2def23f7441b5eebf718fcd350c6a5af8207e01f549932dc78c00eea12f2c02202697c2c7aa53ce55a12e88f4d28a9a92afa6d2e0b03da2c81de9cbeea5945c6b", - "id": "1f79270181b53adc2a6d320fdda137a3a2b0a5d76ebfa29623b7399f1bcd1cfc", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 5893686626629, - "fee": 0, - "recipientId": "AGWQjb8AZSSgin8H2HHHHcMNoA4iS1HweV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205097acaa5ada5c48df6a182a101fd1ad2005a521ed387bda6a3cf48a93951796022001c1d933f2d00539a616151571e013e10a22c25804ca301ddc65652164a2e58d", - "id": "394aa80cac2db93a5aca32bed46ef28feb1c5e49b0fc26cc220bcdb6a255c58a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6008981564334, - "fee": 0, - "recipientId": "AKX2t7kQ4asoStjoSHQKhGVBuDLfXVwtHk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f53f7fae41d09c34b7371af87d33ccfad7b3fd29df5644dea82ed1e45706d620220027cee4f90fa3a0e6037a4a321251a53940df0b8d44564343068187d19e423cd", - "id": "230f86676f4336a0a640ee78f3534633d1af95728a495c47bc0ab4ef1d826604", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6048199451708, - "fee": 0, - "recipientId": "AQQCMjGnEgsutWWRfL8BbUrsGe1z5HryH4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220211e94fec0b2a86f27a27009c1ca016d618d6415f2745e444ffe1d601963f2d1022023829c5346a27ab73f3f70cdde7200df4fdff5be708c6f965874fc200d529a18", - "id": "b26eba2e7164c76a09a874efe75237c6ea80369105872855350b1d3227d0249a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6062051615496, - "fee": 0, - "recipientId": "AdTSi59wjy3uZTv4tnenKL9fFWajaeYsCU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d5e6a1d7bdc9206e118eb2552e96893ed4af47543c1ac692798bc2085ae43fef022067cf6d3e916ef610dcdf6953c778f24306a989041bfff1ef3f1f599b90f11570", - "id": "6d99eb7d36ba0a942ae8e0c870b31312e6445392c2a05dbaa8734c56c249f1dd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6099257525671, - "fee": 0, - "recipientId": "ARPxAfyT51W26UdTgSbfVRdjUpzeiLg3Th", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3043021f4867133c5f9563bd0e953c12c2c7ed084e469b2933da10ba51eda964f3f74d0220527964fd63a3c23a6e140d7d2c142a0297d02786577d91f3a97c863ff1f43f00", - "id": "652889dd48a67aea94b758b01f454c7126398882189dc752bfb11e0a7d06602d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6114940797408, - "fee": 0, - "recipientId": "Ad2CVhECLL4ekRXmFVrYjGjALmyFmDBV82", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa235782a7afa225cc198c424876b1271be94a5cbd18b8b11a2d3a6e5b1ea561022006309b82f6241beea5cbc446d902ae570cb18c6847060e45d6287a2d0524302c", - "id": "2be98fdf7807c6d82dc2d8e7263194164b9f55822de204b1fc9258b735cb9003", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6177278671897, - "fee": 0, - "recipientId": "AUwfer8xFsv4Yk889R3n4oQF1nxAKts1tL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b8cbb6b32084b8ef479e3dfa560eb6a99fcd8391c26299ec9d7339e98bbf3e9b02201effcbfd175cf6d97ffc072c22d3966ed4b22ff771dfd78f7ae7b03039e00bdb", - "id": "d6c0d6f8ccb456ff951f74e3033b503433185415be2fef77b700b096a1dfc233", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6233789537763, - "fee": 0, - "recipientId": "AJ8vfPKvPPLJFdqVSQh7BBcZRF5GSTvBQJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022073c38a4d26f344b6ead47c4ac5f7e6d96a8946d423da13bb14b24c84c69e68c1022071d29be6bfe25b7b4e33b7b6d6757bb435f9ad9d00b1824b81cd5b16f1e4279a", - "id": "277669ed0959b0a322f658dc6cdc807ca4526ef9b7fb07f7b2c55436cffe85ea", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6249771580151, - "fee": 0, - "recipientId": "AaXTDvrMHd2nW34ENidytBxjtrALaB3uVF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206c0337c25c9f541d130700ac0475836bff0a69b427ad1a352fa18155212ceebf022022db04421beced42fd6fb6723769f5eb19443f4cee8146a11f2386fa882087b6", - "id": "5de7ff26c18b4b4ad9ccdf0e09a94fad3053d7f61a4d07beaec231083324a602", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6336348963748, - "fee": 0, - "recipientId": "AGeS6jmsQfmvS95KTbXLCFEKqpwUoEAc5b", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022002d5c9af048e1efd8bc37d2ad0e0aa64781bb3a738bacda828a9d2c3cb7affb302205a3594eaff82c4c46a6386089df34ce45aab71ef0ef1123e623cd64793c5f4d9", - "id": "c13088a3d622fd2925df99c39a4b25eac1632c68b3386f3351d9ede44b4bdf48", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6347464915470, - "fee": 0, - "recipientId": "ALvAAwn8MHupoymbgiJSCDYiqjQrWTEQ1H", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203d2c5ef313400dde6be26935d2bf2eaa8f7daf90acb6dda71af277fab69b55fd02207280ed0a4f4114db9dbab70b067343a36ca6361e0b00cb9064a39730e9162b76", - "id": "631288a43196ef9f2d5b214a93c261d1453a532d83f3f68a441905899c5e5165", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6608432032957, - "fee": 0, - "recipientId": "AcbUExwmdGDJgA1GWAjdEvx5VZv5VZT6ck", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e9c9364a47769112d10d48b398c5190e30c124d1c22b6d3bcd751976bd149bf70220297154a4646628b181af186fafc2f2b89c64b2d6b91b472f14353a324663ed63", - "id": "3def7e1baefa19fdfaa298305b4fed13eeda8ee4d8627304754edfa9b656e403", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6690095694899, - "fee": 0, - "recipientId": "Acj7HFPqsAauRBAmfHqAcPiscUCxdiS7b3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022034909f75febe76da183cd50b5f7e0745a50b6317d1dd1327eeddab9f42701a9a02202583150e149038dff6694c382b45949ec0d052d1edddfee4cc31da83d7c39311", - "id": "e912a67d2b2f1f5579c60cc12b09898804fd06914fb6b7032add9807c7666bd6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6816039047765, - "fee": 0, - "recipientId": "AHBdGME8KmcvG63JkaQ1FndwmPWyuBWTgZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ba60a9d3173dd42720e271a63126cf2b49f83f0846083bc0314d047be29853cc02206d2fe11664db806837fdf975675360c51cf5429a2a32ec88439f4969a40e970c", - "id": "4d1f9302f5b7ceb7682750384d414bf0a5f6b36820de505c8965720f46874df2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 6845037580681, - "fee": 0, - "recipientId": "AXFnbzewz6GyVVXuMhvAm7cyMLZRijpZXZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210084adf931c399b8330219ecb1e0091fa02e7e400e8f44abffa0011c3d5123a8c702207333080ef7f923e35c53f55fbda2cd3b686fd30606b0d6b92fffafc0990a51e2", - "id": "beaa5d25d2d5156209f7a6bcea44f483080f1a3790f72e2e7405a1b7adaf7d83", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7198886304127, - "fee": 0, - "recipientId": "AYidBk2SLnvrtgCu9ZYX8thNPweQXKoBgt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bbc9400997cc61072e38bcf2a3b4a600275332a5667fcf9ba463935309b6a98f02202fa932ee91e6f940b9d943255e1ec649517302c5a93b54fdf49847ee5c0abcd3", - "id": "3e725df96a6f88901607d832bad39c83e18f7b52e689a3590869c85cc4dff5db", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7274098215498, - "fee": 0, - "recipientId": "AQiRjkc6LSFBc5JgQHfEtRYTBKzuibCm9d", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a0d5eff67117daa369f3b77839d2053ab16c5b659dac2f3457a75877cf346ad402206ef582d8394a23e3f535385d02ec135d09bf6aba3c1d812228d59052f3cac48e", - "id": "f13e17151b9761aa406da0d987c8790eb0556d93eaeaab95d5d2b91458a951ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350458828062, - "fee": 0, - "recipientId": "ATWCEtjbAVLDMNCyHmdRMeoiqcHPahb3A5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206fd243d047585330ab1711f38f4a5d8de5d283df73af280628aec73c56bc13f3022035c0af74cb2df381b3e3ea2056d77859d085da0105c2e85edc6d607a6ed6df57", - "id": "9492b0c5b908ff768932a67731efab51d12491d654ddf6144307949596c4f581", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350605843105, - "fee": 0, - "recipientId": "AG61Zuk9tUxhu53EfvBBNzh6aLiHshmbgk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220289eb9006920d7e06fdf963d3a6b1252528cbc897bec005c20773c2a6fcc1d5d022010e275229dd9e83053e9cf8e931e8d6bf28c8b5a7a40275f6da2d08e68f12445", - "id": "a2e69e4e5391966a019444eb1920d8e6c8793cee7c6778848f14eb8e2a711d7b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350752858177, - "fee": 0, - "recipientId": "AJ89PsZmopZgpAEJYiFpN4Nsf37AmqGFEJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b1d30a5b801388ff04e1afbb0a1a08cef63e0ea4f256826b49438559914965e002204c6311d26e092f282985591802be6819a159e368f86f8bc72b39ded94b3bb93c", - "id": "6372eabeb335296df1f7dd2d62aaa635bcfe1259195ae57967a47b1121d16578", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350752858177, - "fee": 0, - "recipientId": "AcxafSyp9wY2zZkqDSfX7uM1ZPsVsBw2mT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100af2d76096e5de9e708de3bd6d68f3144bebc43dfbf09272a9f402870c2581e49022017a11a3726d0930d47a965393455304dee143d0eeb992fba84880474de00a211", - "id": "05418f5b2eaf40ebb39646280a9f1ba7850e96727bfaf4bc4379637ab169e7f6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350752858177, - "fee": 0, - "recipientId": "AZXTaxgDD7YqDPJLTkWwKgB6XPZT9L9Ata", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a54c0c0c82ee1943d9fdd66b10a0a6f70f8697bf5c2e5a70b108ce26669a639b0220463b72006b1f45a7403ac406181fbdad7ad4173d4109f20c25c890551851994a", - "id": "d311075ad854a0aabcea25b63cdcdf8f4b81ea41aeecb05e09f64c5e9d9056c3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350752858177, - "fee": 0, - "recipientId": "AeX9ZCUY3TgYBmxhT1eA28buQymhzcRkdG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202bc46f5c4714ddec901a698d0ecb09e4eaedeeb654ca202f55f3af3256774a97022034c060851358a005d60fdc2c88a4706e0cd4252d1ed84f64c42227bea5fa0a5a", - "id": "f5338178edbbfef4715e79399f932057179aa09bca7ec7fbae3c170bffa5bded", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350899873234, - "fee": 0, - "recipientId": "ARnH7KwF2dFqZBaVtg89YRNpiFoN9E5s4G", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009a0f64be9d209adf953456872e1bb098c29174f219b4b620287a624663890ed802202894dd9517455c765ef6868b12239246a2b4a862cd9e5c5e53cb7e24b452f5dd", - "id": "833780d0f9ddbdd029d0a4c3fc15248044540cafabd9ea99f39ef268de3e559b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7350899873234, - "fee": 0, - "recipientId": "AboFjwBTGZYhpHeBSRyX1gRifpmGaba1Ne", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009f02dfcee31f851c53ee9ba7747732b8395dc294e22ce326bba9087ce845939b022067c32661f0ea2f62133dd9367ce19762e8ba976ae5d9fdbdd0b5747880527ef5", - "id": "da390944985838649b7fb34067c7335926531602ab7fd04993f3d3dca8cfa0aa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7352075993676, - "fee": 0, - "recipientId": "AHJUzj6eMQ9331gQSJ8VMmxwyTxXgojdUy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202efe05396510be9de3a85403629b8de370c7e239b9aa7ba8274a3dfe813b7639022019fb6626e456e050efa3b49ff2bbe5d53ff9c7b0f857bb46a60c4574ae9706e3", - "id": "6baf648e78dd4f865452d1b2a10008e9ba53067f5f7b33689715e1012e11b920", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7467974903126, - "fee": 0, - "recipientId": "ARBHK6526c75WtrSA9twWuFbcvxSQXh1ZB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100820cd452944f0b765c309ecf5888f897a012290fdfa80ada1d79edc8048b068002200a7b4d0de4d6da32ca4e02527c9630f08e2efcb2b5d9a2ad9fb2ac59f9236058", - "id": "b615051a5fe01170d7972e4cc06eb635e0ac7d8435f7122873308a029d64b029", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7596490265803, - "fee": 0, - "recipientId": "ATDz1si1NgxYVhwco5FU8rziXikgzMswrL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203ffe6e56154cd9fec39d3b5ca60ea6b371eb95dac9857a4365b807b329156fdc0220733a0c695b65af96e3f2b8b92661c6a91e1667867580abf25f146ca2362e2545", - "id": "7c77338e20a9478450a35a4ce4a503ae93c1268850c4582deeed42b2ea202600", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7728600535371, - "fee": 0, - "recipientId": "AUzNXqYNtmcbPUesxouPuLnAqcvxeBv62r", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207dfaa07820dff0518a6781b5637985188caad8c308a6716b0eb5b8e49a399d1a02202a44037fcd6142cd558e01469a283cb7abaa6b63f383eed37425a830a38952b9", - "id": "98b95f131466f769280d7aefbe5cf7fdb7aeefab4dda10d561fd22cefb329e1b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 7863973424054, - "fee": 0, - "recipientId": "AcGpDjv1Ja8gybNf1Kg1y5wfjRvB65nLJ1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022001916ce368bcfc70ab18b04a0972d24a4a798c1bfe1ac82af88af3e57f05109d02207d702327da226d245e3e73db80fda6ab378037e21cd96129b5eda4bdc44f0dd2", - "id": "8da4b1228b425051845def8bf958afddd81be64966832d56ae85f30c09b55298", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8061487401793, - "fee": 0, - "recipientId": "AYpKBgSEGotwZF3BvgpXWxGKXsawYQEr7U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b566d3021fa2bd2a9bac9320149fdea4f0aff2e710affebf98a8e4e132270f27022036fcfa529097201dc793d55940b20861d2a359bb14d82e8fd0a1ae17359577ff", - "id": "7f88e5d7f79c63317e0c732c03b451b1ff2714bd5d87900faf43b979e551970b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8091708746281, - "fee": 0, - "recipientId": "AMgi4FUnRNsseqVpBVQLcHtk2PxkRFkv4M", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202750a5d5fdb30175ba7a0e8a417026664dfde32cff5df156e5d73e25b26b5f0d02202a6ecd37f045c321be0526ce5b854dfebd8a7b29e646fed77e98a126458a62e5", - "id": "834bb22d473469b193464fc80c52058042e14d82d6e3ed85b463cef6da9e68f4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8150156945826, - "fee": 0, - "recipientId": "ATuYepEksWdtwk3PTdYEdjytkTKv1L3R19", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a2f40bf9457f828ef8c32f1bcbde320417f0f70eae75ee0492509a44986b97be02206a1fa49f2b1079da0af70eab644a2e161c69774bdcaf10d6b924e3b4b0730a9c", - "id": "2e66f27a9f97ace05f8168b01858118e23cdb033ee68c84ab0c5cad7d9154eb1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8360754246642, - "fee": 0, - "recipientId": "AbA6wrkyQS5p5iDZF25BXcvuUgfZm3Hy7A", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201477fff0e08a35db8bea53057aa0b8cf323ba63d32e3ad28bed7da6d79007ed102200a5bc18c359443f02793e2673e615dca45013fba0dcf11d9719d80478e8d6e93", - "id": "c087ec57f646b73353792010f5dab487f12111ba61950c3cb4dcf26c51daefe8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8820462384640, - "fee": 0, - "recipientId": "AZf55Hbr3KnRwHbZn6zoEov7TSkJGhDHWU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b964e775cf55c8c3c174c1a06ab32919bc940dc74121b06f5f985145c06781550220452aed6f7d901c9ea5ccdc9a3c9538fd5f2fffdc8b4abd1778627f54674319d5", - "id": "206c25241d5f0bf068e1d532c0005957992176629f09a897dd39dada84b7d500", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8820903429812, - "fee": 0, - "recipientId": "AankYCzRAR2mK7z2fRXmXxzUwJMknRPkDq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022023e35b79c2cd4429d44e9a152371760a704819b464132b8464313c139e8be78b02202b5fcd45b45515a5fb41a997d93f55c089e65090a1ffbf31f07147445181d5a9", - "id": "62b7ad211b62f4c87e4dcd71d72cc6d9aadc8a4077379d96377e3a8fdf468b78", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8909427869508, - "fee": 0, - "recipientId": "AP47UoDcZ3XMq3P7VBLpJJnHGjGaD7yGyu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f5619dd275b2e1857001e6a803b2cc5978b562f947e569bb61c66d57a381075702202087f6b32d421f28c766fde6853ce0aeffb672903a9c065f3520d4111653d5f3", - "id": "8a8001b5fd69483fb8abc89b2c13626c30adcdb94c04a25dcdc1d81a5bc3214e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8947739735763, - "fee": 0, - "recipientId": "AeBVmaLa8zk43EApXSKPcTudHKRqDBYFWd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100846c22b8c3b999a50686b0df62755762c8f3d6e856d30ef3e467ea55f0755b9402202611440aa0e7b6c220399b990995b991324b3cf2fe8eee8d3724736edc00be2b", - "id": "e434a3e0a1ca8c488797a9a7e10361b4f0889c8528263f2d5da8de0ce2f923b8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 8987732597729, - "fee": 0, - "recipientId": "AYtFa1PZcaXinZSwvsjoym27aAAKwEAnaX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100871e75cefa2f4aa5aa3e2602c3a918a98589889bf157f72e7f550085423d3300022058f068d7af9ee807c49abae45edda26876e1205504229609397e42bf26d8b4a4", - "id": "89f718ec38b7ac3e6ed5c353e8d6c6b7c77558357fb317e4ad45f00a593b4d2b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9019606582562, - "fee": 0, - "recipientId": "AKeLaHxdcSBzMyM5bJUvThFcTHsVxmmatz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210092e17b11d4c617baefc3df8fbb432d34ff142e1eba516f363507e6f7d5dd655902207278a3fc300dabd87c6103e680d0b4f9a9a9daa9a7ddedb372d8a5039a5b8118", - "id": "b1a04d1ff1bb94dc1b0b1902a803f95f29b4cb75577899ec9ec8f0b3d042fa17", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9073460596513, - "fee": 0, - "recipientId": "ATNoXbqJPxJLUxNrPoGDYXo4TL8dsetmgz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008120ddb52945a8a06d38ed29d2444d99ffe8e2ee7a6626ab6444b3484cfd94f102202f304a833c539fe335c91ec20005231362b5872d37f2f716ef8705857dd44bc0", - "id": "aa0f4fabc4952d57a65943c6d9a4c6ffdeb77ddb984aa4c6e73918028e697816", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9106661500215, - "fee": 0, - "recipientId": "Aa3rBGChkK7iz2NVnkUx7pkjwgtgaZRpVo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e61fe14e4167951da3bb22c81a12a387602ca09796354cb5d2dfa34b453e31190220493edba4cce40195451c2ef5c260726dd80a81f9e53a2c0bf8714c9b2cd33b14", - "id": "9db14bae790c26d0073c5cb21b54d8a860d79573f6a24004d5938db55917d20b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9116425808613, - "fee": 0, - "recipientId": "AbSakR7RXYAEWqBh4btWuVXCG29HTdxCgw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009d069471d4566b87598efb5d7f4550a511b7ce0d303f8eecae2532ec3cf1664b022072ffdbbde754ca2d1b57600ad8f0bf8db9d0c628a4f761479ea547af329091b6", - "id": "a3ade2de7783875db21cdb97071f1cadba19e2a5258fedea37396443f7dc88b3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9176679868148, - "fee": 0, - "recipientId": "AKWjaZp5KyKzdCnJq9wQiF8umWCJL33E6B", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f6d4983def05d2a75eda8867eb47c6ec6cef8c4d13d43c85302161685ec7a324022055c69ebb708ede45540d87ac55fb4b240f9a1160a5ba8dd1aa7d49a4587e6572", - "id": "4fae51a3f14bd18dd23869bfaa3fae58c31142644e18d4422ff46f922f68ff1e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9212629406283, - "fee": 0, - "recipientId": "Acj1Rg3SaJrZbiPsTRN7mfzKwHxh9ZLciN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204dbd9c143b3e6d1335ccee6965087fe0d875d4d8d9c9d3574e018317508e248302207cc154f6ed0baee54eb44551e9a3d67b89ae5c539568c208b6e6243e881909f8", - "id": "5aa7d5de2c38dcc27f69d679349af0ad153ee494ecaee656a757fbf3cc1bbdf0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9261801586245, - "fee": 0, - "recipientId": "AJN6KnBY8VxHfiE7YDyAwneXCwDVtdh57q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ce0a2d090fd6bfd170616d273b648bd862a37d30e5dc365af8e70dc702f0763502205b95aeadb2b0e3e02a68638e5e0ddfdf74ada07ee5630e00b22e58392144f3e6", - "id": "006c9df41217c960f9888779a7fe79f5f7e30dfc06d450379c54aafe01820606", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9431021629737, - "fee": 0, - "recipientId": "AHpsBCfa32ufzETjsSJj3c3hJNGhjAyq6f", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f26cdb6fece9de8ef933318bb3af05a349e888ee8ab47310aa829d96f2b044d602206f97eab55c51cb15bc25e66da6832db6a4b436eb538aa727a5264fff4ccbfe37", - "id": "f543ab6ff8794636fe7aaa81367bbc5b3c47bdb7f9080df77d3fd09067941b56", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9456800520174, - "fee": 0, - "recipientId": "AXt8HyD1jBZsYE1c6x2HdD8HoVJsjHuqjL", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dbb11f95e9145884db5207db7a7a45b4eb04bb4f0c833c2949b2019194f7c7b602202d8c32008367ff0d486a7c584eb23a9feaf533188ad2e38d2a7bcdb912561ac1", - "id": "ddd35a61d78d3ef066178e240dee956ba70f73b0ff41aa59fba2543c01ceee60", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9529256927883, - "fee": 0, - "recipientId": "AXmh1DXiL4g4JjqJp2RgQdHnycZspLeztT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022039c3d55cd46134f05af871e13604c76aff0e92381980fe2d4e5e61f63efa891a02207b707a5e64a03e5ec1b6cce3372da6da6ca0ebb2e2bab0cda4ee104aac92d0ab", - "id": "ac0915e202a613fe3d133a3b8e0a4b86737e0d06bcca02202d583e5e3ddbbb3f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9547731294407, - "fee": 0, - "recipientId": "ASjGMDGXLfB9fViy9qPqYGEjgv1jeuwWox", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205e2ad7e24d33353687c030186542c4ecbe2602a35cea56d4e7b241454e785e51022028d425089617605caac7b1827be5cdf18abe4c8235c2a622669c97af32f965ac", - "id": "d31f907d222323d8e5d6034811433ab67dd7dd334e0a5c6bbe24cdd3accdb501", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9657132907103, - "fee": 0, - "recipientId": "AGFfszsJjBPwUf5ko5gQJaX76xFVoKbh2E", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cece660ea46f4303e6b76cacb62da21ae4c8a6d11481ffbdf1b071e49d498f4a022034ed3118a40013cfc73123a39ced55c65bdcb01dd8ebccdac9ca689faf8a8514", - "id": "2cf431f04c0124d8149a4493ce3b317257512684868568884658512f5bf02d27", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9720973358155, - "fee": 0, - "recipientId": "ALxR1nKJXgDfNtEuek1jaiNJG7gNtfZ6qb", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210086691e951494d4808f416e82c1d346653b04f39272cbcafa934b3e643447dbcb0220292d0fd8915227ba07bdc17bb7a7e946f6a50c4c063950fccd47a73433f5b42b", - "id": "f532635980304bfc7afc3ee2873890f529431ecc3217f8bd580f47f1369d0541", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9861574288956, - "fee": 0, - "recipientId": "AYAYsDGPzVarAcqXrpUE8eYcftN9J2UNtK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d7531a783114009e2775b63dfdf0123ba0bbadd193e284a9076b4e02ad1063dd022041f3c3c3a9dd40d17483c4c01d2615eb89762611a5924718a517aaa0ce09a829", - "id": "cb3f0522a4824486a1277d54d509ea9956aeec80cc2fa77dfdbccdfc7a888a62", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 9967620875688, - "fee": 0, - "recipientId": "AaxB3RG8EhWNB1axLbDrxza3v5E9kphnwn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c9f22c83fd1327fab869e27902fdce90ed952126e7c43c7e9afae0cd9007591a02202141f6f04a4a4b519fcecbee3afa90cac41a090c31393db048946d780735d33d", - "id": "a47732e4feab4d3817daa01358f6f295d9e4f07ab1e383f08b09ba0cb755af05", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10271698022691, - "fee": 0, - "recipientId": "AYDYbKKZ22ym3ZpXcyzPWc7bnvZXRZdiem", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205b259b711e8a7cbef4e8b103cdd7af07e6aed38f65d0f91ce15f906dbf6dff7102202a0e181a4c64494bb7a679e0d86b397c644e3139e0ecd8c90e38fb7dbc798811", - "id": "a46b6b26e9c55a9a265845d4109e3e03ea963491b5efa4756e49a6aaea94d91a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10298404754306, - "fee": 0, - "recipientId": "AcdSagvBLHcUuh1f3GbqhwxDZ9kuNgt9Lw", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201deeb7ffb887271b4ee12f3feb9b5c5ef4ba4c60cb4a1bc6555474cc96e420830220542663416f5fc69b7797d6a4d231041a6d4467d77d1598e19cfc7a64d89632df", - "id": "f9006c06167e9095b7398eeb11f8195d715b04be4689eebe59a5864def4798f0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10305487746344, - "fee": 0, - "recipientId": "AGVBHkaRr9XWBLZQJDdV1Q9RXcoB67PBeG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205601083db78190cda32bbc1af11fffcd7f933052794e7d4c95b6292db63c3f2102202bdaf8d58e7cbeab12ce6fa0d34cc70cb02f381273d533db553e15f9b2cd2d08", - "id": "66092335843fe18ab2c4a8d137fe0e341057060964ab4169383b461df9f7f385", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10318455720429, - "fee": 0, - "recipientId": "AdbuHGsffes9YYyKBp9DzKrcQofg7zuSKQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200a8c0a7ace259d0393f8c3662af8f8e27a0f261e69fcbe00fe99a095193965ad022034badec2675471d2d846e28aa0f4cc48ec9d1b8f6879f2dfd769300496e0dc78", - "id": "457678f9e0c30fd116dda8f82fa4e479b23a0139d36872bc4b992fcda5fa095e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10360354001447, - "fee": 0, - "recipientId": "AXkBtARJ9ibkidfLMhzVSRCAuqWBxRaDhj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204131d85f9493b052192d281fef2a87ecebead69c4e4b72b306c967a2bffd013602201786ecd3284b341843cf434ae92600de4bd044840ea306ef36372da4f8fe7e13", - "id": "eb6590373de4b7155bd45679378a312473be8ceee6c9c4fb86f2ebaea4c1be1d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10445981844431, - "fee": 0, - "recipientId": "Abu2eNnxLNUKimhqKJg6x2aAcsUJStuCAG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eccb27c0cfc2b60f4ebaf647654132c4e7fab649368a77241c55f72dc8ca3025022016463ae4170d699d3cdd336c992057ac3d5c2ace3be998adb189840afd328d20", - "id": "b4b03eee17ce3bfdefba3d60783e5fefbfa99f5e2a3ae36d3a77f42dbba37837", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10446382135839, - "fee": 0, - "recipientId": "AXBkJGbRzM2rUD6orebcoP3peg2xGbgT7j", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022008bd51d33b28766782018df754e853f8d92672e3ff570d1261fb1cb2f30fc9640220022208c3f0da9cb972e2c1888f1fb720c1e977b7376caff753f23fcad801a5cf", - "id": "818bcbbb73b9470f0e4d0e9606f937ae2281dfec46f55251ac7b77d8ae545bd9", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10585084115774, - "fee": 0, - "recipientId": "AJXuBCyg1jivEDuJqrJwu9R19C6uNG3rHM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100887bab7c2e2800b93cc9df58e418ecec30395fb35db6c3598d541295cf4104580220128140bf3e75736d5710c9df703b7f3a9818a6a1caeae847c0ab154645bd68c4", - "id": "95ea9f02ecbd2722503b7ad6754a29b3c743e2e114fdd403fc96b16097a3ff31", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 10792241696323, - "fee": 0, - "recipientId": "ASSd4LMA4BLtskUS3XvPbQYLe84qCJBF5V", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202885acb08d8088274b1a8ab4a28c796f5489b82412344c5ddec9f21389bbb21b0220525fa2842e1e3b91b98a5f8326cdc89d880e543da82be9adff19d923a87b43c6", - "id": "872e270c3eb5dc0d487a040745293ee5373e2622ddc9d34cf3e4ad793f2c6a25", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 11033661805931, - "fee": 0, - "recipientId": "ARo1r6758wTHS1QR8LnhJiyLcudybWPhb7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bfa945a5eb00deeb1f4774b9a2d46a44832d38877976f385be3fa88628c00ab5022024546c571207800790fab06037d3292a46d31e9c947acf318a27f67db8e42ead", - "id": "1e03b495436ab9cad5aabd942c84cb315f7c7796a597a56a6aaf9680f1124173", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12262151610133, - "fee": 0, - "recipientId": "AWFxQB7mKARk8W63fNRpp6kZjVsapdFsDa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a31f640dc8ff053899dbcad3ba8b4d67b1046db0c9cfd2dbe8c15a1b7fb7d920022003b089de0b92bdb55701c76ab22306636acd88e933f75cf6d077bd4cc7844c5b", - "id": "e550f7a823f274dfb83ffdd148c2d18b573abad654583438af7a702ac6de40f1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12274908823760, - "fee": 0, - "recipientId": "APcaeU4K9uQXSRLRGEgDM1G8gtiBG5tCYZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210098a25f2a406512474e48e14d7d009cc8e7349e7b4a51f2ebcd63f4996c2507a502203cda6a22c4fec125995d1163ff1ff725ffe060c6b720f91ae76c20a7217b2e6a", - "id": "736b8f9771bbec8794353162441c1dc30720dab02dda5f17721f3577b0f7b454", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12417330032449, - "fee": 0, - "recipientId": "AHLhrDJwqQmHnZnhEPBJqfEHYHegt9Y5rd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100edd8d1aa1c15c0249e1cd88ccd81edae73250b10df8c8a32549632404b5f0c560220441b966469b37869d5415cc7d90205be883870c3895db4c8b5ce2b4a7d0dba17", - "id": "9340ea9bdaa5155914bf58d74b856d532f1710efe6c569ecee144d1c6ec51ceb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12465317334603, - "fee": 0, - "recipientId": "ASECaxsSkxVDWry9D5fcygAY5ZuutNBvPG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201ecaf13c7ce1da00ef9f8e28e95be0d15d26951dff1f742399e5298031337c8c02202d756de469c1f1ea127c4d785d723ca5c7f6991bdb8b8e3a9e7be2498f5c9377", - "id": "6902bbe0e9cb1b9cf2faebf4f96d27c85f4b1aacf9ef7b4dd52aa7d882b4470c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 12637331924100, - "fee": 0, - "recipientId": "AJ8YEJ79noVf2n9dm7WktKLy2yJPqmUWiE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b4236f3e9b43c0c3f503906f146a6c736e8572264c9da1507de8cca20d7df08902203004c4ded33dd32eec7b97384b8edaec3cf17c8b0f813084703c289e17572cfc", - "id": "695597762e9a98153891ef3ce163835b600d7d6c8d622b15b53f70b1da3ec08e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13156450295530, - "fee": 0, - "recipientId": "AMnJCV7YZg3aQ9XFH2Wm48wX5Q8KP8yf3s", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220798836f686318188a82f5a0b37e419bd52a94582e4bea79a981bfef94485a06002205539539bda455d85f826a824772f5a068359d2c0af5f8fa94876fa7b6a83f14d", - "id": "dfe1528b1eda46e77923bee940a97c678ac3ccf209e146cee8b4d9e590d652f2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13231355144718, - "fee": 0, - "recipientId": "AYhj1z71Ajs7gQPb7TQoK1Th2HWne8Dicz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204e4e7921958f482891c6edf82e87d577bc1525ec26a1606239dc3bde80c7e20e022036dfd51919362e104d7cf98a1d2fcad1517261761e780569d60503dd87c85b9c", - "id": "d531efdb301103524d38dd7720408e6692c77d4b2581c46b44e98352952e2990", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13433119945670, - "fee": 0, - "recipientId": "AJuwxFoWbhWGiSghWDuNLh7B2u6orxYKEr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e2e10a21a73b7fb49a51c9ce6981401bb7041ee97b929781e9a33c55ba85f8fd022045871513d5cd53061f7b21ba1638ea8502f17cec0eceeb1ef66715712d3a2d92", - "id": "c37d982d2277da42cb03590feed99bf612911df0df4d0819c410d906e28237f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 13711299443014, - "fee": 0, - "recipientId": "AcFJgpMAH1SCrCRcsgWfXgbQvDiBh7suEd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206fb5cbaef0ff5662a750ac6a89b6090cb5e54934f86e1852f75b65801397fbb4022052e7eab49c5daabe9b69fe1ced745c2cb722c3c221864f9336584082bdc750dd", - "id": "96f9f1ef9345ec0a80a1d49ac3a366ce130174c328432db08758a69f694ef2e0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14115873577166, - "fee": 0, - "recipientId": "AZLm98LrEyNEmbfe2AHV1q5V3FZza6x6fB", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202d808872dea296887d825d2939947ac1ef11f72f86dd8d0c6725f280d611ac9002205b08d41e9438076a7689cc07f0d74f3af73158568cda9b0469cdeb4d631c1373", - "id": "60df15ce23bbf98fcfef08bd24668c1951af818540910ec3ad9dcbc5d2600995", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14159732932194, - "fee": 0, - "recipientId": "AWiJP7L3ZvRPzZyc6mmM9qqNcaYbL9ABxx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204c66842803665c4bcf29c2043e1d9714e3724a1e1dcd935e1ccefe6228704675022010f26ee2519ced03bd61a8095e0830cab83d159c56928d7f11017c67b8c6247c", - "id": "9d5279ed2f9c0e85a47378bdfd168d29b83e2ee7d6ce8813c3a7b4a68fe50d85", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14302397057832, - "fee": 0, - "recipientId": "AcRE9hpuKFJtyqCSGDS1QcAFTYCU17t1F1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022019ce5205866f88b78846a07a0862122d34113cd250df8abd884c7202603af10c02206d05348f0438e537108ae858798cc390f104c69047eb00aa47455bd89fff0b22", - "id": "5dced5fe4b20327ae767ec3902dfe102e6764a86f20fcb9fc75c6356cbeb7385", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14627998187772, - "fee": 0, - "recipientId": "AeDZmqf4hg8HYMvpxJujrVWqCyVUyqZ3Zj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022050e3a4396de2c6bbbc7cd1120e46a01863b9d961bc19ed42f98a18750e61a8bc02205ba8fd0e08d26df41708ae78280ba08fffe1dbdac48092b464da8b77649a716e", - "id": "816320ee8653dbc97e201b24ab45af6d3c4d182ca9d59b96c60f218803bbe76c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14701505716353, - "fee": 0, - "recipientId": "AdTzqZEgkihPhhqn7isgQNM7UmJCFjSRaJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205fceba8d1f594a4c189f6939db2567a740598184cbb2787a82560c7d33939191022054ec9894c1f430554cadace94c584a6c6ea5f907f06f5636de53d01c9937667a", - "id": "bbf0def2ca931ae79bb421b00044e5873840c3b761a24cd6db19161a069d8575", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14702758612028, - "fee": 0, - "recipientId": "AWSCxEf8ZFBNNMturNTbyrwaHqJHTAz5hF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220415ce13dfa1e2a0f62c4a1b452747c0818aeb16cf852e7c649a7e061b522295f0220738dd8e58a0bd7b0cb3035295d38aa9c164cf1a023fb69c2512f964ff840c0b1", - "id": "39aa37fb064becdf8f37a9bb1e55d3a1e8a96beb6be1fc1779967690a74dd7b2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14702828851868, - "fee": 0, - "recipientId": "ALcRivRn5MpE7gjYPfhvdjkT7EidnM6AsT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022041e7c81b713fe8ebc58fe4d9db338b89b3663afdd526ecd31dcb0b6e9a8f8b1d02201eafac17f41fdc7dfa29c748261f9a0e79bcfac5e418a1c144b0d820dcf54ab5", - "id": "19270d05eff4baf154541b1e9acea7f70990c9fad504e043c281d384a856314e", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14708415424040, - "fee": 0, - "recipientId": "AbVMJrPSe2z7UCqxfebGwvVnCPePzYqqPX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220429ea8d0eb83bc86a8ecbde1777798182f219eae5aa56de3cae7bbdb4d04e054022051e6769e588884e730ca65b1886452de1d2f7050a9bdf758675f78c0606db368", - "id": "a116b90dd6924989225c4ccee132706146ccc6a508c8c0f2055349c71d01a375", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14818222131272, - "fee": 0, - "recipientId": "AbmqyXrS3NaqZL1JanAV7pi33cxKHewwQP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b1d6a14c5c53e2c96d5080df55c593d397771c9d4cdd9c87a35dcb886102e7c202205e2ead847be17b2d5c71b93b7b50acffae974232c1164971145e6bdbf912a85e", - "id": "5b864466691fa06bd64c97204b10e4cc4de6acbc4f9f02d9183e96f53ae9e6e6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14822705716353, - "fee": 0, - "recipientId": "AMwimBgD3PLbthyD2pw6x5kr7XdAG81u4Z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100cc44646a784672d659731b5c7aa58da1d4f010845bef874c5dcabd149d2c871702204e067aa1fdfcd05315ff030f488bff4160bc6d4dff149e6d0f248cfe0606e5c1", - "id": "ae29982e256bf9213db1e8944b73f2c98942fc31a4b107c2d5201f67595102f4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 14895015891637, - "fee": 0, - "recipientId": "AYJiw1a15awqfRNQWXXuDmxpQ3DNjWitzM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402205f487f6903a7767049c69f2d80637c45ddf757d5cc2b879df4b12e421223e0c702201ff2ad7ab479099433d5cb74b17dd647a0fb0ea38fe72950601d4510910b730b", - "id": "4b00501c8bfb24939da2e8402e40ef56c58a5c61ab6b4eedfc528b1b28f35157", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15082558701296, - "fee": 0, - "recipientId": "AQbJg9r4c5TxoPPMcywzVfLHavxbWCQs5f", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220502e111dd7864a3bf81e5dfa24abe4f74db23747b2ae7017cdae6cccfb72567e02200737316bb0d540184321f56e7e1099d857c56fb50d9156f2fa4356c56b369d6e", - "id": "430dc9734272db7f88762940c12c32b5063b5ce5042e8846a48937d2636e11ee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15241050976143, - "fee": 0, - "recipientId": "AdYqsVoQVTUto6ALnocgCvnssTbDWnPAbP", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204012639c893ef39e7d67a0ba9fb133a37120007ae3b9d8ab7544cb2bcdda32640220232cc268d227799a8ea63d58697cfe0a2cc9de3123ff825ee7551a894d0b6d3e", - "id": "bab6b296e910810366f4ccba38ef01545ee34cd4386c0c1f806369c8d71579db", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15458231619516, - "fee": 0, - "recipientId": "AcXhb4DBzzmBG2USvxuJY2uEuFnMr7Qfhm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a2ce8edebcb297f1948498412a60c8b736320ce1ad347043222bf034b21fa69402205e098d716d247f85e9c454b7b53b43489d55a91be9f19d792ef92b303b48e9e9", - "id": "d4a58d3b71902d221cfe3cf915429551c77a0c4d3c73c92a2473c3c151c1f9af", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15525088530753, - "fee": 0, - "recipientId": "AXDwt4SoPBiSvfjSKgZdV4vMTaNCgaFHMK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100eef9c401c97b593bd75fb52828a9df9bbeb82508006ff0d683309a7c1119e6f10220040995de4c4246197bc51ac7abc5094d9ac8ed8b1a8562ba85634bf430d9d866", - "id": "f3e07c51b39f93978aa8b6e4f133400e33ab808f17b3a47825afa75a58bb76a5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15609782909903, - "fee": 0, - "recipientId": "AaT8V4r48jE2MtWySFmdk2gqz3oz5RkXvs", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b46dbd720df1b60f59d8221ba0f7af053d7447acfe378b0763edc899300e035602206723c53b8ec4b41f1c374efe7620b94e62f0894fc90174404fe2d36c190ec396", - "id": "c37f2b2b221265b74534bb17e8791ae000793bea52102905f35c89a5fea5bbf3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 15840600000000, - "fee": 0, - "recipientId": "APpQDTuoqkWQex8ZjLCHxNf7xpYuEdWpJ6", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b6d5bcdb2466f138713b14a398ad38c470affb52f714693f836574106ea1cc3002200bbe4f8923a286f2e677e660c7316a815b444db2b6f46849d73402466b12cd98", - "id": "4c20e8ee483a76470df958bf62ddd99ee14e9a2536be56599145d28afe1700bf", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 16008869346988, - "fee": 0, - "recipientId": "AeRmfbh2GUoEitySmsQRACFRkPQFh6XxCn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210082cbed89e51bde2a686b62c96c564914f843ca0771b18ee0f57a1266eb181d2402200d87f5c50a8be268afe0210c67a394dc63cfc5bf026db1b58c8950ea185dfd57", - "id": "9ca43cdb29bb7b775d487bc7392be4f6da70b6065cad1ccf40483b0c53e0b23b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 16171656287989, - "fee": 0, - "recipientId": "Aa3E6iHPVW89sPXamzE5CUfAYhEp9arAw4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c1f8856ceeae2243ebb9a171b36ab449c637536365e033e244a6ec47688922530220565bd9cea70b34e2f7a7fe5ed5b6b4fe2eec554def7c32b400df035c5dd9997f", - "id": "e370cd7fae15dfee56e44b6b602c0bea7819758a9a61fe0cfb5387cffbf5c747", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 16171656287989, - "fee": 0, - "recipientId": "AYHTbw2P8oKpk9JyPFXKuCw6auvVtr1Upn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009985662bfaf857f58295def71d71bf62109d4e7454eb087bf0af4509896161e102207bce078f3fba1bddb717c318f6eced379c983b9830d4295ae2f6f90ba2fdfe60", - "id": "7874cc7ba980cda4c93a50b415074b3406415f7a84a10efb43db211eb3bbe794", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 16191556704498, - "fee": 0, - "recipientId": "ASUXHuCsmbvLNURKg8bG33iPKid3dvyX7d", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022061886ff0eba8d281f18efe0bcdd9ee543c91a19fa82e539aab7a1bd83cb5839002204f05a0c30ab8083a8fbd9d1e48f376b52aee5c23470c0f9f270e04e88fbf854b", - "id": "ee246b6d6c7fb8aad48fd8dc16e11b86a796a21d24068a87f9822f6fb6db857c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 16746157299652, - "fee": 0, - "recipientId": "ATybACcBV3KhQEuhhJC2CbEUeAX5V9UpGy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ae2122c8f375f70a1d174ec4d2d0fcd25936177ecb38ba43095d7166fc19da2802206c22f1e50d9e150d40e1d77bf1e81b153c546c3f55444e9073c440f5f9a08467", - "id": "bdaf7fef1b17036b8c7e15f7bd23c975166b368dfcfa840b5e569e5e95b71d49", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17763076895273, - "fee": 0, - "recipientId": "AGycSD9trbo9XRQh7cFjk68dniLHM7AfBa", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008ca39400df66d8192aaedf44bc2cd791bed8ac3e0e048f3a94da6d56e201b6a702205fa0caa9f2f27a0d5321e06b0e4a68ca30af1813f7e1069d8e70afa378f794cb", - "id": "d5102ff88df36c6c97dd02fb8ef8e178ea72e32556c91a2e36f41d2363cc4e7a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17882280422877, - "fee": 0, - "recipientId": "AYzPTsYgUcgZXi1GjA2wHzutz7BMEcbm5z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100da21d666d853ed342f3cdc1f29eb6e6a557ca202c8876723637b51afd38499200220453d1e8130029e2c9b27bc97babefa23385b46f42b25dadd9467e867d2a6a66f", - "id": "d45a0dad2d8010afcfb30088104c4821dafaffa0c4348db0004e95e4b330fa24", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 17882662903696, - "fee": 0, - "recipientId": "AJAz37b4PJ9J5tnqBoQy2TDoDE5TiN8qnR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220796c225df7d57a949b38b5dce6ec87cd4e8c1842826369e19ef2fe672c8cf3550220061c7b547e460153c1be12930ca3e9d8fa39edc2cbbdf550d3286ffc1bc2a628", - "id": "ab25cc4069078357692cde0f4cb9d850988fec28be74f7ad3e97c377bacec8ba", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 18446724051788, - "fee": 0, - "recipientId": "AGjo125YZU5Ssop4kuNTUYSeWqxMqvKA6c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fced646103cd17b38cce5ff86201c1426304b6ca429e5231422d69e1db3ab93d022043255a9791928cd34118c57b07f1f587811179f29f8065ef35efbc8a698bd61d", - "id": "197db69caeb74f3de59dc23a8c5d65878964963e785874dd6b8474890106b791", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20225218326294, - "fee": 0, - "recipientId": "AWDLp4YdsBUdxtqBjC8J4jNr7dddSCmbDJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203f5912d44a52659b0a679ab303f5044ff5413876a759cd20974afe45f0041b3c02204e313f1fd22299f8f811fd603f7482ca519a8ca9595694bd28d0416a10952fc1", - "id": "0053820aeed9059247f556586e9e0a4109f4579fefd5245cdaf52f9fd901787a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 20741626925528, - "fee": 0, - "recipientId": "AN9sqKHVipVtacB39BFQkPa6dDhdSbUJ4F", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207f0cc0e15878619c6a0eb4a321492c62cbdb57ea87c5d38bf8c45fe8f188178a02206312324c971349b94bbbfca52ad71928386d6810194c2e1d3d0d7ea9d1f0b216", - "id": "1eff5e79d10063be3e29e764d3a6e4f64cd681a0dabecd2568492e25355bd2d1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 21951221806397, - "fee": 0, - "recipientId": "AS7WWoFGGutbnpBg3GmzqSLapSRxqNqoTZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c98da3c32cd3fc557d169c23b454a294d7c226f5481593b22e4b4b0168b3110c022063e54d574b1580bbc056eac3598f96b3c6a6bfc4ff348ae3861835e6457eaf2a", - "id": "c14b7e1b79336f7c4b26ccd962d0d594c39b067cfc7475a2a577725937e1017d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22059609327388, - "fee": 0, - "recipientId": "AHDuDQzUhW9r5AEcxWYLg6rtnAej8LpjVg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200645d5b06ea85a4712b6dd58dfd40731036928dfd3c72142558b8d45d3889d2102200b2ca62f33a473edf0330d1773c505561e1334452d0bc1aad4eedd4a9771e5d7", - "id": "e5bbad079581ef00c4ade09d30c649cdd6b36b69437c0690561e7b950830a1c8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22212505397146, - "fee": 0, - "recipientId": "AYo8E9DEoYD94vJuhFSmZ7Z7JdL5vUuFng", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210080fcc91a7c3aea6ab14ddb92132d820f48d44d08f956c6463a40139cc731051f02202f0dbbc598dfa8830d86d23ac58ba13a2e91c283590dcdbda9dcdc7a5ddd9e52", - "id": "f8e39f689d66d7277e0afd9c5e8d8e90582abf5eb2517ada993d9db8b6a4d5a3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22232699614369, - "fee": 0, - "recipientId": "AL8BTYeajbkWG8r7A2uYpAsKoX3LMk9uga", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206259fb0a490e9fc37569a4f1fbeec01d783a53a18630b70d2ebba2bbe0f6d270022020025b4c0c7215410bb5ab68d5c551a377e7bf7ae7e364454b7baca54f1b045e", - "id": "500ce8f0b6eb93696a6445f97c26e559f93f043f80ec4eba7d8e59ab30cd6220", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 22622858823224, - "fee": 0, - "recipientId": "AWEmVoaBW9Xs9mt14YeXxmqfmSJ7iLszQu", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207d52cc181b70569c0f2058e68603232d5532b5ca9863ad0464ab0af2c29b369802201e7ca2e873ff0f73427d1afb837220a89f17204c04c41c15dc5a32b57e1262c8", - "id": "74f0e117703a37fb072ed990224d3173a02ed8577441faa8704e767f9dbaa517", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 23205200171281, - "fee": 0, - "recipientId": "AdCphTbTtmBouY5wTen7MSzjy9fc84J9M3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200ac08f44e5c63a577d4dc5536ffb1bfe437813b5e79073d3118f66011c78bb41022034517534731c508f7c6d87d44e5848cd38d8475802a453295ec332ba79fe1366", - "id": "7622484aebbb489351c45157af39ec0e1c2f1c75ba8b6d3cdc5bbeeb87cd32ac", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 23285447659034, - "fee": 0, - "recipientId": "AMnNU9x52nTd9kEu8rDQu4wrDm4DPB3SCi", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202b899c4adaf2d1ba16c09c995dfeb02489ea611d07ed055662388ac291f12d3b02203965814654985d31c688c2678085328126fcc1959913fb6cbc1cf0b671a85e54", - "id": "d2174364d96891d86b3de6457e579b53b5750218ef9bbda87b5cd9fbb0aa24c2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 23749226298416, - "fee": 0, - "recipientId": "AQFdF9mpwxWsjMASsSQFD3kMxghDVfkWh1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204a71c021260c33ee6ae8bb5966e6ab27f09b2c3f11d8123b85c532339b2f6711022042f89f016efa114dee8c5944f160129dc5b1329ecbc61222f3ea5d7a2cd0c547", - "id": "d231ed2b05da5b4771bca09d3262cd4b66d7efd38ee36378b93ee9e66f0bf6aa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 23788728527651, - "fee": 0, - "recipientId": "AT38VrTjd8gcmW1XWao5hHz7zA5Cxvzber", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402204f4d855fada8a76db9e26e0e79c9e67f51907cd2ea7ada9bb6d640c77d49c15a02206fd26c6dac762a78531b42e74ee643f541fab7b4467caa936a865b63302dab59", - "id": "4cfdd48b353e9f7324dc3991738c9dc8f4c93c51cc18c535390c5328a4ec5ed1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25133628072635, - "fee": 0, - "recipientId": "ATYKgxB6sMBiW8bkuiq18eZBNM7ceyJ1zK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a7c5a575fc88063777204691c7d8c83bbe794820058c87a47099b4fc523b7ae802201925f71003886a98bd9a14d2e8a4596acf325186449825bfad2d2b0947e75967", - "id": "5d86d1704c731ec1454b149b7105c223ada5a8244045c91a7386444cbcf9fbc2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25643974662172, - "fee": 0, - "recipientId": "AVQ1UscRdsXQZncshwXSZEEWqvPNMJXtpY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210092d6502d9d4945e59d22fb7a1141717e41918428362fac7b365c5e6d2ee9bfdc02201c86ca71aac24424cbf2ac44ca3b77088cc7c873f826b8b6542abddb68ba50dd", - "id": "b387e98cd84f82172c378834cc12bf9b21646891deeb37ed9c2647bf400009c8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25690453309429, - "fee": 0, - "recipientId": "AMpKaxERjupes1GjHCVV4Rctj49BJXQ5Qk", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207bb590646296b2ac4b2ae347eb2da399b9df6ba21273b463e7117dd14ab61e4702204861d99922766f5a05b12730ffd84bb9784a37c8e27b58aed3f0b896230ec082", - "id": "81b65afd6830ef600587c8f22cf425cb485df032f8fa236121d986a0937ba41c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25763719365860, - "fee": 0, - "recipientId": "ANymwDRzUoszXjbJXQMhPBmLrBv37AoysR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008815fa72ddf054b47d90c7a4b9f12c783eca5ce10a557caa79109ca2e51176a20220595c9d31fb930159603274e72362a8807999dc80b3bb49ec43958837ac60d270", - "id": "434775c5695597b6554e89281a194cd903f8808b655c65f1b8cbcfae3e39c89d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25896835969408, - "fee": 0, - "recipientId": "AdLxpRiPQdsdZqxQWEc2kYxD5YFzKsLB5T", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022079e5465fdccb479bd275f6157553ae46ce6b5c51dd51ff0fd4ec517b6deb7c3d02205213870fefea75258a08f564a7920f2740f4026a8655fbc8d1a6efc6dae611b6", - "id": "3c33d4479fbe31777d0263d7a4b6c035be675a2a8b9eb9964353af2c43737a61", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 25963064569714, - "fee": 0, - "recipientId": "AYqoBy73QzTfN9LHCgvT7eH4tfet2RU4Av", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e891a570ff244ae679fb0284f55f65595d7d86ce5ea42323708bcf5daab0b67022078488e4c73790511ed6c1f9bebe8bcb0dd8914fd9ce070d23ca5553923f9d775", - "id": "5016cce5130bdff278ac554c89615fce32aad318bb8917784b51fe40bbab4e63", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 26922263723607, - "fee": 0, - "recipientId": "APjnWFvHhfjudvjXHQ7t3d3JuW1efhiWkM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203bdb6af67dbcc1a8a1fc4a71d9e7fd6ca66ac92d0321eafeb3e7e44aae2d25dc022045598a45acc95423dad43741624c047bd2c08aa878cf3175a789f3ef75e6ec26", - "id": "981e2c0669340b781e6989d2e4ed088e71df124c2c3f90eb97f024fdbea70ef1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27197785575254, - "fee": 0, - "recipientId": "AZrX4pmxJEzr8WkANjFsofL4ww5UeqbVE5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201b3c396a9b310ecc51420e3ba5d9a711269202abe71353aa9a99c9388b3a282c0220221c52a87c44586507dc1ba810a7aec7eabf07d84ed7185616a3ec891f224fab", - "id": "421acc286ec7cc4bea14a8d3cbe33e77cfdda80ac748e27e4f0dbfc09584bbc0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 27237835405410, - "fee": 0, - "recipientId": "ANgMKTXMWHB3WnU7sBeLqYmUCN8khwc4o4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fc96ad466ecf9cde5f005a6124f12ea237e66013b782406db1d8974926be0946022063f106ce7ebeba7a8c56393748460eee5ac888385a2467cd495d0ac7e6dae9d7", - "id": "594abec1830a739b1cbf625eef2b6301b5595ca5f229f353545c920c9b74d2e3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 28691934514542, - "fee": 0, - "recipientId": "AM78rDegT1jy4U7CEsWErtPSuJCdF6fJVD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3043021f33602385c5f0c9d5a374ab0a835c0ebecf104951dd15dc812cc94738ed6ff702202c366784aa6cec2524f816bb41b0eb397cb0702ac7e069a7a8938694093b868b", - "id": "c05ad581c646d9d8975a65e54a7deaf2c255fa6622af349bfa16a9dd753e418c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 29252093379711, - "fee": 0, - "recipientId": "AUL2WbCnaYBkSRfz3Ak11KA3Vn6b6eyM7B", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402203e1c6a962f0d9dac66fe40b694ddf60d1ae12a0e8d7d4d12d71615dc08c885380220597d2eb1eb70e8afac03f9f02a0fed3532d15958d431d41e852adf147015f43b", - "id": "909a1c0f7371c0e7bd5b668e80ee67c980c7c06ac95c9a8561e2d245439eb29f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 29403011432706, - "fee": 0, - "recipientId": "AW3MgXojRTWgrx4PzBmk5pjn5nyhY1aKH5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402206afb959420376fd8e172ff9535ad4c73423eb6528b90d888ef4698437e5560e002206d00854f5d1964357562ed79f680e7d5cfe887ee413073645842cbb248f5c34e", - "id": "69fd70220c9ad3cef6201a5043bc55f944cabbdf705808c6331a947ffb7933e8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 29495120724982, - "fee": 0, - "recipientId": "ASaaHvXuvS52QJuvp3nkm3EC4pEAze47a3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c3838c356872fd692642ca595c4c53678396cec839de9841a74a4799cd37c41c0220168441e0f980c92516b41c8ea7e337e384ef95ad130f2a62d297bafc5ae6f710", - "id": "87e6c248637dc0774a78b108505ad19ece71c9248cf5151ef517561135c5467a", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 29617494546387, - "fee": 0, - "recipientId": "Aabe2A3WdrsrhBp8uTjqxUEQVyB7G1tica", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022011583f87dc647bc5887425c585d04c395f17c6bda80dd0fb2b310acde7294e1102205e64a69a81cd848f5743ec144bb932ebf18136f952b240f5f579e9524ea1c61d", - "id": "ca4e72b41eb5b6bf5210d47eedcef40cc9ce15168fbeea443dc8c179ffa6e711", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 30052189818242, - "fee": 0, - "recipientId": "ANaT8P38GwSHV9mcCk7cEZZAieB3Mp8jxE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100dfe6ce17738cd9e3565cc2ce92f2b1171104573deec8f7c35ebcf39b906afa5b02206310b1b28588bdee1c4c9cccad579606a0150a73fc2500a78b7f126c6a30a736", - "id": "1aee700c3737c07c9a6a8dbb6ad404ab7dc313af7e1e0c63e07cbbc1b3642845", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31009000000000, - "fee": 0, - "recipientId": "ATgAA1gP4awvYXk4T3FrHRgpNsVtaBU5UY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f44445e667716c93963815e8872bb798f050c69dd18cf11260cca530866aaf6e0220741085d44bcc255dbfc4a8f34ba652ef20798f50ea350929196dcdcdf79685ca", - "id": "2f5f4923cb78e7d9aaa2b5028c16f3ced1448fdb6749b82711b46d469865bb1d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31239229496679, - "fee": 0, - "recipientId": "AUcXquR6CPooj4FPVQomhCjfhJik6KTzYp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e20a35c940073e07cdc048f9cab3ce5417ee3886790f8d8211b72d0d2c68ae97022018201c4a586e505c12d7eda3a6a8976fa4009bc8f10e11debcbd6df8c92bc59a", - "id": "11d6023327daa0bdecfc7f0db4b7f476270cce07d1442af2d81282004841f302", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31413424826151, - "fee": 0, - "recipientId": "AdYVznebAKBjBGhNuKeKA4zzXLFJBxX6Z4", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100ca7de35722d5a049d61c01a8bcbd7df1a990534e589e5890d07cf7ae6181c0da02206ce232bf4f5d2e144e0aae9001e0f64f38b462720be5417ccd4e3192efcf54dd", - "id": "db369c7c18acfacd9d702e63c7a77e8bcb07d4e104ad8fa39ffa37489e2f1b29", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 31703556507222, - "fee": 0, - "recipientId": "AFumy9qrTPRdfjMq3Q71CyJGADVURsQUyS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220082801ddb5cee6641ae199fc46cdab4d8bf945a602b60099ab0a6014a07f1b4302205aae82168430ad6632563426a4b6efa855d7ba81773a50894427dc9f54832580", - "id": "1ec3d57963cfe1528f5032fc045c7516f2e811ae5b5362a5e5e98b887abbdd22", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 32988434694317, - "fee": 0, - "recipientId": "AQpbXQB2G9fyoTsc84PZKtWeTANaXg6USH", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220118c9096756390292e7fadd471894e27610985552c8a69abc091abbce4426527022073bfcdb62d459942d9fedfc00dd3b7f473a2b7a8f3b607caddebe79788732adf", - "id": "f5a02075ee137276ace1af818d4e40826cc57ca42a28e2f4966008114f1ee3e2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 33301358574530, - "fee": 0, - "recipientId": "AGv459sY198H3RyLRwSUNUNVY2sdHBakyF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100baa3c39d1f99a14adadaabda9abcc74a8eb0fabf4478ec84e44c4c981194ea9f022026b3d6ed87d29120f5338fa371813700f08d989f8473f4e565e10ae7d7f3a5a0", - "id": "f85ed28225d31a5e12d1390e288d870d3fda8c31004435e7b581fbef143777a6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34838200000000, - "fee": 0, - "recipientId": "APz3bVED2boFzmM6X8Ri6EptTuwYecymNG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bc5e1636cc1a3a20189320632b53d9ca4dd4d6ed236cd79bc39897cb32ce1b4e022012c647f2c41f7f7be18989d37b837a1c597b9e8d9db34830e8c96aedf793c36b", - "id": "f762656f41ede6c6b2248a567e9338fa5fbcffd6e3acfcd166b3d7091dabe45b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 34933049632938, - "fee": 0, - "recipientId": "AGnmhUFTHK9waUB2RvPKcGLBfxRow1Hz9e", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201feb35774ceabcbfc577b257045b93395c3eb0e81d91cf4e0799d0c74fa9db4502203fa395d409e2aad11c3cb7a0db6d2904cda3ec8c850b810b737202a9717d90b1", - "id": "9f9b749ef794c617b2136b392f09d358f27c1c7ae33d917812f777405d9ca61b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 36753764290883, - "fee": 0, - "recipientId": "AXicS7Jbvo9wCN2uVa7RfBLrBS4oPHXvmF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a9d7ffc30ff5da829ab6a0705fc673c208fb1d90b4f37865fb859fe32771425a02204dd95d698733a1cd9ea1ae28044186dbca88e643f740593c4528e2a225be7656", - "id": "1f308b5f48e5396ca4b83e14ad69a329a486d695ecbd6819be782db4e79ef161", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 36753764290883, - "fee": 0, - "recipientId": "AMKQ7YtVjAHt8mWniZsvj6rqmA5jzu1rnZ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a75993db6caf823dc6d38968f205c3fec9406d2e215791a62cbe9951bad8677202202d0ccd24e0c9757d557720cb40182f5bbbd4f17b756aba16289de6190a2e7205", - "id": "dff7c0fe0b3548e5b68bc7705dcf7a33660bd77cc5d66bc1a5445f1ee5eee757", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 36827565849579, - "fee": 0, - "recipientId": "AdUHt2sqD22q3Jhby5K4nSVhBjPaBDrcN7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008dfbca2daed7bead47d885fd3f763a75c4b076bfcac2ad209987a2c0a2ab913c022019fdb82528d253b78768f8b8e508da440fe8f2f7a34ad9aba2c37f91df76bd01", - "id": "22b6643f0273b7fba1e4c4095c245ffd0a59f8c75582bbaba951f0f626fb6b67", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 40000000000000, - "fee": 0, - "recipientId": "ALcLekYJLkWKLwEX4TNsQszu8nfQbWSkDt", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202a594df3905ee3f8c8db32a69857b686badcc6784b698f8ad1ffcbdc5cb5d2d0022072a8e72f42fde11f54bdaccd48de55ed2d3046b1f426ddbb560b97658a0bc83b", - "id": "08a515e48fde2ed1141f3b7c6726284d1ae7b5ed1dbef1742ec43bf52d229eee", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 41574092648018, - "fee": 0, - "recipientId": "AGctmMKFHy54o2pzgeh4ESHfqZ9B7zMnzA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009ed97e274d06d1458aa4d0860329931ba6e3a2886f45ca9f9cdbe46f089d6a85022068a1f3f8edecbd96d580148602ea10709e92655fde424d68fb8d049d8841c418", - "id": "afc119b8eb143c9fffd3bfe31a39d7e42497770bfbad4ae7190b5b7b2001c9aa", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 42101522494901, - "fee": 0, - "recipientId": "ATAu9Q9UEQQua2UvXfQ6aev6eZAqWmSNFp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022060955d98b915a99a40fab60a89c9813d050550f17ea432f309c62d1e4da4f80602205685972723f6a5223466af3fd20c15afb95938182e7b2a636a32f6ab88cb4628", - "id": "bbb16d3b87ba67e0ad32da5eb342cc9bf60b51825b11d2d435f94afcc4ec42e3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 43098358629104, - "fee": 0, - "recipientId": "AWbC9Wq2LyJki7J84AkTLJkzguF9mo89BF", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100fa22327b9cd6deb3153c39d8f5d76739754798733aa96fedf2283dfb9b1ba055022014e16da33b966e40e27c883b314341391b7f85ee3c1f272dae914533ba540c3a", - "id": "8a87ccc624c279197648fca4c03622ca87b4dbfac293377c260e536e6ca969f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44103929088831, - "fee": 0, - "recipientId": "AUJWBJDUgY52u89unPzk5oV9e9xge7P1Kp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207069312fd56f16680a836c2a45772777be38c18c6f86287f414e786b8f2095d202203f646bbd58f31c2618c31649f1c48bc1f4ee3ec791a1805d788497179564b72e", - "id": "ba49cc0e31cee0207d2f0211262dfcf9787a314f0ce63d3c292f8f20c596a821", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 44104664164117, - "fee": 0, - "recipientId": "AUTMnQD8ANkE1wtMnH3aoPCrYkVntiaRDc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402200bdcfb494ba5f808db740b6535ae3adf6719d3442d618e027314833aaa3fd7ef02200cd5039225a23be87af57866b6de9c0e005a643fdbb9d94e2bf0f1bc9eb4227e", - "id": "954643f48700f6776cecc0c8d28edd052b6ada807c4ed80d4020db027f8302a1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 46581295003558, - "fee": 0, - "recipientId": "AM6bDiAfJMXdivdUiGaDy984KA97ZEcKWq", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f8bdac69ac19d78a7db5398b5bc643399689262446788af4805d85766e705480022066cc9169d1a7f9f53351ee695e2016ca5384d8f7069feebd59e8fb4a9788559d", - "id": "545bd5d745ede704e8c2a8d69a2eb6925610f37e7fa79ed9483266d860c7f512", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47047758593474, - "fee": 0, - "recipientId": "AWvqcwdrJyDEfsbveh5F7Nh6F1KWD3KAHS", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d41cbcf3488a0246488bf2cf73bf1e4e797260d1a5250ed8f04957b31586b0fb022036ded1aad7477562792bb697ed03f86d158ecc8dc325f37ba3efbfc2fa7c1fa2", - "id": "45838af24957952037e376f81200350a6d795b8435613a4845adbec9dece4d66", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 47594373941014, - "fee": 0, - "recipientId": "AJUkMpMFt8Zny1v1rENN9ZPZYitfePBvyz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100c5d4aa1d91f3a0b7468a31097fc9613e9c3830e1b3c6dc3926217442fb605f6f02207efbbe01ac68387e6fe0719ee635b4574d7040595d086d7030f903070f7c3247", - "id": "bb58a2aa2a8048b937156f3618663c1015f1b9ae7527ba8a0bc351c6f0bda349", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 51199117149060, - "fee": 0, - "recipientId": "AZaAZmaQgY3JaUTK72WUJEp9ZEgQgW1r6z", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220060cdc83a40e3ec69b3359c304a0ac46d863617599e2de03fc9a022efdec329f022018cbb65aea9d099cf6cc372a6599525b1aaf8a9cba53edbc95235764d265b3c5", - "id": "97d3f0573caf2552f8f5a696bbdb7f95782b98e5057f26ff7eec8e152513a3d4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 52274142846490, - "fee": 0, - "recipientId": "AMRYJaup1hTjCsV79HCTn4ySvhAzb8xSo7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022070140fe92e3dcf59f0ae1a87bbb061cc4048a8489d47cc37a3f3ef489548dc2302206163dd3630b960269613c98ca886637f32efc495f6c4e593cdb8c91dbe696c44", - "id": "223bf57b085d5fb463d106ae666648acfa042952e7417e4d22f51a100e3bbdd1", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 56564297142251, - "fee": 0, - "recipientId": "ALy8rPTjEELHQbkkDfsQTWT1M4s1Q4NFDX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b5f571f366492003d3c17ddbcdafddee27389907a33cf348f7ef0b4d6f4cdffb02207300a2d680dd5ff2fe2fc70d71e76c875ea15572be63fbd583a4de383f99c6da", - "id": "5333df7856532e4d8a13adf69376b48c986a46f773a3e8677b5722baa45bbda3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 56945445022471, - "fee": 0, - "recipientId": "AVRGa6s7qAzChqfXrbJEhExzrTo1uscsf1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d41baf4236b93dac1d8604596cbdabedd8081b8666d239f2a193b69e56acfe490220046d22d267e480523194aeb5890d54b5e161718ad754321aebe61a5a9a6a4947", - "id": "f466357fc462681688413511091b23148fa0271b2045dbd8b28aa591db29f850", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 58952449862348, - "fee": 0, - "recipientId": "AWeSiyLTbvHyfqmUNNcRZABWZkcd36oz7x", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100d8f5f18629c666ef1aad569544d581c9c6fe2beaa751658389ef260559f946ff02202da47ce0d71725b3004c43de69c0fe0a5fc94dd7296d16b5f64c2fb9ce1a515a", - "id": "edacc4e6c26a064c04742503524ac91012a87e77f6bf7186b4f591f3221b7b58", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 60406501374583, - "fee": 0, - "recipientId": "ARviCJv7BdnDHCxPgFa2uqRLXNNtBqc8Jv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b49e507c020746c9f70dcd26f52d071e05bfe9dbd303d528812a700883f7a0c4022009f6cbfd51b20e464451a6ebb5184c0abcfa758ca304bc88d031c8075c7d6902", - "id": "daa89776995d4fa94c6c64cd06b1c2d1036cfc9082151f9d3a70adf948c93939", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 61218300000000, - "fee": 0, - "recipientId": "ANLeC5wGgtqiDQcX5pmHiYayqMyWy3AYCQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022049a07caf745eafae488a242e5066c8d2220b99ec3127818ff9ccb2faf682fee6022072aab5d565ac05759ebdf6f896a53c584961a6c0a362a321cd0efb2e78cfa9e8", - "id": "789e6d917e48fa9a4538ce5948a1c4013c189d080b94275eb975299d3d22177c", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 61966258534201, - "fee": 0, - "recipientId": "ASaaFCmPG3yPUwngEFoPA12KnQYqjgVKnv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220449a94f1a85146353328281b937a9ce91b5644235ece1e2046a28d1199fc7f7d02200dd68146162a869688f44fddddb990bd2edb1c4194aa8c1edccd72cb1dfb7e03", - "id": "e09409c50d3ab8e86cec1216a5a8f971a8830c4bf6f847fa59c7eeff606ee9f5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 63613272588492, - "fee": 0, - "recipientId": "AYpQxRNbaiEL6FT7YUNmjNjRvpUfvRh6xA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100953475d0a8a1f5b26eeea7d4697faf1eba8f4a4e465b387f6c3550407c2adbef022047a7f4af1bbad90c076617319eed901196e46a692131fb1c8861c3ba2df2a23c", - "id": "311931514d05d2af41ba127a24dbc950f7476f78d9184469947d4b3b59561c4f", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 66966172832127, - "fee": 0, - "recipientId": "ATVwQWf1mpyRa7L4BAkSUxz9osCfBsBopT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f571ced76143801d0760e47fdb4583bdd45d1dc46d03fe9195be5cbe0a9f44a402200f6a4cd36111e9b5dd06f966e3ebad05294ab03b5be9f6a231665ed4ae53a275", - "id": "28a7145ea1920ab8de9b6037f050627ffb2e37c9f9f1d16be8db7110f401fbd3", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 67622306078799, - "fee": 0, - "recipientId": "AKzAriWhTPPRXueXuSttRiBLkjP7i5wKpQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100969e9376ec702a41b6da148d02603e8e1b4c1bfbf52e060aff80a89cab4eb3c9022076972f9de9cc4e883c63ab263cc76df952b0bc55dec80b958fa8069b94046f12", - "id": "7a6649786a864355ed73e0f194caab15593a11f93400b13ce6d0b7030057d903", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 80563884771309, - "fee": 0, - "recipientId": "AMFQ8md4BymLJ2ecw436fhyUyjyRM7ygzz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220188dd6b6c7e1c09b420ea73ecb17db20d85848342cf45c65192c77f5390fded502205eb3a4304dd2ee62ec1fa9e848dcf2d695ed92162e13b52271e3430ebde7eeb3", - "id": "fdeba3a56077063853cfb940d036d3ba75e03b6fa68d45de52bf58d9165c4b96", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 81926947255471, - "fee": 0, - "recipientId": "AMMDbSvuBNCi1oAXvqgBguAeTehWRTjBrc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022043e6c8beefe6b5ab949adabed4830fd87d4639a6afd7fe8e1c3de3087f7f47b302202cce568aacb5bc2a416a20b6f940020b2e9c1f0404264230bf1b9c0086735ea7", - "id": "1630a5b12b69dc1317bce4ba97a2207c6bb4ff8c9e3b5af88d02f4b42829d51d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 97831169789473, - "fee": 0, - "recipientId": "Ab6JpVxrytQhj4QpBJ3LGsLvEhVmgCKk6Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202b44c2bf60f8af0927f9a5c62fba3ceabb58e5944dedfbd37cef6368b219225502201c1074fb7303b8a88479009e3a36dda95d6c8d0bc60770bd7f12beafcf680c0d", - "id": "0c8a56b4a0aa03b2f49dce2fd0e7f367096065a51128950188b553b8a750d4c6", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 98443286091931, - "fee": 0, - "recipientId": "AMQEjpMh7o1sJT65Pdm1Wt829evGsTWCJ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304502210082a8b45a0e351baaefcdddee3583b1b5ed7af81fdc9e2aa7243696388a5d6b4302205bd1469c96a3494a9b0bc8cc4513cb3ca57b8cf3b3250c028346d42666daa02e", - "id": "564f7c69a9cacf987aaee9ecb5bb726d1ae26a26111f0f8364611984d18b300d", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 100568151043750, - "fee": 0, - "recipientId": "AZdsyiib53nWP1wG5wara6XfvmmqqBWxWR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100da0594484575e66f1eb34211a7d3249c15d0eb422be3e1b46ee364c4a377befc022041cd9cd4237cc8a1a7e9359adfe182da7edb24a69368ae1deb7e2fad26aa0312", - "id": "16a95bcd5ca6388de776cc01666f616f863f9dced646badfe97b53a266985bbd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 110555322986976, - "fee": 0, - "recipientId": "APaRmUnSummHSAyHnYqd1BGvPqgYReEuWG", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100874c685c11233280806221994ce6ac3be8ca6f2dd88421991a1c9810614e9bb5022071ededd800067a5a3e6828ab6cec496b153c9331e5f08a417c09e583b05219d7", - "id": "a387aebbc8fefd8f0963f2ffbac837f5bed99137b19b19c356949bdf12c0d0cb", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 113717099151927, - "fee": 0, - "recipientId": "ALC5AHjaUUSaecu9R4wADbKdqzqf9U4e19", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100b5b9bf26d7020ca50abd010624a7443e7e4d0c5d17c50aaff12c2a357380a212022037b7e9e80dd9e5a092d99aaae3fe0722ff6e085b8ed42e4d47030e9497e6093a", - "id": "912d5b497042a6da16f4dc3fb0b7711f19aeadd1ebcb5aea32e2e49d6b2bcde0", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 115371683274712, - "fee": 0, - "recipientId": "AVNXBEcnwKmirrG3kcZSzHa6JvRkiCKqXc", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220401302e2773dfc0ef1a6b4b013d98fcbaca1838e9c688013989ad990a7eb51dc0220268eb491d53660b33ace47049d96e4c97dde689621044002f703425e17a3cc3e", - "id": "1dab66009d3db2e422824d6af3e727ff146c2b3bfb1daeef583742a920d51908", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 120262700000000, - "fee": 0, - "recipientId": "AHHpmXD7F8j1r5bftPMkp35ct9qGYiupwj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100edfc8dbb54abcc1096194776c222e08fae1e125851b580f510ba5b6c6ea39f38022032b48c581a123b298838dba4a22b71ddfc51c0514f260fb07dcb2374bbfc8e92", - "id": "5ffd7c36deaef4295257fec15f27e451864538aeeae9b21ded6d4e579f19d7c8", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 123626090911753, - "fee": 0, - "recipientId": "AczFP54nMoafRABK4c7idxHpAC4RwmQsDE", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402207a3f7c3ecb0295f08a85e12af23156e0765ad1540047f68cd89be139307b5bb3022072be91d55a080d6e5fae0a327225dac4c63505b292dda6958a1207f9adadc8c4", - "id": "03b08987233a71d17b296c057e03821022e9b504b47471670ddad11080e1f4b2", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 125719320015809, - "fee": 0, - "recipientId": "AUq3S7bXMHmz9zBbz8YLN6LrSrshvxXTU5", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022033d2d418dae0c1c1ef89b1866258410008eb39a409c98f6ec3a73918f449ead102200ca603311c12cb4a5f050e0872e05a0e8f2953827b168b85211b179600154585", - "id": "82b2c63e0139fa52979e008e6a3d3086efc4aaf5e1799c57a29bdaaae9a50189", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 126273901505716, - "fee": 0, - "recipientId": "AdCXBbcpNrnMDHMar7Ebjxu959uwN2jt2y", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022078cb591e525902157b5736e798461266a2c7e32d7aa4d06d07f80d64244e0a2602202981d622da3539fd16efc0aa0ed8503ce2470ce8cc3235cc325904b9246dfb1d", - "id": "367b31d00256d8608bbe3b62f350bbbac10d896a3a3cc084e203493abf6f74b4", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 126299500000000, - "fee": 0, - "recipientId": "AeYkC4MJ24EF7yVkb8NfSryBw9xrTvSJ5o", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100f3fc2ad6305967410752b664c2d8bca041d552c013fa576e10889a69a6c4fce5022067ecc8de665c05a1594067f873e067db5e1884486d4815bd1cb3959c282cf0a0", - "id": "af6c2725034e35ca7dcf0b20a9161af2ece9d9e30d1d8f934661c2e121844832", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 126837200000000, - "fee": 0, - "recipientId": "AXx966jfTHUz4nSjZsFEKkpsgNoByhuMJo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30440220025e2076ba6db9412d886925b4ccf810ff5aa45711ba9c9f049061f675560937022042ed7606e3f46bc5fb5b2ef2b36e894666c78804a1d95ba7916d6a41f92e36cf", - "id": "e274c6833dc8021404af6782020539329acda8f274ac2be31535eef95cd70668", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 127928194702051, - "fee": 0, - "recipientId": "ARwU8mRM9aM4KjwFLpNH8eHntj157LNAW8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022050f8011fa9011b5b4501bcc1d4154864b6c5125e9a00f468a194e59edd32831c02200c9f77d38533ceaa8bbda2bbf9ddc73a7d6654d3d3a338a3e04a80f67fe16a59", - "id": "2debd1afcd9cbc2e3c57dc27258cb49d6ef21b10d36648efacc0053acf7cba19", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 128450700000000, - "fee": 0, - "recipientId": "AGto2VECRE2tGFr8X9pfhqHNgF2bdW5mgR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100aecdaf102f6d5a4b18ddeb17c8d5c00c18cfe0ec6382df8c972937a17e9271e0022074d6bf200487ee5c03800c2cc28e571bea6129c95ebffc2a1a8435fe6bb351d0", - "id": "7477beb697266fa054ab120b1381e293d8533d1a56eec29731525b857d05e8dd", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 136497083929386, - "fee": 0, - "recipientId": "Ac15ysDrQ3fvMYL8S2u52VSwXZPKZQs8MY", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402201e2a494e971932480c8286c2c12748daf90afd3234969cd720ac9346da94fff802203fdeef1aa6daffc70709e5c86bf53fec3169651671060b8e495e1c0c40c725d2", - "id": "811f46c52bc4e94cebb2c8a4053c13c3ac3f7df24134cc027a37f7614e935a7b", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 140099329287361, - "fee": 0, - "recipientId": "AZB965rP7y3PUs8RXqpkRy5Uu9dbQgu8mM", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221008fdc7606464939463c4c48ef06663b63e447db9b560fd323efe695b59832e792022048631bfebc37ff98d5d5089f607869eb05b86119830da14817cd31125a4910e8", - "id": "8bb29e211493fd3f3282498d749b55d5824e9ed9d5813a834258b002204a2cb7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 150034082630911, - "fee": 0, - "recipientId": "AQK3Zu6VW2QMh6a3ckNzNsYduTV1MmDkyJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "304402202d42c892d4cd76cb582573977a7b2a294a945754dbd3de4411fe3a3ed814f0d402207c49588af322b49914a933cda46e2ade762f5bb9b9383cb690bded2f0a734d90", - "id": "7e1e33f30bf45a2b2b7be555693894171481a7cbdc48397575e6a99d98382138", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 151578069939920, - "fee": 0, - "recipientId": "ATUe1KJzRsHUW6rA8UnNNNo4yVEF6TqyEv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100e97f8bccc9e3f5e3c3a5b32987c64caf4b60faf4a282784ab3b1ade1579c3b0c02202041fb2375eb061fc3ddb64cf94edc614a690d6fa9e22e9964aab80a66eb5b34", - "id": "be993fc90ee11f5b9bcbeec1a272e94c1894b65804011f76d0d3227c71ba19e5", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 191573850243351, - "fee": 0, - "recipientId": "Abee76TtVambNDBmTTyoCYosWQ13MC9XEn", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bca5294ba5fd28e35afb067b328aa2469656158f4c8580d98bfea76ef925761102202ca7d152501f2999169ee65cac56b25f41d9b140d11708d236f9c4be972b0dcd", - "id": "2bec691439fdb6fd9a324f26d44e2ad64bd9e2ffd566d220e01c4aa12028fa91", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 196019568105246, - "fee": 0, - "recipientId": "AX1ZCuzNnVHfPvKikwsnsNRUxXfKq5g9dv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3044022011856a7a6cf61478d183e18a4a7404241dc561629d7db7ac70d1ff385e85d724022003404898de3709cac4bd5a6aa718c7ada3c9d08219c60f034a45da8135ef4a51", - "id": "57b80107cf40b39135241aa92e7e254da815763735f0ac5a9919dd8485865d80", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 225493764614631, - "fee": 0, - "recipientId": "AbYwZ1DAUDLgJwusR9bCZoTfuUTwNq1C3c", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100bad48978061d554290cd1afd07851e3101aee65f3b7f9f677e85a28d6590df5f02204d7a721a90dcec6644bff0aecd2f1d5b506c334bad8810b1b2d123b6978d57aa", - "id": "5ffc47d5a0826f031d51f0502e45ddb9d7b8c4f4bf9b2d4bd7a60c4ab027b541", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1034744897737528, - "fee": 0, - "recipientId": "AaFgAghGAmNWndDnaxkbcYFE7fxbxZn6dJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100a4489ad919a05f73c3be4de717a3b23115812c146caa1f33301485e1d9521dbd0220487441651432d5b34bbe7f383ee3a1bdf84dd22b82982d9566020a6033783685", - "id": "99a3fa3f91f257fd3e3ed284a9bbcc5eb007ca7e004372a5912bc3850d5ec9f7", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 1256766795179826, - "fee": 0, - "recipientId": "AUhVLSiqTnnzqhxp5RKzheuRyvh58TSCUR", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "30450221009e21a29b8428afdd7f140379d839d5ee75191c3e5add0582f58b9a3b2b3d69e30220749a0e7a3a8c63e877f1bdb0ec12987db8b138bb1746efe0b18d9ee817276cfc", - "id": "ac7ce9c3dcbee526a9f15af016fc15b6dabed5f8a61997c4de15fcc987926234", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 0, - "amount": 3141438227793004, - "fee": 0, - "recipientId": "AN7BURQn5oqBRBADeWhmmUMJGQTy5Seey3", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", - "signature": "3045022100df12738ec2164ce2d2ba980ab8499dae066149a1019f37ae5b17f28e9cc2aafb02202a0db97f9e012f2a8ed819d2cb02e5d9e63fda01754d6f42308c3cab4bf92e1a", - "id": "5222124556517be676fa67fccb07fd581ec2928140a72afdd65259dc3e5db921", - "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "033c9e5a710bff3131b406a8023a60e6b76a2ccf937cd85b56add7c4a33ae3090f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "033c9e5a710bff3131b406a8023a60e6b76a2ccf937cd85b56add7c4a33ae3090f" - } - }, - "signature": "304402205532bc3fe7be805b4c8a0df2995158e634c7146c2467d05d75f754782b87bbbf02207164df69b13c9c3c46bb3c7d027e7ba81eeaf726f25e08d268c1507da4581b73", - "id": "8cc98e02422afa8a246faa66c1672b62996be356735b00614d8739ea3a5f73b6", - "senderId": "AQaKx7UU8857b4tJij1jb7aURUzd3GDyKt" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "031de61bd525abc902396bc66daf513eb20715180beb50be3a1c56a36dc73476a8", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "031de61bd525abc902396bc66daf513eb20715180beb50be3a1c56a36dc73476a8" - } - }, - "signature": "30440220125d7a0f64c0328590d67dd27eb5d8f209bdcce03d730c7469ab25a8943c8b920220027305ec4ac8d46b0c2e711274159ab2fffec7f5a5994aea775cd48260db93b4", - "id": "bf2641e6ffce5848282027221c7f43eedbd0b72a7ee2a56cee1472c5f69f194c", - "senderId": "AU9BgcsCBDCkzPyY9EZXqiwukYq4Kor4oX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02614722c83a05de882d887ad51bc5c687e747d6d19b58f5731d38223c58bb6ca0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "02614722c83a05de882d887ad51bc5c687e747d6d19b58f5731d38223c58bb6ca0" - } - }, - "signature": "3044022078d5f10573deff4cf16ee4b253c6b985b8a16e5f7f4840493e2834809e26ce6f022020d6502389f7512ea3b8f9fa9ac296c153aabce8f9066cd42d854b94069f6f2d", - "id": "3625b2591f15394ea0dc6b26a113284b1b56de0cf3a21c3a1dd92f373f796982", - "senderId": "AJqbav8MPPAe3zdzo1f471gaciQbx1SRe3" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "029191a8ae0fe0561e3ddce562554e8ba5ba36cf9ecb389290a1a74acdc53ca89e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "029191a8ae0fe0561e3ddce562554e8ba5ba36cf9ecb389290a1a74acdc53ca89e" - } - }, - "signature": "3045022100abd4ff196ac1fb56fdcc0f0b71a49ceca5da07b3631570e22626fb43a415a9e2022042d32e0fc05c6e2467b9749ad1f6bf5e1fb9cc8eb5c21a40baa1381cf58fa327", - "id": "05c1fd17a20d62c2740782097261b960f9ad353f68b94d4a13dcab05d08f9dd1", - "senderId": "AXArbuCVSiRuYBkzCAajboxCfNiw8AyQX1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0204f8aca74a87f69432298b13555cf50aa0709e1a942aa3efb447620eb78bbcbe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "0204f8aca74a87f69432298b13555cf50aa0709e1a942aa3efb447620eb78bbcbe" - } - }, - "signature": "3045022100d15eb20b58fb9a6e7659706d20c3deb26dc176cdaab02fff557dc5a8d65982a3022064686cae9101510295993e1d3663dffc88741bf1e2781021e28a7b2aa71ba80b", - "id": "6debf628e330726bee0f64d9fe7c56fe4024941b4394ad5c7cb08fa9a3abc0f5", - "senderId": "AbrgipEvLp1ZHNJ1GcWWAsCLNgSgDG6Lxh" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c12428bb56ceb96e9c2a558e045df6b7b2c551fdb6a132ac6c3956d10f479f52", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "02c12428bb56ceb96e9c2a558e045df6b7b2c551fdb6a132ac6c3956d10f479f52" - } - }, - "signature": "30450221008c43c6f251517d972386adb42d69510ca59d882a89d7fffa55484ad7989a3bbd02200925d009f1183934e4be66ad48ce7149c990d0d87afe629c14551ed34e54ee1d", - "id": "557d5fb9b8b948ba2a6fd3d4dceb476e97e61faefde8eb11f4372028346c5aca", - "senderId": "AbyacFxcWS1JsokdRCx8bFsWP8f48XftmS" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03f11ddeca3845ea59c82120a7eca68558c99e4e7d373142ea0554408c58ca82be", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "03f11ddeca3845ea59c82120a7eca68558c99e4e7d373142ea0554408c58ca82be" - } - }, - "signature": "3045022100f04fdee2cc09b029d1bedea130174f21e24e673438655e71bd61998732debb040220439b36ecd46cf6ccc9c8a8fc44085733bb82427a760ab87bfee1166c7623813a", - "id": "6d8c04b24c24c9680b4e496b686c2a37cc05fcd9e6f2090cc945712199b2b8ae", - "senderId": "AYLTbdiYWHvPvJo5Lh42MEVS4Fnepg5vgc" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c134247a5d54cd30a5e3080daa99c74f54e21ad0cffa60e7efdecece862898c9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "02c134247a5d54cd30a5e3080daa99c74f54e21ad0cffa60e7efdecece862898c9" - } - }, - "signature": "304402201820565556e3039944c8c7240c567c5d8f96a8a34a05dc1d68577883a8025e94022011f55c1511627de25b4cd7410efea22ef2df50b3c1f4ed55a8ce8d4f3da91862", - "id": "e606ac6acbfd96fc4054131061a69e3d3f58e044636a6646227775c4e646a126", - "senderId": "AVi8PFHr5jFBFGWSissPFDFXUPABuBgxSa" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03afac7e796d781e17600945010be34cb6760fa919be67baec2cfb691cf4ce5f33", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "03afac7e796d781e17600945010be34cb6760fa919be67baec2cfb691cf4ce5f33" - } - }, - "signature": "3045022100bd7e5550aecbd212d9fc13842c7ef586b146ce6f289199107af979eac6b61fe402204607eb502c9c8f2363e468e22afccd754e042e605199c92a93a0c29a19ec628a", - "id": "2cd3411cf5a52c717afca1f42cf97627fe81c0869f39ab494df0ce6e99f7e485", - "senderId": "APXkAdLbLiLJDC9Ls68Y9a5ws3PcgmUDAo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032879c56cfb96539f6a8c03a91b4a987e35973a79e62f9da438831b353066a84b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "032879c56cfb96539f6a8c03a91b4a987e35973a79e62f9da438831b353066a84b" - } - }, - "signature": "3044022060dc174d40828324a18074db079ea300d382ed5c2f27a7ff782686e7e78002fb022072e809275cc872b3c4b430e3094268e0260aaf67ced17387ee17a207a9e2032e", - "id": "4b16c34911fac46716cf8ed383401f70657201763b54ecaa5904bc09fddc774d", - "senderId": "Ad8UiXMZPecuNGwCXZTmF8Mysn1TchVVTf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03bc7be5c8b1221beb63f6ed23d4b5e2748b64983087986c4953579fa7d9022a71", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "03bc7be5c8b1221beb63f6ed23d4b5e2748b64983087986c4953579fa7d9022a71" - } - }, - "signature": "3045022100c8901dbad46f6fd0ae9db39c7be5b002bc871906a80181ae45de6fa67eb4068a022068af82d46fd008c8efe5c3fe265313e01aca9262b1160a5c095c6b31d97f1579", - "id": "2d808f28b223999c1d652c01b2f22ceeca15d1abf2acb42c04570358ed176913", - "senderId": "ANUfH6C3Jjp46pDUxWgeV6WUbWCagaVxM1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c5282b639d0e8f94cfac6c0ed242d1634d8a2c93cbd76c6ed2856a9f19cf6a13", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "03c5282b639d0e8f94cfac6c0ed242d1634d8a2c93cbd76c6ed2856a9f19cf6a13" - } - }, - "signature": "3044022051a8aa7906358589161956d1c182ef9ee5c77a144c2d9965eeff043ddc3b7a6b0220797a2475b5dcd8f0ef7a24a0fbe3d0185839e33f313b93b566b82be168d4ca33", - "id": "3f21a6f499292b9bf0c2970cac7d78953a977f7004d29446698c7a29da656fda", - "senderId": "Actv8ebcwNsbfx8MM5k2AeJidJuLL7PotT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "036c474d6b22e6d7c2cf054721ca73d0e2a904135b49bbf9dcc7ed7ce9718984ed", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "036c474d6b22e6d7c2cf054721ca73d0e2a904135b49bbf9dcc7ed7ce9718984ed" - } - }, - "signature": "3045022100d3ab1ee59bd1a45bf936867661f9fa9b22c161506bcd61ad17ac871ba4a17e130220114d37a98120cd1f2465c9700089101db7748ab0562561d4e01ec98c0e96750f", - "id": "337968b8f3e7ddb26ff3e6740df0b12c7be6bc427d9d570bd77a9312baafc80c", - "senderId": "AR5z28tRUnvaBWC14KSBpvNJDgsVCNtagq" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "028bc0f094738f7699ab4432d13e3c18e09a462dca06960a1fd0eee82482f50be6", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "028bc0f094738f7699ab4432d13e3c18e09a462dca06960a1fd0eee82482f50be6" - } - }, - "signature": "304402200cd7121995da2f8fb24ef2aab15b8da97b050ff9aa600363305f2c0a0c8ca72602200099ab565497c84319dd91a34001b16a92211248652ee109fd28eb90112bf022", - "id": "061e51e451667ae7ea59e0d1cc66fe47700d6bed1dfa671efc028a2b580b71a3", - "senderId": "AbkDPAUDsfQJgvHn5g8AisDm9XqLAtFFai" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038467e1dd4d138f007f70cb09afdd8439ea744b282cbba2d76fee8463dfd17e2b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "038467e1dd4d138f007f70cb09afdd8439ea744b282cbba2d76fee8463dfd17e2b" - } - }, - "signature": "304402206772c6c4e2f58c1401570fb76ab87bf63865fcf32c5f9a5a3f88f4360f44e346022075710eb73a4303ecf5eb899019ee59c9c5ad206b6da902adf90f3df02f108512", - "id": "ee5347ef87ceaf28aefd8374f686725b8ffbbe55042dba0c59c625e4c822fbf5", - "senderId": "AbAeJ4YJU5rxuNtXpDn3E4W5E8UNHyc26a" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "023f425c88cfa289c28e5fefe120033c9beff72487dbc4722e900d6da59943b8d0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "023f425c88cfa289c28e5fefe120033c9beff72487dbc4722e900d6da59943b8d0" - } - }, - "signature": "3045022100c185d867d976dff795ccba4654199f1b075bfd3441e045a2f6ac64c4fd0bb6d702202323d84585da2268276d6cce663e842ea5f8617e5e12cf4a20ed7436a1ff7f04", - "id": "592ee85d2922ef565fb02d649cdb2104caf06fb8747ba2425057f79518132b1c", - "senderId": "AP8agRcU4WmsYhC72pBqyfLwaDU6NYKUL6" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02d2a0fda621ac213ca51dfa8efe1772eab8659d8b8c7023a35dce48f2c88316ed", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "02d2a0fda621ac213ca51dfa8efe1772eab8659d8b8c7023a35dce48f2c88316ed" - } - }, - "signature": "3045022100da6c5c7945eb7dae41e7ed133a58e06e660fb8d012e9ba93df38b8b86a3aa7ca022007514624a32a5fd6f294bfd8d6fce6e8c75b20b7fcb8d3f8b82725c83fe43db6", - "id": "686baa6cda61403ce346c293a8f27e0dc3899eb4629d4b3bbbb9f02b41ea36bf", - "senderId": "AHmBqWJgxZfaC2azkyASdrbCxF6csE7Qxx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026ffd3c35c7ef08e3dcdde741994bfdf9cf6af75cdf9e6f4ef8c251b5eb321fd1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "026ffd3c35c7ef08e3dcdde741994bfdf9cf6af75cdf9e6f4ef8c251b5eb321fd1" - } - }, - "signature": "30440220192c5a3433384ea1ec3f81473f18962a179fcdd067359accac33e8ee4e571a780220549fb42c1a5d40daca2fc97aa8e0379e041c28fa524505c0e43760f1022a1369", - "id": "bf1644830edb59dea6ea1f3fa107ce84546d14904715b84bf2d5e9648ad426d1", - "senderId": "AeLV4NUMsPJW1nvHquBWPFVKCWirs1pshf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0295f1d78f3adae92f0c2d061367e0d06a3fcc9a838a725ccfe660b9c2024253fb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "0295f1d78f3adae92f0c2d061367e0d06a3fcc9a838a725ccfe660b9c2024253fb" - } - }, - "signature": "304402207720c103e2c3000d867ce3073f9c5375d9f3cbc69dc6affd08d72cdf0322dd44022040ef700ba6c235af8b2e0b71b3b8a825a18f276a192ffdabc01118e2786eb567", - "id": "aa03f6eff50359838142b70634991e93d48480369f087d73cecd0b93847233d9", - "senderId": "Ad6y8ae35QWkrtiLBpiXRR5Cj2KK2u3EGX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ac0a9348306d3dea50bde6b38791b2716f698044c207bf005788e13f35b46693", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "02ac0a9348306d3dea50bde6b38791b2716f698044c207bf005788e13f35b46693" - } - }, - "signature": "3044022070bf433e253fa69a33abf85559d804d1d0e055199c89abef5dba34e5cccf1952022023fd278d32b75f0465c777c39b0a616a4dd0a957b08e9723c1702dbf4d6a1ff7", - "id": "c64e9e47d8c7573dd97d2bb634eb7bcb141c156ab701044319e19ac0a7f55f3c", - "senderId": "AGb36aHfdxvqbMqoDCnm6wVkKKtqkE3zAh" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c075494ad044ab8c0b2dc7ccd19f649db844a4e558e539d3ac2610c4b90a5139", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03c075494ad044ab8c0b2dc7ccd19f649db844a4e558e539d3ac2610c4b90a5139" - } - }, - "signature": "30440220470149adad4cf95460a3e02ce0fd79d933a1612ee9d8711d104efb5c708022c5022077eb9aee6dc62bafcb4217484cb2b892d8ddac8ba2a170390438cf47521a8d3a", - "id": "1a7f292a5f3df9f9a3ec97d7abace3d398adb943ff59da7c0b0eed8f19928ec7", - "senderId": "AeLpRK8rFVtBeyBVqBtdQpWDfLzaiNujKr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02afe652f7d14a877e73f00cca6c836efda1a05e79c6fbd7f92b600a4cd519f4f3", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02afe652f7d14a877e73f00cca6c836efda1a05e79c6fbd7f92b600a4cd519f4f3" - } - }, - "signature": "3045022100a7aa2d57b6598379d04e9143542bf9abc4fb4899aff37ea7a7be0dd07c7317cd02201284a2101aa06dd7fa6e34006b666e4916bd76071032002302dde92610e04dc7", - "id": "e8afeca5ff2aa3ccd8c29f7249d93d465049bc31b3cd3fd6cdc6e09ca92ab921", - "senderId": "AW91q3n1QYTn3caBm7KR35zexcJU7PgMtZ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "024dd37755804e6dda0ddaff6691aa038c57d8db36d59fd1695a2519835a828072", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "024dd37755804e6dda0ddaff6691aa038c57d8db36d59fd1695a2519835a828072" - } - }, - "signature": "3045022100f5a4c4d258caffc712c9aa515f3e235b352a8d2250695e7570a14337be64cc410220446bbd588e8aab1f69c900e8472c8bbb61cb6b14aecd0e0d693f74714e8511be", - "id": "e77aac7c7d14794e86175a0b0e985a09dcc485c4e1117aba251dc3e45addf4fd", - "senderId": "AW58iWw1ATvzGHu338WqMYvgUhmvMMsRjF" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02763591456b7d3ed76125e4b9d5b34c3926f5aa166200dd270e45ecb6a6b15d9a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "02763591456b7d3ed76125e4b9d5b34c3926f5aa166200dd270e45ecb6a6b15d9a" - } - }, - "signature": "3045022100f6ee4060cfdac8c8a9dbc50b49b7cb59d4a587a5d14135fc7345594ac2b7e10102205594aaafae4f78415f01e0228b7233ec36356db4d73da92f6720502874bc1672", - "id": "4164cada2725a8e52f8713f8600b3d945bb3045fc956d79066974e3a012021ec", - "senderId": "AYaYHEKaJvLLWX2NgM8VXk15zSDxV8vCgn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "025e6a4c19af3228659e5aeb3638e78c50203d76a806d2e1ea938b79247a25a932", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "025e6a4c19af3228659e5aeb3638e78c50203d76a806d2e1ea938b79247a25a932" - } - }, - "signature": "3044022075186579fba3bb355622ca8e4e0470bc54e82ae15a16b5e1b16784b29087f70602203fabc6d8547b6eb07c59193e0b42f3bd6b5cc234ca5d415c791c9c5f4a4b7f90", - "id": "6aa2b74f026ff9cfb1e5e00a3b4a75f752f912337fdcb61ac9257d58fb406cce", - "senderId": "ARDkQVS4DbJnErrptyWSWdJD8gtaPEyRH8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03916b256a6ff5c51bc0b7866678cdc40a2d06477fd022b61f79383b6cdf487bcf", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "03916b256a6ff5c51bc0b7866678cdc40a2d06477fd022b61f79383b6cdf487bcf" - } - }, - "signature": "304402207143545435e31741050f802e434d9559c486dcc19e136e6eb68c7881959f5f970220387f941b78ecbe29b209993bae101828daf5492cd2febf256a87d157b97af8cf", - "id": "d6805135ece218f780e604673b9946b789d4406553a5dcedee1c06575cc7c63b", - "senderId": "AJjkVwkhsvsX6dVKhVxpmhRvz4CyXLEvQ3" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02b38e56ef8fb018fe914c484ea4b0aa18f1a938f46af1c394dfca40adf2771bde", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02b38e56ef8fb018fe914c484ea4b0aa18f1a938f46af1c394dfca40adf2771bde" - } - }, - "signature": "304402203222d23708aaba6f4904fa04ff5cd88c9c9aeb91771754763be9206d79f2d85402206ce18e2b3bccb6fbca2e024b1bb38ecd859c27986a0bc123bcc29f99b0dcc6b1", - "id": "86367f56d8b4a1bc34cf1790b3a1f81cf794b9e9683b347b7a5a5110e63b9b34", - "senderId": "AJrXd5u3y6FH5HktH2jgkLQHjgD9ZztMtk" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034bef7fed4f718a450ec4672533d74ef95039cda85a1a0ff74e6b2dbb9e220d68", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "034bef7fed4f718a450ec4672533d74ef95039cda85a1a0ff74e6b2dbb9e220d68" - } - }, - "signature": "304402202b33813e4cdc7f97dd41747d53542803cc7c5ae7aae56cfef3e4b9d85e74302102203854d993b16efbf7b21454bcf44873969f35d033ee93866fd2293841f866441a", - "id": "65303140bae99aef3d68b36379acd8b9f4263f1d90887efc1fe7f0735c26b84f", - "senderId": "AZengw5WND4WLC8JKz6xUDFwLz3yCKPpTC" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d48d4160457b1fe8d76e2da68f33860ad2520836b5ddec8af925757690d93c21", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "03d48d4160457b1fe8d76e2da68f33860ad2520836b5ddec8af925757690d93c21" - } - }, - "signature": "304402202c43ebd655af5fb0e7336fa41f03977bc5b4c05a5a6f335e544a58acb5f5ff1d022074d415ad20703c67fed90e1d0bc970ebe1d55f31a1a3dede173362c3736ba6e5", - "id": "c529cb54924f221a3aba98b204f82c033b67375a15d52e52671bb526957baa3c", - "senderId": "AaFxHxsjYyYCsZtpQwwYGvYESogJ2SHxe5" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0384e505b25eaac812f8acbaa1ecd09e2ca4b5d973210189a39dc48d2a8f908780", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "0384e505b25eaac812f8acbaa1ecd09e2ca4b5d973210189a39dc48d2a8f908780" - } - }, - "signature": "304502210099161fc12c6ea658f382dfcbd495c84961d1f8f029edfb6c2746ddca918418400220556ffdde284f10b5ba62bdf6a67139bc56f7d0e44e87c651cad6e8c2f646aefc", - "id": "bb014f5d3ba5ecdf3dba9949673e1cea6eeb3771f41bd88881aa4f055307e4d4", - "senderId": "AY5GwZtG9sFvDzMKAQfAD1Q8EHDh4bW718" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c3709dda58fcf7e26f94e865458b08d95d398446f67f465387be56b03ec36f4b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "02c3709dda58fcf7e26f94e865458b08d95d398446f67f465387be56b03ec36f4b" - } - }, - "signature": "30440220562a015669686e64c274be9823add0ce9da9bb8ea31e376c194b7e194c7306670220186b3482683572164588f2d89c8483eb6a789efaaf1945ba86b5333f23235254", - "id": "ee1933bf760161dfcce831d57e784e13f1e428bdf6ff59a0f06c778873172032", - "senderId": "AZvjdJSG5V4WnNjPaRbDNfLD72j3rYWqbs" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021c2280e462313d08fbe92bac59f43203cef4e21611d42747535ec2987a80e30b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "021c2280e462313d08fbe92bac59f43203cef4e21611d42747535ec2987a80e30b" - } - }, - "signature": "304402207e28352abc1eab6c5405cf78c2c1fe406912d3b08c6e02bf44877dd94d117eea0220144013da4977b0517f8fd8b7dc6838185e25d0f6f272af17125f50feced46242", - "id": "658f84d6568a7f48295e9f25281c6ca71c404245ece501cbbebb0c60efd08f31", - "senderId": "AKeDRRgG4TdDo4Z2iCzaBZS2UcvjWKMSrC" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0212fc419fd5f4c88f73b8475ea676f3800f4bc96433a074462d827fc35f4fd883", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "0212fc419fd5f4c88f73b8475ea676f3800f4bc96433a074462d827fc35f4fd883" - } - }, - "signature": "3044022036cc85f65b9cd55c1a336cc02c06cbecbc90a70314ed2848dbb7bb9cb7f7fad302200b217996c20eca5c5eaafcdc4d2a27a39d8945f55cd39322a0070b180e7a923d", - "id": "b8a1791640f64e4c977bc2c6f8afae412f6d125db9791db33d979fd3f607976d", - "senderId": "ANeLRbMxkSguPMq9CJeMgr8xZxwZ9mbqbx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02015ebd34b20a1c7bcbcff1d53e7d2bbcb2b4eacee494830409e1f84117b802a1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02015ebd34b20a1c7bcbcff1d53e7d2bbcb2b4eacee494830409e1f84117b802a1" - } - }, - "signature": "3044022023390c4ff70c6f02923b4f55ecd6ef41e93871b4e4eb21be80b5023f72f1095f02205c921d9051395cf7a4d335a00a89a9e0f0d7afaae699b371503d35eda7cd13aa", - "id": "fa9bb4c78bbcbc164ef4a630997199aa01b53bc93d49b9f3237efea13717f04b", - "senderId": "AdCa1oGLMRQqygEBGGq1AEtSV44CJ5Kbdo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030cbba2415349f8093539632fe26692411219c11e740e9a3e02b6c64415f056b4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "030cbba2415349f8093539632fe26692411219c11e740e9a3e02b6c64415f056b4" - } - }, - "signature": "3045022100c0e0115ba685768aa3aa11b9d0e1149a63824f72a486efe2dfa16df93d8f8123022023b207efa969e876be02f87ec8085da800750c127edeee1fc481fe978029802a", - "id": "4bfcd5ae4f01935aedb2fed2d509698902a054456f7dcf5fce02da02f1c7d752", - "senderId": "AXuVpX3tAewNp5YVWvXWv3etxjrMDgaZdK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03367a6969c0d62e9b0fb3439ff2574dbacd5d616cc57b08f7c5417d4ac6e94faf", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "03367a6969c0d62e9b0fb3439ff2574dbacd5d616cc57b08f7c5417d4ac6e94faf" - } - }, - "signature": "3045022100f9a6be773cea615ec0d0cf0e330f3ca1faaa86f7475d620f6ae1299ca58671d5022065ae6e660f1c80e935db4e66387fb44225ee73f671e54003c0780e435029acc3", - "id": "54f2abed7bbdedacff1decc7be229f6385561cc8f800676ea61deae358bb252e", - "senderId": "APQgLh8MaztT1XuWVKb6d4FKM9HMmdmDVB" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0315ccdbcb0ec7bf484726f95bcb2b331cd976bd7d72f0e3f5c38fe4167d7c69fe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "0315ccdbcb0ec7bf484726f95bcb2b331cd976bd7d72f0e3f5c38fe4167d7c69fe" - } - }, - "signature": "304402205a4959cab68877017ad4d4ac61a5b7c9a7cc674ec66b21e7463a8673b8d51ab002200fbdab9ab0e4601e1fccce869be1a1b3348d8127e4febeaab68228953a81cf04", - "id": "7348d455b5dae1aebd48b3668b571f5e44c0e8f612c1377a19de8f3e27c84644", - "senderId": "AGZhXHXFUdvd7W4aWs1fYJe6XFUYeSWrd4" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03477d8e77a43b443d401aeec8b32aec5429f8f453b93d5c4061cd17101e09b077", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03477d8e77a43b443d401aeec8b32aec5429f8f453b93d5c4061cd17101e09b077" - } - }, - "signature": "304402201a4433b86c57e2701fdbf7640b13b21634b7cbd3319aa0c914e16b59fae45bfd022044dc7bec58897ddb36fadae3c52662aa228ae88b0ee6c467a52d1e476784a30b", - "id": "6f6fa4b601053f6fcf50e734d6caf590dc4d23980c32d9f1b62e10cda3241018", - "senderId": "AVoTzMjHaaWKDuA26ysXGU681N6Q2PQre6" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "037d5887d9a9bb48e45404fc5c3149bbd53d82bb4572bf468f0da9b4e9f6c73ae7", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "037d5887d9a9bb48e45404fc5c3149bbd53d82bb4572bf468f0da9b4e9f6c73ae7" - } - }, - "signature": "3045022100c2f43505df18e7682ac36c16f6429764ad86339527e554a78ef44d0941cfc937022007118cfd9b51ea359ce4dcbbb5dcf06147fd219a38bf0eee9a220c507c5944de", - "id": "dca332856f06a5431c648f73f859b8f8286bac60d7f452eac42d7febef0fd7f2", - "senderId": "AGdHR2UZ3MJuaXWejKGoAycztyN8BFQnNG" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026f1910d432c8ca8f04248e74c4b565a236d9851caeed4422550c3803b313bf39", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "026f1910d432c8ca8f04248e74c4b565a236d9851caeed4422550c3803b313bf39" - } - }, - "signature": "30450221009a73cbe01aba47c293914b2bd382e5715a611dc14714e225fe9287cf293fc34f02201b05843284b43452c1b779b6121be37b4459e96d6e1328f6ca4c076522f4351f", - "id": "49620d01436262018949d76325f7f7c974fe5419a32626c3ba1e7c3b2f0752e1", - "senderId": "AYTEu82arYgRyvTgi7dbYwjodV7ignYucz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "024eda8b8e70b93f87c1e18929a2ad789aad3f6b3fe4aa12b4f74513bc45916726", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "024eda8b8e70b93f87c1e18929a2ad789aad3f6b3fe4aa12b4f74513bc45916726" - } - }, - "signature": "30440220637254d742d5038797dd38323259ba4263c3e98eefae4a8c6717faec0e866a2f022075765f0a4036afbd297bb3bd10c7ee7f62731b45dfe708a04096947af7caf106", - "id": "63acffbbad5440dec99e17dd3eb4d501f44330f0d8c4db4cf161cc0992eae5c1", - "senderId": "AWLoVgSScNNB2kecswuhbY9tcrtv9kf2iC" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02886a2ad45ba50edeffbb7447cc1baf1cdd16e3b91e5ed6ba8b16c687f03e01db", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02886a2ad45ba50edeffbb7447cc1baf1cdd16e3b91e5ed6ba8b16c687f03e01db" - } - }, - "signature": "3045022100e78d6752036f323c61e78cefc4355a1dd7372fd782377052b5c80d30f81597c3022004b86acb40c52d91a1eea79d4de929c59a5e24fed6f243157088c1eed03f039e", - "id": "531ecd8620d51f46dc1140c8d8f0ec8344b4ebef61f17e8c304e5b3bd5e02586", - "senderId": "Aa5rKoVusA5xiyh8Git1tJUWZE48ScbCR8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "037bd8595ad6b5787671c99921208284ac6f791a8ca55c6e49ca94a94731b6cba9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "037bd8595ad6b5787671c99921208284ac6f791a8ca55c6e49ca94a94731b6cba9" - } - }, - "signature": "3044022066203d448724540f14a916591051ed79f57e3995af8cd9bab5ef003064c08dd002202a60f251bda6088ce8d43d6289ea2ded6442e95344654f02a17488f26c10a893", - "id": "eb253b77c4560b226d8edbfbb860717289258f4cd1ef15cee6da88f80029a33b", - "senderId": "AbfnTeGFiRM3m8eHcMsrqNvrpUCoCnuSzH" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e8eeb0e5063caf214986fa2e085dc67897908cc2501ccdeefeef33722afde50a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "03e8eeb0e5063caf214986fa2e085dc67897908cc2501ccdeefeef33722afde50a" - } - }, - "signature": "3045022100c6bcd9f75dbc9bafeb2e134028b2c4f8be77214611012f4d9b92bad5610d42c1022039923f93586e1976ae614fc90208352dac120505a08be0a2b9b82840622473a4", - "id": "5378c9044477765f44580a5f42ace617df524457315fe63ef9806e41de5e65fc", - "senderId": "ANstQM4LaxfsuKtFMd8vqdGte3CL9s2vMA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0307784cfc3d9002be47ffa32e8d99146869342254247df059452c5f92f7ae521a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "0307784cfc3d9002be47ffa32e8d99146869342254247df059452c5f92f7ae521a" - } - }, - "signature": "30450221008ad9a84421282b28c6d4cabc42a7855bb3853a02edb0524fecc21b821216a6d702203b0558b44c659d52fa163f8941a61ae0b8cec81cdd3fdbc8e91577ede6815fcc", - "id": "20d635b856a468eefc0801853afd1d387d25fdbe69c677e56b850fc45a7c3029", - "senderId": "AVFkEkCmEg7cuCXoVrfvtH6mKz6XC9XnvV" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "028dc3fd930165f910571a159f574ab15ac740f48e68429a7fbfb42ba202c64a0d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "028dc3fd930165f910571a159f574ab15ac740f48e68429a7fbfb42ba202c64a0d" - } - }, - "signature": "3045022100fdb6d70801c889e1173ae9de78a07e55bed999ef39107f2dac1e65d6bacbe89b022004557733f6fe0d96f67cf420691375a8ab23ab212365a96acb9b36aa54f8f415", - "id": "c7e1b81e3cb7f63eb947647fa572b5309b9f022d893e745baffa282f51e65dba", - "senderId": "AMG1Y3LP4kZJMAtoPhsnVkpsMTJ8nnCDr3" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03de80311e0ca23a780507fae2691b7c995cf36fb2b2d079b7c518e03302d56eff", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "03de80311e0ca23a780507fae2691b7c995cf36fb2b2d079b7c518e03302d56eff" - } - }, - "signature": "304402200bd5d223d887130a7a8b65f10e8f0a7eacda456257dc5eae4cf0f3f7068ed6e402207d6d5dd9cf0f69f592fa81bb117a83cd0e55a21b16abe048c1d4b603f295fcdc", - "id": "71debbd9fb57e0cdfcf42d9d012768a7d9ad85b4b6f32d520b424ef0e0893bc8", - "senderId": "AFxqVbsVuDfnfyd9ciRAuGq6waR5GqiC5R" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021780f82dfb6331cbcf35b9fc233cf3494e569a329b2966249c5632b0ecf53cb2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "021780f82dfb6331cbcf35b9fc233cf3494e569a329b2966249c5632b0ecf53cb2" - } - }, - "signature": "30440220043a0359c3d68ff69877cbf2f116410bea908036f57c3aa1d51684ef9e4d1fa102202a55ae852b5e547c6733d8ea0fdf137f6525b4effda003e09289936be0f333e4", - "id": "3eee8478d002ba59f2ab94bd539c26f805005b24d8d0cdfda6f79ba576db55ce", - "senderId": "ATjjXXcGPTum6wogPVGb9pmimpSo4EDDEv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02d113acc492f613cfed6ec60fe31d0d0c1aa9787122070fb8dd76baf27f7a4766", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "02d113acc492f613cfed6ec60fe31d0d0c1aa9787122070fb8dd76baf27f7a4766" - } - }, - "signature": "304502210097b10f85338d39ca4dfda4baf9b86f97253cb06ff2c80d3d9a3e5023b2294f060220192986b55a91bf3c5d5ad1e8f7aebf69df89f022e1d8f8674ecb675faace1623", - "id": "69483bf341ab8e5ef989340da103a162b25e09ef205b53a7d2f6f8547bc7334c", - "senderId": "ARagsXvdeTHYghaQgJkwbdSkPLZ73qdMkR" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03aa98d2a27ef50e34f6882a089d0915edc0d21c2c7eedc9bf3323f8ca8c260531", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "03aa98d2a27ef50e34f6882a089d0915edc0d21c2c7eedc9bf3323f8ca8c260531" - } - }, - "signature": "304402207f3b69c5fe22ec832246ff2e0318b361849cb8fb7250d9eee96639e17d112ecd02203a4d4010360992f4162f7ffd5361884d787a07580a579da11fee4a8b8b7e78d0", - "id": "d6424b437baef1b93e967a48b29313347e6a79cd93818528c9a7515ad167b917", - "senderId": "AT9xWcPQ8hGYuXZ8aWE57VJFohyX1TTLkH" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02e83dae2b59ad7923d931f8d0ff96588b6f2b2183288c667bacf2888d5f9d80c9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "02e83dae2b59ad7923d931f8d0ff96588b6f2b2183288c667bacf2888d5f9d80c9" - } - }, - "signature": "3045022100e6d497a3905baff592921a269d78882d787a03a4c67e7818a5c71aa57c00186e02205b54519080782485a6ca918a50bdac68c0cc1723259cededb1ae9f011a88b919", - "id": "fd90a8bf3d38e0fc81020705be347205da7006d4db889f8a5c28653069d060b8", - "senderId": "AGNMmJ5upuU38ucaG3TUsG1ESaQDExSMo4" - } - ], - "height": 1, - "id": "4366553906931540162", - "blockSignature": "3045022100c442ef265f2a7fa102d61e9a180e335fd17e8e3224307dadf8ac856e569c5c5102201a34cb1302cf4e0887b45784bfbdaf5cfbc44f6d6dad638d56bafa82ec96fd45" -} diff --git a/packages/core/lib/config/mainnet/peers.json b/packages/core/lib/config/mainnet/peers.json deleted file mode 100644 index 194ee88bf0..0000000000 --- a/packages/core/lib/config/mainnet/peers.json +++ /dev/null @@ -1,269 +0,0 @@ -{ - "minimumVersion": ">=2.0.15", - "minimumNetworkReach": 20, - "globalTimeout": 5000, - "coldStart": 30, - "whiteList": [], - "blackList": [], - "list": [ - { - "ip": "5.196.105.32", - "port": 4001 - }, - { - "ip": "5.196.105.33", - "port": 4001 - }, - { - "ip": "5.196.105.34", - "port": 4001 - }, - { - "ip": "5.196.105.35", - "port": 4001 - }, - { - "ip": "5.196.105.36", - "port": 4001 - }, - { - "ip": "5.196.105.37", - "port": 4001 - }, - { - "ip": "5.196.105.38", - "port": 4001 - }, - { - "ip": "5.196.105.39", - "port": 4001 - }, - { - "ip": "178.32.65.136", - "port": 4001 - }, - { - "ip": "178.32.65.137", - "port": 4001 - }, - { - "ip": "178.32.65.138", - "port": 4001 - }, - { - "ip": "178.32.65.139", - "port": 4001 - }, - { - "ip": "178.32.65.140", - "port": 4001 - }, - { - "ip": "178.32.65.141", - "port": 4001 - }, - { - "ip": "178.32.65.142", - "port": 4001 - }, - { - "ip": "178.32.65.143", - "port": 4001 - }, - { - "ip": "5.196.105.40", - "port": 4001 - }, - { - "ip": "5.196.105.41", - "port": 4001 - }, - { - "ip": "5.196.105.42", - "port": 4001 - }, - { - "ip": "5.196.105.43", - "port": 4001 - }, - { - "ip": "5.196.105.44", - "port": 4001 - }, - { - "ip": "5.196.105.45", - "port": 4001 - }, - { - "ip": "5.196.105.46", - "port": 4001 - }, - { - "ip": "5.196.105.47", - "port": 4001 - }, - { - "ip": "54.38.120.32", - "port": 4001 - }, - { - "ip": "54.38.120.33", - "port": 4001 - }, - { - "ip": "54.38.120.34", - "port": 4001 - }, - { - "ip": "54.38.120.35", - "port": 4001 - }, - { - "ip": "54.38.120.36", - "port": 4001 - }, - { - "ip": "54.38.120.37", - "port": 4001 - }, - { - "ip": "54.38.120.38", - "port": 4001 - }, - { - "ip": "54.38.120.39", - "port": 4001 - }, - { - "ip": "151.80.125.32", - "port": 4001 - }, - { - "ip": "151.80.125.33", - "port": 4001 - }, - { - "ip": "151.80.125.34", - "port": 4001 - }, - { - "ip": "151.80.125.35", - "port": 4001 - }, - { - "ip": "151.80.125.36", - "port": 4001 - }, - { - "ip": "151.80.125.37", - "port": 4001 - }, - { - "ip": "151.80.125.38", - "port": 4001 - }, - { - "ip": "151.80.125.39", - "port": 4001 - }, - { - "ip": "213.32.41.104", - "port": 4001 - }, - { - "ip": "213.32.41.105", - "port": 4001 - }, - { - "ip": "213.32.41.106", - "port": 4001 - }, - { - "ip": "213.32.41.107", - "port": 4001 - }, - { - "ip": "213.32.41.108", - "port": 4001 - }, - { - "ip": "213.32.41.109", - "port": 4001 - }, - { - "ip": "213.32.41.110", - "port": 4001 - }, - { - "ip": "213.32.41.111", - "port": 4001 - }, - { - "ip": "5.135.22.92", - "port": 4001 - }, - { - "ip": "5.135.22.93", - "port": 4001 - }, - { - "ip": "5.135.22.94", - "port": 4001 - }, - { - "ip": "5.135.22.95", - "port": 4001 - }, - { - "ip": "5.135.52.96", - "port": 4001 - }, - { - "ip": "5.135.52.97", - "port": 4001 - }, - { - "ip": "5.135.52.98", - "port": 4001 - }, - { - "ip": "5.135.52.99", - "port": 4001 - }, - { - "ip": "51.255.105.52", - "port": 4001 - }, - { - "ip": "51.255.105.53", - "port": 4001 - }, - { - "ip": "51.255.105.54", - "port": 4001 - }, - { - "ip": "51.255.105.55", - "port": 4001 - }, - { - "ip": "46.105.160.104", - "port": 4001 - }, - { - "ip": "46.105.160.105", - "port": 4001 - }, - { - "ip": "46.105.160.106", - "port": 4001 - }, - { - "ip": "46.105.160.107", - "port": 4001 - } - ], - "sources": [ - "https://raw.githubusercontent.com/ArkEcosystem/peers/master/mainnet.json" - ] -} diff --git a/packages/core/lib/config/mainnet/plugins.js b/packages/core/lib/config/mainnet/plugins.js deleted file mode 100644 index a52fee8f73..0000000000 --- a/packages/core/lib/config/mainnet/plugins.js +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || `ark_${process.env.ARK_NETWORK_NAME}`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: !process.env.ARK_TRANSACTION_POOL_DISABLED, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4001, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: !process.env.ARK_API_DISABLED, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4003, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4005, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4001}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-snapshots': {}, -} diff --git a/packages/core/lib/config/testnet.1/delegates.json b/packages/core/lib/config/testnet.1/delegates.json deleted file mode 100644 index 33f51bf736..0000000000 --- a/packages/core/lib/config/testnet.1/delegates.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "secrets": [ - "target sort neutral address language spike measure jaguar glance strong drop zone", - "race total stage trap wool believe twin pudding claim claim eternal miss", - "parade isolate wing vague magic husband acid skin skate path fence rib", - "neither fine dry priority example obtain bread reopen afford coyote milk minor", - "token atom lemon game charge area goose hotel excess endless spice oblige", - "pledge buffalo finish pipe mule popular bind clinic draft salon swamp purpose", - "west hat hold stand unique panther cable extend spell shaft injury reopen", - "van impulse pole install profit excuse give auction expire remain skate input", - "wrist maze potato april survey burden bamboo knee foot carry speak prison", - "three toddler copy owner pencil minimum doctor orange bottom ice detail design", - "ceiling warrior person thing whisper jeans black cricket drift ahead tornado typical", - "obvious mutual tone usual valve credit soccer mention also clown main box", - "valve slot soft green scale menu anxiety live drill legend upgrade chimney", - "twist comfort mule weather print oven cabin seek punch rival prepare sphere", - "say tumble glass argue aware service force caution until grocery hammer fetch", - "idea illegal empty frozen canvas arctic number poet rely track size obscure", - "chalk try large tower shed warfare blade clerk fame second charge tobacco", - "category nice verb fox start able brass climb boss luggage voice whale", - "favorite emotion trumpet visual welcome spend fine lock image review garage opera", - "waste axis humor auction next salmon much margin useful glimpse insect rotate", - "remember rose genuine police guard old flavor parent gain cross twelve first", - "coil tray elder mask circle crush anger electric harbor onion grab will", - "shove airport bus gather radio derive below horse canvas crime tribe adjust", - "retire lend burden cricket able sheriff output grocery empty scorpion flat inquiry", - "agree grain record shift fossil summer hunt mutual net vast behind pilot", - "decide rhythm oyster lady they merry betray jelly coyote solve episode then" - ] -} diff --git a/packages/core/lib/config/testnet.1/genesisBlock.json b/packages/core/lib/config/testnet.1/genesisBlock.json deleted file mode 100644 index 317cd8ed94..0000000000 --- a/packages/core/lib/config/testnet.1/genesisBlock.json +++ /dev/null @@ -1,2312 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", - "timestamp": 0, - "numberOfTransactions": 153, - "payloadLength": 35960, - "previousBlock": null, - "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", - "transactions": [ - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", - "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", - "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", - "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", - "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", - "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", - "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", - "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", - "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", - "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", - "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", - "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", - "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", - "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", - "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", - "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", - "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", - "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", - "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", - "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", - "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", - "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", - "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", - "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", - "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", - "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", - "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", - "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", - "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", - "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", - "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", - "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", - "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", - "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", - "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", - "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", - "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", - "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", - "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", - "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", - "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", - "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", - "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", - "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", - "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", - "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", - "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", - "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", - "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", - "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", - "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245100000000000, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", - "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - } - }, - "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", - "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - } - }, - "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", - "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - } - }, - "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", - "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - } - }, - "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", - "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - } - }, - "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", - "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - } - }, - "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", - "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - } - }, - "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", - "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - } - }, - "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", - "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - } - }, - "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", - "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - } - }, - "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", - "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - } - }, - "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", - "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - } - }, - "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", - "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - } - }, - "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", - "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - } - }, - "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", - "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - } - }, - "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", - "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - } - }, - "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", - "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - } - }, - "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", - "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - } - }, - "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", - "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - } - }, - "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", - "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - } - }, - "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", - "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - } - }, - "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", - "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - } - }, - "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", - "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - } - }, - "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", - "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - } - }, - "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", - "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - } - }, - "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", - "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - } - }, - "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", - "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - } - }, - "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", - "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - } - }, - "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", - "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - } - }, - "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", - "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - } - }, - "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", - "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - } - }, - "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", - "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - } - }, - "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", - "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - } - }, - "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", - "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - } - }, - "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", - "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - } - }, - "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", - "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - } - }, - "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", - "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - } - }, - "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", - "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - } - }, - "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", - "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - } - }, - "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", - "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - } - }, - "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", - "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - } - }, - "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", - "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - } - }, - "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", - "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - } - }, - "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", - "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - } - }, - "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", - "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - } - }, - "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", - "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - } - }, - "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", - "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - } - }, - "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", - "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - } - }, - "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", - "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - } - }, - "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", - "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - } - }, - "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", - "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - } - }, - "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", - "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "votes": [ - "+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - ] - }, - "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", - "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "votes": [ - "+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - ] - }, - "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", - "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "votes": [ - "+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - ] - }, - "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", - "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "votes": [ - "+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - ] - }, - "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", - "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "votes": [ - "+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - ] - }, - "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", - "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "votes": [ - "+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - ] - }, - "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", - "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "votes": [ - "+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - ] - }, - "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", - "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "votes": [ - "+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - ] - }, - "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", - "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "votes": [ - "+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - ] - }, - "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", - "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "votes": [ - "+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - ] - }, - "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", - "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "votes": [ - "+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - ] - }, - "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", - "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "votes": [ - "+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - ] - }, - "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", - "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "votes": [ - "+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - ] - }, - "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", - "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "votes": [ - "+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - ] - }, - "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", - "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "votes": [ - "+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - ] - }, - "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", - "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "votes": [ - "+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - ] - }, - "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", - "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "votes": [ - "+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - ] - }, - "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", - "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "votes": [ - "+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - ] - }, - "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", - "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "votes": [ - "+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - ] - }, - "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", - "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "votes": [ - "+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - ] - }, - "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", - "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "votes": [ - "+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - ] - }, - "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", - "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "votes": [ - "+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - ] - }, - "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", - "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "votes": [ - "+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - ] - }, - "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", - "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "votes": [ - "+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - ] - }, - "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", - "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "votes": [ - "+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - ] - }, - "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", - "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "votes": [ - "+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - ] - }, - "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", - "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "votes": [ - "+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - ] - }, - "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", - "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "votes": [ - "+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - ] - }, - "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", - "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "votes": [ - "+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - ] - }, - "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", - "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "votes": [ - "+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - ] - }, - "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", - "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "votes": [ - "+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - ] - }, - "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", - "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "votes": [ - "+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - ] - }, - "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", - "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "votes": [ - "+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - ] - }, - "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", - "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "votes": [ - "+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - ] - }, - "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", - "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "votes": [ - "+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - ] - }, - "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", - "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "votes": [ - "+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - ] - }, - "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", - "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "votes": [ - "+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - ] - }, - "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", - "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "votes": [ - "+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - ] - }, - "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", - "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "votes": [ - "+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - ] - }, - "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", - "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "votes": [ - "+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - ] - }, - "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", - "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "votes": [ - "+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - ] - }, - "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", - "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "votes": [ - "+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - ] - }, - "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", - "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "votes": [ - "+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - ] - }, - "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", - "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "votes": [ - "+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - ] - }, - "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", - "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "votes": [ - "+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - ] - }, - "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", - "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "votes": [ - "+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - ] - }, - "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", - "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "votes": [ - "+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - ] - }, - "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", - "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "votes": [ - "+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - ] - }, - "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", - "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "votes": [ - "+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - ] - }, - "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", - "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "votes": [ - "+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - ] - }, - "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", - "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "votes": [ - "+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - ] - }, - "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", - "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - } - ], - "height": 1, - "id": "17184958558311101492", - "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" -} diff --git a/packages/core/lib/config/testnet.1/peers.json b/packages/core/lib/config/testnet.1/peers.json deleted file mode 100644 index e4708d521d..0000000000 --- a/packages/core/lib/config/testnet.1/peers.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "minimumVersion": ">=2.0.12", - "minimumNetworkReach": 20, - "globalTimeout": 5000, - "coldStart": 30, - "whiteList": [], - "blackList": [], - "list": [ - { - "ip": "127.0.0.1", - "port": 4102 - }, - { - "ip": "127.0.0.1", - "port": 4202 - } - ] -} diff --git a/packages/core/lib/config/testnet.1/plugins.js b/packages/core/lib/config/testnet.1/plugins.js deleted file mode 100644 index 1cceec6f0a..0000000000 --- a/packages/core/lib/config/testnet.1/plugins.js +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || `ark_${process.env.ARK_NETWORK_NAME}1`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: true, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4102, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: !process.env.ARK_API_DISABLED, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4103, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4105, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4102}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-snapshots': {}, -} diff --git a/packages/core/lib/config/testnet.2/delegates.json b/packages/core/lib/config/testnet.2/delegates.json deleted file mode 100644 index a094c271a3..0000000000 --- a/packages/core/lib/config/testnet.2/delegates.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "secrets": [ - "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", - "venue below waste gather spin cruise title still boost mother flash tuna", - "craft imitate step mixture patch forest volcano business charge around girl confirm", - "fatal hat sail asset chase barrel pluck bag approve coral slab bright", - "flash thank strike stove grain remove match reflect excess present beyond matrix", - "various present shine domain outdoor neck soup diesel limit express genuine tuna", - "hurdle pulse sheriff anchor two hope income pattern hazard bacon book night", - "glow boss party require silk interest pyramid marriage try wisdom snow grab", - "direct palace screen shuffle world fit produce rubber jelly gather river ordinary", - "wall ketchup shed word twist flip knock liar merge rural ill pond", - "measure blue volcano month orphan only cupboard found laugh peasant drama monitor", - "scissors sort pause medal target diesel reveal stock maze party gauge vacant", - "hand anchor hip pyramid taxi vote celery clap tribe damage shrimp brave", - "merge thunder detect stove else bottom favorite doll learn festival basic basic", - "educate attitude rely combine treat balcony west reopen coil west grab depth", - "advance silver advance squeeze load stone middle garden perfect invest field lounge", - "prison tobacco acquire stone dignity palace note decade they current lesson robot", - "team impact stadium year security steak harsh vacant fire pelican until olympic", - "walk intact ice prevent fit trial frog glory monkey once grunt gentle", - "same lens parrot suspect just sunset frown exercise lemon two mistake robust", - "skill insect issue crazy erase okay govern upgrade bounce dress motor athlete", - "peasant alert hard deposit naive follow page fiscal normal awful wedding history", - "resemble abandon same total oppose noise dune order fatal rhythm pink science", - "wide mesh ketchup acquire bright day mountain final below hamster scout drive", - "half weasel poet better rocket fan help left blade soda argue system" - ] -} diff --git a/packages/core/lib/config/testnet.2/genesisBlock.json b/packages/core/lib/config/testnet.2/genesisBlock.json deleted file mode 100644 index 317cd8ed94..0000000000 --- a/packages/core/lib/config/testnet.2/genesisBlock.json +++ /dev/null @@ -1,2312 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", - "timestamp": 0, - "numberOfTransactions": 153, - "payloadLength": 35960, - "previousBlock": null, - "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", - "transactions": [ - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", - "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", - "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", - "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", - "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", - "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", - "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", - "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", - "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", - "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", - "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", - "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", - "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", - "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", - "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", - "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", - "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", - "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", - "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", - "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", - "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", - "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", - "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", - "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", - "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", - "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", - "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", - "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", - "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", - "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", - "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", - "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", - "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", - "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", - "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", - "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", - "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", - "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", - "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", - "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", - "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", - "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", - "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", - "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", - "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", - "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", - "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", - "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", - "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", - "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", - "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245100000000000, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", - "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - } - }, - "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", - "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - } - }, - "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", - "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - } - }, - "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", - "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - } - }, - "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", - "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - } - }, - "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", - "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - } - }, - "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", - "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - } - }, - "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", - "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - } - }, - "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", - "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - } - }, - "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", - "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - } - }, - "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", - "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - } - }, - "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", - "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - } - }, - "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", - "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - } - }, - "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", - "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - } - }, - "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", - "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - } - }, - "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", - "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - } - }, - "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", - "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - } - }, - "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", - "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - } - }, - "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", - "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - } - }, - "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", - "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - } - }, - "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", - "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - } - }, - "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", - "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - } - }, - "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", - "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - } - }, - "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", - "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - } - }, - "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", - "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - } - }, - "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", - "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - } - }, - "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", - "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - } - }, - "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", - "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - } - }, - "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", - "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - } - }, - "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", - "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - } - }, - "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", - "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - } - }, - "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", - "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - } - }, - "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", - "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - } - }, - "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", - "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - } - }, - "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", - "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - } - }, - "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", - "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - } - }, - "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", - "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - } - }, - "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", - "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - } - }, - "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", - "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - } - }, - "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", - "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - } - }, - "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", - "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - } - }, - "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", - "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - } - }, - "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", - "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - } - }, - "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", - "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - } - }, - "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", - "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - } - }, - "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", - "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - } - }, - "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", - "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - } - }, - "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", - "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - } - }, - "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", - "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - } - }, - "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", - "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - } - }, - "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", - "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - } - }, - "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", - "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "votes": [ - "+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - ] - }, - "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", - "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "votes": [ - "+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - ] - }, - "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", - "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "votes": [ - "+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - ] - }, - "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", - "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "votes": [ - "+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - ] - }, - "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", - "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "votes": [ - "+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - ] - }, - "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", - "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "votes": [ - "+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - ] - }, - "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", - "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "votes": [ - "+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - ] - }, - "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", - "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "votes": [ - "+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - ] - }, - "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", - "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "votes": [ - "+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - ] - }, - "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", - "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "votes": [ - "+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - ] - }, - "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", - "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "votes": [ - "+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - ] - }, - "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", - "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "votes": [ - "+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - ] - }, - "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", - "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "votes": [ - "+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - ] - }, - "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", - "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "votes": [ - "+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - ] - }, - "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", - "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "votes": [ - "+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - ] - }, - "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", - "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "votes": [ - "+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - ] - }, - "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", - "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "votes": [ - "+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - ] - }, - "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", - "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "votes": [ - "+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - ] - }, - "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", - "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "votes": [ - "+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - ] - }, - "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", - "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "votes": [ - "+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - ] - }, - "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", - "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "votes": [ - "+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - ] - }, - "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", - "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "votes": [ - "+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - ] - }, - "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", - "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "votes": [ - "+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - ] - }, - "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", - "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "votes": [ - "+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - ] - }, - "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", - "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "votes": [ - "+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - ] - }, - "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", - "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "votes": [ - "+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - ] - }, - "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", - "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "votes": [ - "+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - ] - }, - "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", - "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "votes": [ - "+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - ] - }, - "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", - "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "votes": [ - "+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - ] - }, - "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", - "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "votes": [ - "+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - ] - }, - "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", - "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "votes": [ - "+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - ] - }, - "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", - "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "votes": [ - "+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - ] - }, - "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", - "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "votes": [ - "+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - ] - }, - "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", - "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "votes": [ - "+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - ] - }, - "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", - "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "votes": [ - "+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - ] - }, - "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", - "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "votes": [ - "+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - ] - }, - "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", - "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "votes": [ - "+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - ] - }, - "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", - "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "votes": [ - "+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - ] - }, - "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", - "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "votes": [ - "+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - ] - }, - "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", - "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "votes": [ - "+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - ] - }, - "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", - "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "votes": [ - "+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - ] - }, - "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", - "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "votes": [ - "+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - ] - }, - "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", - "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "votes": [ - "+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - ] - }, - "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", - "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "votes": [ - "+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - ] - }, - "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", - "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "votes": [ - "+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - ] - }, - "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", - "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "votes": [ - "+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - ] - }, - "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", - "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "votes": [ - "+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - ] - }, - "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", - "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "votes": [ - "+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - ] - }, - "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", - "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "votes": [ - "+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - ] - }, - "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", - "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "votes": [ - "+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - ] - }, - "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", - "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "votes": [ - "+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - ] - }, - "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", - "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - } - ], - "height": 1, - "id": "17184958558311101492", - "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" -} diff --git a/packages/core/lib/config/testnet.2/peers.json b/packages/core/lib/config/testnet.2/peers.json deleted file mode 100644 index e4708d521d..0000000000 --- a/packages/core/lib/config/testnet.2/peers.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "minimumVersion": ">=2.0.12", - "minimumNetworkReach": 20, - "globalTimeout": 5000, - "coldStart": 30, - "whiteList": [], - "blackList": [], - "list": [ - { - "ip": "127.0.0.1", - "port": 4102 - }, - { - "ip": "127.0.0.1", - "port": 4202 - } - ] -} diff --git a/packages/core/lib/config/testnet.2/plugins.js b/packages/core/lib/config/testnet.2/plugins.js deleted file mode 100644 index f97525d009..0000000000 --- a/packages/core/lib/config/testnet.2/plugins.js +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || `ark_${process.env.ARK_NETWORK_NAME}2`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: true, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4202, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: !process.env.ARK_API_DISABLED, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4203, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4205, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4202}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-snapshots': {}, -} diff --git a/packages/core/lib/config/testnet.live/delegates.json b/packages/core/lib/config/testnet.live/delegates.json deleted file mode 100644 index 096f5472e1..0000000000 --- a/packages/core/lib/config/testnet.live/delegates.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "secrets": [] -} diff --git a/packages/core/lib/config/testnet.live/genesisBlock.json b/packages/core/lib/config/testnet.live/genesisBlock.json deleted file mode 100644 index 317cd8ed94..0000000000 --- a/packages/core/lib/config/testnet.live/genesisBlock.json +++ /dev/null @@ -1,2312 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", - "timestamp": 0, - "numberOfTransactions": 153, - "payloadLength": 35960, - "previousBlock": null, - "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", - "transactions": [ - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", - "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", - "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", - "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", - "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", - "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", - "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", - "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", - "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", - "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", - "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", - "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", - "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", - "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", - "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", - "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", - "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", - "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", - "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", - "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", - "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", - "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", - "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", - "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", - "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", - "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", - "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", - "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", - "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", - "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", - "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", - "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", - "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", - "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", - "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", - "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", - "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", - "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", - "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", - "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", - "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", - "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", - "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", - "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", - "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", - "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", - "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", - "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", - "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", - "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", - "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245100000000000, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", - "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - } - }, - "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", - "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - } - }, - "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", - "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - } - }, - "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", - "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - } - }, - "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", - "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - } - }, - "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", - "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - } - }, - "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", - "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - } - }, - "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", - "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - } - }, - "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", - "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - } - }, - "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", - "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - } - }, - "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", - "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - } - }, - "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", - "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - } - }, - "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", - "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - } - }, - "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", - "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - } - }, - "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", - "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - } - }, - "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", - "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - } - }, - "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", - "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - } - }, - "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", - "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - } - }, - "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", - "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - } - }, - "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", - "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - } - }, - "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", - "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - } - }, - "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", - "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - } - }, - "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", - "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - } - }, - "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", - "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - } - }, - "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", - "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - } - }, - "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", - "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - } - }, - "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", - "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - } - }, - "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", - "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - } - }, - "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", - "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - } - }, - "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", - "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - } - }, - "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", - "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - } - }, - "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", - "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - } - }, - "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", - "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - } - }, - "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", - "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - } - }, - "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", - "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - } - }, - "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", - "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - } - }, - "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", - "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - } - }, - "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", - "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - } - }, - "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", - "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - } - }, - "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", - "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - } - }, - "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", - "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - } - }, - "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", - "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - } - }, - "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", - "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - } - }, - "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", - "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - } - }, - "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", - "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - } - }, - "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", - "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - } - }, - "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", - "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - } - }, - "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", - "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - } - }, - "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", - "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - } - }, - "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", - "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - } - }, - "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", - "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - } - }, - "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", - "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "votes": [ - "+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - ] - }, - "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", - "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "votes": [ - "+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - ] - }, - "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", - "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "votes": [ - "+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - ] - }, - "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", - "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "votes": [ - "+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - ] - }, - "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", - "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "votes": [ - "+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - ] - }, - "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", - "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "votes": [ - "+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - ] - }, - "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", - "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "votes": [ - "+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - ] - }, - "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", - "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "votes": [ - "+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - ] - }, - "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", - "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "votes": [ - "+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - ] - }, - "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", - "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "votes": [ - "+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - ] - }, - "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", - "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "votes": [ - "+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - ] - }, - "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", - "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "votes": [ - "+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - ] - }, - "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", - "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "votes": [ - "+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - ] - }, - "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", - "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "votes": [ - "+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - ] - }, - "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", - "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "votes": [ - "+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - ] - }, - "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", - "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "votes": [ - "+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - ] - }, - "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", - "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "votes": [ - "+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - ] - }, - "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", - "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "votes": [ - "+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - ] - }, - "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", - "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "votes": [ - "+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - ] - }, - "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", - "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "votes": [ - "+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - ] - }, - "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", - "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "votes": [ - "+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - ] - }, - "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", - "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "votes": [ - "+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - ] - }, - "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", - "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "votes": [ - "+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - ] - }, - "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", - "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "votes": [ - "+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - ] - }, - "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", - "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "votes": [ - "+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - ] - }, - "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", - "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "votes": [ - "+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - ] - }, - "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", - "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "votes": [ - "+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - ] - }, - "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", - "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "votes": [ - "+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - ] - }, - "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", - "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "votes": [ - "+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - ] - }, - "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", - "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "votes": [ - "+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - ] - }, - "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", - "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "votes": [ - "+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - ] - }, - "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", - "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "votes": [ - "+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - ] - }, - "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", - "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "votes": [ - "+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - ] - }, - "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", - "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "votes": [ - "+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - ] - }, - "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", - "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "votes": [ - "+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - ] - }, - "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", - "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "votes": [ - "+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - ] - }, - "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", - "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "votes": [ - "+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - ] - }, - "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", - "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "votes": [ - "+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - ] - }, - "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", - "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "votes": [ - "+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - ] - }, - "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", - "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "votes": [ - "+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - ] - }, - "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", - "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "votes": [ - "+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - ] - }, - "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", - "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "votes": [ - "+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - ] - }, - "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", - "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "votes": [ - "+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - ] - }, - "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", - "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "votes": [ - "+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - ] - }, - "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", - "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "votes": [ - "+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - ] - }, - "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", - "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "votes": [ - "+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - ] - }, - "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", - "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "votes": [ - "+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - ] - }, - "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", - "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "votes": [ - "+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - ] - }, - "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", - "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "votes": [ - "+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - ] - }, - "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", - "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "votes": [ - "+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - ] - }, - "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", - "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "votes": [ - "+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - ] - }, - "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", - "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - } - ], - "height": 1, - "id": "17184958558311101492", - "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" -} diff --git a/packages/core/lib/config/testnet.live/peers.json b/packages/core/lib/config/testnet.live/peers.json deleted file mode 100644 index d1ba485f9d..0000000000 --- a/packages/core/lib/config/testnet.live/peers.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "minimumVersion": ">=2.0.12", - "minimumNetworkReach": 10, - "blackList": [], - "globalTimeout": 3000, - "list": [ - { - "ip": "51.255.100.0", - "port": 4000 - }, - { - "ip": "151.80.125.32", - "port": 4000 - }, - { - "ip": "167.114.6.80", - "port": 4000 - }, - { - "ip": "137.74.79.168", - "port": 4000 - }, - { - "ip": "193.70.72.80", - "port": 4000 - } - ], - "sources": ["https://test-explorer.arknet.cloud/testnet.json"] -} diff --git a/packages/core/lib/config/testnet.live/plugins.js b/packages/core/lib/config/testnet.live/plugins.js deleted file mode 100644 index 93c1884fa8..0000000000 --- a/packages/core/lib/config/testnet.live/plugins.js +++ /dev/null @@ -1,74 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || - `ark_${process.env.ARK_NETWORK_NAME}live`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: true, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4000, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: true, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4003, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4105, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4000}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-snapshots': {}, -} diff --git a/packages/core/lib/config/testnet/delegates.json b/packages/core/lib/config/testnet/delegates.json deleted file mode 100644 index 5af0f71488..0000000000 --- a/packages/core/lib/config/testnet/delegates.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "secrets": [ - "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", - "venue below waste gather spin cruise title still boost mother flash tuna", - "craft imitate step mixture patch forest volcano business charge around girl confirm", - "fatal hat sail asset chase barrel pluck bag approve coral slab bright", - "flash thank strike stove grain remove match reflect excess present beyond matrix", - "various present shine domain outdoor neck soup diesel limit express genuine tuna", - "hurdle pulse sheriff anchor two hope income pattern hazard bacon book night", - "glow boss party require silk interest pyramid marriage try wisdom snow grab", - "direct palace screen shuffle world fit produce rubber jelly gather river ordinary", - "wall ketchup shed word twist flip knock liar merge rural ill pond", - "measure blue volcano month orphan only cupboard found laugh peasant drama monitor", - "scissors sort pause medal target diesel reveal stock maze party gauge vacant", - "hand anchor hip pyramid taxi vote celery clap tribe damage shrimp brave", - "merge thunder detect stove else bottom favorite doll learn festival basic basic", - "educate attitude rely combine treat balcony west reopen coil west grab depth", - "advance silver advance squeeze load stone middle garden perfect invest field lounge", - "prison tobacco acquire stone dignity palace note decade they current lesson robot", - "team impact stadium year security steak harsh vacant fire pelican until olympic", - "walk intact ice prevent fit trial frog glory monkey once grunt gentle", - "same lens parrot suspect just sunset frown exercise lemon two mistake robust", - "skill insect issue crazy erase okay govern upgrade bounce dress motor athlete", - "peasant alert hard deposit naive follow page fiscal normal awful wedding history", - "resemble abandon same total oppose noise dune order fatal rhythm pink science", - "wide mesh ketchup acquire bright day mountain final below hamster scout drive", - "half weasel poet better rocket fan help left blade soda argue system", - "target sort neutral address language spike measure jaguar glance strong drop zone", - "race total stage trap wool believe twin pudding claim claim eternal miss", - "parade isolate wing vague magic husband acid skin skate path fence rib", - "neither fine dry priority example obtain bread reopen afford coyote milk minor", - "token atom lemon game charge area goose hotel excess endless spice oblige", - "pledge buffalo finish pipe mule popular bind clinic draft salon swamp purpose", - "west hat hold stand unique panther cable extend spell shaft injury reopen", - "van impulse pole install profit excuse give auction expire remain skate input", - "wrist maze potato april survey burden bamboo knee foot carry speak prison", - "three toddler copy owner pencil minimum doctor orange bottom ice detail design", - "ceiling warrior person thing whisper jeans black cricket drift ahead tornado typical", - "obvious mutual tone usual valve credit soccer mention also clown main box", - "valve slot soft green scale menu anxiety live drill legend upgrade chimney", - "twist comfort mule weather print oven cabin seek punch rival prepare sphere", - "say tumble glass argue aware service force caution until grocery hammer fetch", - "idea illegal empty frozen canvas arctic number poet rely track size obscure", - "chalk try large tower shed warfare blade clerk fame second charge tobacco", - "category nice verb fox start able brass climb boss luggage voice whale", - "favorite emotion trumpet visual welcome spend fine lock image review garage opera", - "waste axis humor auction next salmon much margin useful glimpse insect rotate", - "remember rose genuine police guard old flavor parent gain cross twelve first", - "coil tray elder mask circle crush anger electric harbor onion grab will", - "shove airport bus gather radio derive below horse canvas crime tribe adjust", - "retire lend burden cricket able sheriff output grocery empty scorpion flat inquiry", - "agree grain record shift fossil summer hunt mutual net vast behind pilot", - "decide rhythm oyster lady they merry betray jelly coyote solve episode then" - ] -} diff --git a/packages/core/lib/config/testnet/genesisBlock.json b/packages/core/lib/config/testnet/genesisBlock.json deleted file mode 100644 index 317cd8ed94..0000000000 --- a/packages/core/lib/config/testnet/genesisBlock.json +++ /dev/null @@ -1,2312 +0,0 @@ -{ - "version": 0, - "totalAmount": 12500000000000000, - "totalFee": 0, - "reward": 0, - "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", - "timestamp": 0, - "numberOfTransactions": 153, - "payloadLength": 35960, - "previousBlock": null, - "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", - "transactions": [ - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", - "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", - "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", - "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", - "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", - "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", - "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", - "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", - "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", - "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", - "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", - "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", - "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", - "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", - "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", - "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", - "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", - "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", - "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", - "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", - "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", - "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", - "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", - "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", - "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", - "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", - "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", - "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", - "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", - "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", - "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", - "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", - "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", - "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", - "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", - "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", - "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", - "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", - "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", - "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", - "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", - "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", - "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", - "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", - "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", - "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", - "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", - "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", - "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", - "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245098000000000, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", - "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 0, - "amount": 245100000000000, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "timestamp": 0, - "asset": {}, - "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", - "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", - "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", - "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_9", - "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - } - }, - "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", - "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_18", - "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - } - }, - "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", - "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_47", - "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - } - }, - "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", - "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_5", - "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - } - }, - "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", - "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_19", - "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - } - }, - "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", - "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_42", - "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - } - }, - "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", - "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_10", - "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - } - }, - "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", - "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_20", - "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - } - }, - "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", - "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_49", - "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - } - }, - "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", - "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_3", - "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - } - }, - "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", - "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_21", - "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - } - }, - "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", - "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_41", - "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - } - }, - "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", - "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_11", - "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - } - }, - "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", - "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_22", - "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - } - }, - "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", - "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_46", - "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - } - }, - "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", - "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_6", - "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - } - }, - "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", - "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_23", - "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - } - }, - "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", - "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_40", - "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - } - }, - "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", - "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_12", - "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - } - }, - "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", - "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_24", - "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - } - }, - "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", - "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_50", - "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - } - }, - "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", - "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_2", - "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - } - }, - "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", - "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_25", - "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - } - }, - "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", - "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_39", - "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - } - }, - "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", - "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_13", - "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - } - }, - "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", - "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_1", - "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - } - }, - "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", - "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_45", - "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - } - }, - "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", - "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_7", - "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - } - }, - "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", - "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_27", - "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - } - }, - "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", - "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_38", - "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - } - }, - "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", - "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_14", - "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - } - }, - "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", - "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_28", - "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - } - }, - "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", - "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_48", - "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - } - }, - "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", - "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_4", - "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - } - }, - "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", - "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_29", - "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - } - }, - "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", - "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_37", - "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - } - }, - "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", - "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_15", - "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - } - }, - "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", - "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_30", - "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - } - }, - "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", - "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_44", - "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - } - }, - "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", - "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_8", - "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - } - }, - "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", - "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_31", - "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - } - }, - "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", - "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_36", - "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - } - }, - "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", - "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_16", - "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - } - }, - "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", - "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_32", - "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - } - }, - "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", - "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_51", - "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - } - }, - "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", - "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_26", - "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - } - }, - "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", - "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_33", - "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - } - }, - "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", - "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_35", - "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - } - }, - "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", - "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_17", - "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - } - }, - "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", - "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_34", - "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - } - }, - "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", - "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 2, - "amount": 0, - "fee": 0, - "recipientId": null, - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "delegate": { - "username": "genesis_43", - "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - } - }, - "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", - "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", - "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", - "timestamp": 0, - "asset": { - "votes": [ - "+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" - ] - }, - "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", - "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", - "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", - "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", - "timestamp": 0, - "asset": { - "votes": [ - "+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" - ] - }, - "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", - "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", - "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", - "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", - "timestamp": 0, - "asset": { - "votes": [ - "+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" - ] - }, - "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", - "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", - "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", - "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", - "timestamp": 0, - "asset": { - "votes": [ - "+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" - ] - }, - "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", - "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", - "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", - "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", - "timestamp": 0, - "asset": { - "votes": [ - "+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" - ] - }, - "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", - "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", - "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", - "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", - "timestamp": 0, - "asset": { - "votes": [ - "+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" - ] - }, - "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", - "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", - "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", - "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", - "timestamp": 0, - "asset": { - "votes": [ - "+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" - ] - }, - "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", - "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", - "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", - "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", - "timestamp": 0, - "asset": { - "votes": [ - "+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" - ] - }, - "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", - "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", - "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", - "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", - "timestamp": 0, - "asset": { - "votes": [ - "+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" - ] - }, - "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", - "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", - "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", - "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", - "timestamp": 0, - "asset": { - "votes": [ - "+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" - ] - }, - "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", - "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", - "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", - "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", - "timestamp": 0, - "asset": { - "votes": [ - "+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" - ] - }, - "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", - "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", - "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", - "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", - "timestamp": 0, - "asset": { - "votes": [ - "+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" - ] - }, - "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", - "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", - "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", - "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", - "timestamp": 0, - "asset": { - "votes": [ - "+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" - ] - }, - "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", - "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", - "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", - "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", - "timestamp": 0, - "asset": { - "votes": [ - "+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" - ] - }, - "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", - "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", - "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", - "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", - "timestamp": 0, - "asset": { - "votes": [ - "+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" - ] - }, - "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", - "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", - "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", - "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", - "timestamp": 0, - "asset": { - "votes": [ - "+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" - ] - }, - "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", - "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", - "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", - "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", - "timestamp": 0, - "asset": { - "votes": [ - "+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" - ] - }, - "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", - "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", - "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", - "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", - "timestamp": 0, - "asset": { - "votes": [ - "+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" - ] - }, - "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", - "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", - "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", - "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", - "timestamp": 0, - "asset": { - "votes": [ - "+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" - ] - }, - "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", - "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", - "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", - "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", - "timestamp": 0, - "asset": { - "votes": [ - "+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" - ] - }, - "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", - "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", - "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", - "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", - "timestamp": 0, - "asset": { - "votes": [ - "+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" - ] - }, - "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", - "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", - "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", - "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", - "timestamp": 0, - "asset": { - "votes": [ - "+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" - ] - }, - "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", - "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", - "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", - "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", - "timestamp": 0, - "asset": { - "votes": [ - "+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" - ] - }, - "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", - "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", - "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", - "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", - "timestamp": 0, - "asset": { - "votes": [ - "+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" - ] - }, - "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", - "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", - "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", - "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", - "timestamp": 0, - "asset": { - "votes": [ - "+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" - ] - }, - "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", - "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", - "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", - "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", - "timestamp": 0, - "asset": { - "votes": [ - "+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" - ] - }, - "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", - "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", - "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", - "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", - "timestamp": 0, - "asset": { - "votes": [ - "+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" - ] - }, - "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", - "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", - "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", - "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", - "timestamp": 0, - "asset": { - "votes": [ - "+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" - ] - }, - "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", - "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", - "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", - "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", - "timestamp": 0, - "asset": { - "votes": [ - "+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" - ] - }, - "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", - "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", - "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", - "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", - "timestamp": 0, - "asset": { - "votes": [ - "+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" - ] - }, - "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", - "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", - "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", - "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", - "timestamp": 0, - "asset": { - "votes": [ - "+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" - ] - }, - "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", - "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", - "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", - "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", - "timestamp": 0, - "asset": { - "votes": [ - "+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" - ] - }, - "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", - "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", - "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", - "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", - "timestamp": 0, - "asset": { - "votes": [ - "+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" - ] - }, - "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", - "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", - "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", - "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", - "timestamp": 0, - "asset": { - "votes": [ - "+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" - ] - }, - "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", - "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", - "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", - "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", - "timestamp": 0, - "asset": { - "votes": [ - "+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" - ] - }, - "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", - "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", - "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", - "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", - "timestamp": 0, - "asset": { - "votes": [ - "+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" - ] - }, - "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", - "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", - "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", - "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", - "timestamp": 0, - "asset": { - "votes": [ - "+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" - ] - }, - "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", - "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", - "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", - "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", - "timestamp": 0, - "asset": { - "votes": [ - "+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" - ] - }, - "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", - "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", - "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", - "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", - "timestamp": 0, - "asset": { - "votes": [ - "+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" - ] - }, - "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", - "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", - "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", - "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", - "timestamp": 0, - "asset": { - "votes": [ - "+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" - ] - }, - "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", - "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", - "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", - "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", - "timestamp": 0, - "asset": { - "votes": [ - "+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" - ] - }, - "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", - "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", - "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", - "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", - "timestamp": 0, - "asset": { - "votes": [ - "+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" - ] - }, - "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", - "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", - "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", - "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", - "timestamp": 0, - "asset": { - "votes": [ - "+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" - ] - }, - "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", - "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", - "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", - "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", - "timestamp": 0, - "asset": { - "votes": [ - "+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" - ] - }, - "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", - "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", - "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", - "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", - "timestamp": 0, - "asset": { - "votes": [ - "+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" - ] - }, - "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", - "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", - "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", - "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", - "timestamp": 0, - "asset": { - "votes": [ - "+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" - ] - }, - "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", - "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", - "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", - "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", - "timestamp": 0, - "asset": { - "votes": [ - "+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" - ] - }, - "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", - "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", - "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", - "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", - "timestamp": 0, - "asset": { - "votes": [ - "+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" - ] - }, - "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", - "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", - "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", - "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", - "timestamp": 0, - "asset": { - "votes": [ - "+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" - ] - }, - "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", - "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", - "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", - "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", - "timestamp": 0, - "asset": { - "votes": [ - "+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" - ] - }, - "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", - "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", - "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" - }, - { - "type": 3, - "amount": 0, - "fee": 0, - "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", - "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", - "timestamp": 0, - "asset": { - "votes": [ - "+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" - ] - }, - "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", - "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", - "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" - } - ], - "height": 1, - "id": "17184958558311101492", - "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" -} diff --git a/packages/core/lib/config/testnet/peers.json b/packages/core/lib/config/testnet/peers.json deleted file mode 100644 index eca3695bf4..0000000000 --- a/packages/core/lib/config/testnet/peers.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "minimumVersion": ">=2.0.12", - "minimumNetworkReach": 5, - "globalTimeout": 5000, - "coldStart": 30, - "whiteList": [], - "blackList": [], - "list": [ - { - "ip": "127.0.0.1", - "port": 4000 - } - ] -} diff --git a/packages/core/lib/config/testnet/plugins.js b/packages/core/lib/config/testnet/plugins.js deleted file mode 100644 index 04b85febc0..0000000000 --- a/packages/core/lib/config/testnet/plugins.js +++ /dev/null @@ -1,73 +0,0 @@ -module.exports = { - '@arkecosystem/core-event-emitter': {}, - '@arkecosystem/core-config': {}, - '@arkecosystem/core-logger-winston': { - transports: { - console: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - dailyRotate: { - options: { - level: process.env.ARK_LOG_LEVEL || 'debug', - }, - }, - }, - }, - '@arkecosystem/core-database-postgres': { - connection: { - host: process.env.ARK_DB_HOST || 'localhost', - port: process.env.ARK_DB_PORT || 5432, - database: - process.env.ARK_DB_DATABASE || `ark_${process.env.ARK_NETWORK_NAME}`, - user: process.env.ARK_DB_USERNAME || 'ark', - password: process.env.ARK_DB_PASSWORD || 'password', - }, - }, - '@arkecosystem/core-transaction-pool-mem': { - enabled: true, - maxTransactionsPerSender: - process.env.ARK_TRANSACTION_POOL_MAX_PER_SENDER || 300, - allowedSenders: [], - }, - '@arkecosystem/core-p2p': { - host: process.env.ARK_P2P_HOST || '0.0.0.0', - port: process.env.ARK_P2P_PORT || 4000, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-blockchain': { - fastRebuild: false, - }, - '@arkecosystem/core-api': { - enabled: !process.env.ARK_API_DISABLED, - host: process.env.ARK_API_HOST || '0.0.0.0', - port: process.env.ARK_API_PORT || 4003, - whitelist: ['*'], - }, - '@arkecosystem/core-webhooks': { - enabled: process.env.ARK_WEBHOOKS_ENABLED, - server: { - enabled: process.env.ARK_WEBHOOKS_API_ENABLED, - host: process.env.ARK_WEBHOOKS_HOST || '0.0.0.0', - port: process.env.ARK_WEBHOOKS_PORT || 4004, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - }, - '@arkecosystem/core-graphql': { - enabled: process.env.ARK_GRAPHQL_ENABLED, - host: process.env.ARK_GRAPHQL_HOST || '0.0.0.0', - port: process.env.ARK_GRAPHQL_PORT || 4005, - }, - '@arkecosystem/core-forger': { - hosts: [`http://127.0.0.1:${process.env.ARK_P2P_PORT || 4000}`], - }, - '@arkecosystem/core-json-rpc': { - enabled: process.env.ARK_JSON_RPC_ENABLED, - host: process.env.ARK_JSON_RPC_HOST || '0.0.0.0', - port: process.env.ARK_JSON_RPC_PORT || 8080, - allowRemote: false, - whitelist: ['127.0.0.1', '::ffff:127.0.0.1'], - }, - '@arkecosystem/core-snapshots': {}, -} diff --git a/packages/core/lib/start-forger.js b/packages/core/lib/start-forger.js deleted file mode 100644 index d54b102ae0..0000000000 --- a/packages/core/lib/start-forger.js +++ /dev/null @@ -1,26 +0,0 @@ -const app = require('@arkecosystem/core-container') - -/** - * Start a forger. - * @param {Object} options - * @param {String} version - * @return {void} - */ -module.exports = async (options, version) => { - await app.setUp(version, options, { - include: [ - '@arkecosystem/core-event-emitter', - '@arkecosystem/core-config', - '@arkecosystem/core-logger', - '@arkecosystem/core-logger-winston', - '@arkecosystem/core-forger', - ], - options: { - '@arkecosystem/core-forger': { - bip38: options.bip38 || process.env.ARK_FORGER_BIP38, - address: options.address, - password: options.password || process.env.ARK_FORGER_PASSWORD, - }, - }, - }) -} diff --git a/packages/core/lib/start-relay-and-forger.js b/packages/core/lib/start-relay-and-forger.js deleted file mode 100644 index 51bd23c405..0000000000 --- a/packages/core/lib/start-relay-and-forger.js +++ /dev/null @@ -1,27 +0,0 @@ -const app = require('@arkecosystem/core-container') - -/** - * Start a relay and forger. - * @param {Object} options - * @param {String} version - * @return {void} - */ -module.exports = async (options, version) => { - await app.setUp(version, options, { - options: { - '@arkecosystem/core-p2p': { - networkStart: options.networkStart, - disableDiscovery: options.disableDiscovery, - skipDiscovery: options.skipDiscovery, - }, - '@arkecosystem/core-blockchain': { - networkStart: options.networkStart, - }, - '@arkecosystem/core-forger': { - bip38: options.bip38 || process.env.ARK_FORGER_BIP38, - address: options.address, - password: options.password || process.env.ARK_FORGER_PASSWORD, - }, - }, - }) -} diff --git a/packages/core/lib/start-relay.js b/packages/core/lib/start-relay.js deleted file mode 100644 index ebb9275c55..0000000000 --- a/packages/core/lib/start-relay.js +++ /dev/null @@ -1,23 +0,0 @@ -const app = require('@arkecosystem/core-container') - -/** - * Start a relay. - * @param {Object} options - * @param {String} version - * @return {void} - */ -module.exports = async (options, version) => { - await app.setUp(version, options, { - exclude: ['@arkecosystem/core-forger'], - options: { - '@arkecosystem/core-p2p': { - networkStart: options.networkStart, - disableDiscovery: options.disableDiscovery, - skipDiscovery: options.skipDiscovery, - }, - '@arkecosystem/core-blockchain': { - networkStart: options.networkStart, - }, - }, - }) -} diff --git a/packages/core/package.json b/packages/core/package.json index 4ae27796bd..b5a667c349 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,75 +1,96 @@ { - "name": "@arkecosystem/core", - "description": "Core of the Ark Blockchain", - "version": "2.0.19", - "contributors": [ - "François-Xavier Thoorens ", - "Kristjan Košič ", - "Brian Faust ", - "Alex Barnsley " - ], - "license": "MIT", - "bin": { - "ark:start": "./bin/ark start", - "ark:relay": "./bin/ark relay", - "ark:forger": "./bin/ark forger", - "ark:snapshot": "./bin/ark snapshot" - }, - "scripts": { - "debug:start": "node --inspect-brk ./bin/ark start", - "debug:relay": "node --inspect-brk ./bin/ark relay", - "debug:forger": "node --inspect-brk ./bin/ark forger", - "debug:snapshot": "node --inspect-brk ./bin/ark snapshot", - "start": "./bin/ark start", - "start:mainnet": "./bin/ark start --config ./lib/config/mainnet --network mainnet", - "start:devnet": "./bin/ark start --config ./lib/config/devnet --network devnet", - "start:testnet": "cross-env ARK_ENV=test ./bin/ark start --config ./lib/config/testnet --network testnet", - "start:testnet:live": "./bin/ark start --config ./lib/config/testnet.live --network testnet", - "relay": "./bin/ark relay", - "relay:mainnet": "./bin/ark relay --config ./lib/config/mainnet --network mainnet", - "relay:devnet": "./bin/ark relay --config ./lib/config/devnet --network devnet", - "relay:testnet": "cross-env ARK_ENV=test ./bin/ark relay --config ./lib/config/testnet --network testnet", - "relay:testnet:live": "./bin/ark relay --config ./lib/config/testnet.live --network testnet", - "forger": "./bin/ark forger", - "forger:mainnet": "./bin/ark forger --config ./lib/config/mainnet --network mainnet", - "forger:devnet": "./bin/ark forger --config ./lib/config/devnet --network devnet", - "forger:testnet": "cross-env ARK_ENV=test ./bin/ark forger --config ./lib/config/testnet --network testnet", - "forger:testnet:live": "./bin/ark forger --config ./lib/config/testnet.live --network testnet", - "snapshot": "./bin/ark snapshot", - "snapshot:mainnet": "./bin/ark snapshot --config ./lib/config/mainnet --network mainnet", - "snapshot:devnet": "./bin/ark snapshot --config ./lib/config/devnet --network devnet", - "snapshot:testnet": "./bin/ark snapshot --config ./lib/config/testnet --network testnet", - "snapshot:testnet:live": "./bin/ark snapshot --config ./lib/config/testnet.live --network testnet", - "full:testnet": "cross-env ARK_ENV=test ./bin/ark start --config ./lib/config/testnet --network testnet --network-start", - "full:testnet:live": "./bin/ark start --config ./lib/config/testnet.live --network testnet --network-start", - "full:testnet:2tier:2": "cross-env ARK_ENV=test ./bin/ark start --config ./lib/config/testnet.2 --network testnet --network-start", - "full:testnet:2tier:1": "cross-env ARK_ENV=test ./bin/ark start --config ./lib/config/testnet.1 --network testnet --network-start", - "full:testnet:2tier": "cross-env ARK_ENV=test ./bin/ark start --config ./lib/config/testnet.1 --network testnet --network-start && ./bin/ark start --config ./lib/config/testnet.2 --network testnet --network-start", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "@arkecosystem/core-api": "~0.2", - "@arkecosystem/core-blockchain": "~0.2", - "@arkecosystem/core-config": "~0.2", - "@arkecosystem/core-container": "~0.2", - "@arkecosystem/core-database-postgres": "~0.2", - "@arkecosystem/core-forger": "~0.2", - "@arkecosystem/core-graphql": "~0.2", - "@arkecosystem/core-json-rpc": "~0.2", - "@arkecosystem/core-logger-winston": "~0.2", - "@arkecosystem/core-p2p": "~0.2", - "@arkecosystem/core-snapshots": "~0.1", - "@arkecosystem/core-transaction-pool-mem": "~0.2", - "@arkecosystem/core-webhooks": "~0.2", - "@arkecosystem/crypto": "~0.2", - "bip38": "^2.0.2", - "commander": "^2.19.0", - "wif": "^2.0.6" - }, - "publishConfig": { - "access": "public" - }, - "engines": { - "node": ">=10.x" - } + "name": "@arkecosystem/core", + "description": "Core of the Ark Blockchain", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Kristjan Košič ", + "Brian Faust ", + "Alex Barnsley " + ], + "license": "MIT", + "main": "dist/index.js", + "files": [ + "dist" + ], + "bin": { + "core:start": "node ./dist/index.js start", + "core:relay": "node ./dist/index.js relay", + "core:forger": "node ./dist/index.js forger", + "core:snapshot": "node ./dist/index.js snapshot" + }, + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "debug:start": "node --inspect-brk node ./dist/index.js start", + "debug:relay": "node --inspect-brk node ./dist/index.js relay", + "debug:forger": "node --inspect-brk node ./dist/index.js forger", + "debug:snapshot": "node --inspect-brk node ./dist/index.js snapshot", + "start": "node ./dist/index.js start", + "start:mainnet": "node ./dist/index.js start --config ./src/config/mainnet --network mainnet", + "start:devnet": "node ./dist/index.js start --config ./src/config/devnet --network devnet", + "start:testnet": "cross-env CORE_ENV=test node ./dist/index.js start --config ./src/config/testnet --network testnet", + "start:testnet:live": "node ./dist/index.js start --config ./src/config/testnet.live --network testnet", + "relay": "node ./dist/index.js relay", + "relay:mainnet": "node ./dist/index.js relay --config ./src/config/mainnet --network mainnet", + "relay:devnet": "node ./dist/index.js relay --config ./src/config/devnet --network devnet", + "relay:testnet": "cross-env CORE_ENV=test node ./dist/index.js relay --config ./src/config/testnet --network testnet", + "relay:testnet:live": "node ./dist/index.js relay --config ./src/config/testnet.live --network testnet", + "forger": "node ./dist/index.js forger", + "forger:mainnet": "node ./dist/index.js forger --config ./src/config/mainnet --network mainnet", + "forger:devnet": "node ./dist/index.js forger --config ./src/config/devnet --network devnet", + "forger:testnet": "cross-env CORE_ENV=test node ./dist/index.js forger --config ./src/config/testnet --network testnet", + "forger:testnet:live": "node ./dist/index.js forger --config ./src/config/testnet.live --network testnet", + "snapshot": "node ./dist/index.js snapshot", + "snapshot:mainnet": "node ./dist/index.js snapshot --config ./src/config/mainnet --network mainnet", + "snapshot:devnet": "node ./dist/index.js snapshot --config ./src/config/devnet --network devnet", + "snapshot:testnet": "node ./dist/index.js snapshot --config ./src/config/testnet --network testnet", + "snapshot:testnet:live": "node ./dist/index.js snapshot --config ./src/config/testnet.live --network testnet", + "full:testnet": "cross-env CORE_ENV=test node ./dist/index.js start --config ./src/config/testnet --network testnet --network-start", + "full:testnet:live": "node ./dist/index.js start --config ./src/config/testnet.live --network testnet --network-start", + "full:testnet:2tier:2": "cross-env CORE_ENV=test node ./dist/index.js start --config ./src/config/testnet.2 --network testnet --network-start", + "full:testnet:2tier:1": "cross-env CORE_ENV=test node ./dist/index.js start --config ./src/config/testnet.1 --network testnet --network-start", + "full:testnet:2tier": "cross-env CORE_ENV=test node ./dist/index.js start --config ./src/config/testnet.1 --network testnet --network-start && node ./dist/index.js start --config ./src/config/testnet.2 --network testnet --network-start", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-api": "^2.1.0", + "@arkecosystem/core-blockchain": "^2.1.0", + "@arkecosystem/core-container": "^2.1.0", + "@arkecosystem/core-database-postgres": "^2.1.0", + "@arkecosystem/core-forger": "^2.1.0", + "@arkecosystem/core-graphql": "^2.1.0", + "@arkecosystem/core-json-rpc": "^2.1.0", + "@arkecosystem/core-logger-winston": "^2.1.0", + "@arkecosystem/core-p2p": "^2.1.0", + "@arkecosystem/core-snapshots": "^2.1.0", + "@arkecosystem/core-transaction-pool": "^2.1.0", + "@arkecosystem/core-webhooks": "^2.1.0", + "@arkecosystem/crypto": "^2.1.0", + "@types/bip38": "^2.0.0", + "@types/commander": "^2.12.2", + "@types/wif": "^2.0.1", + "commander": "^2.19.0", + "wif": "^2.0.6" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } } diff --git a/packages/core/src/commands/index.ts b/packages/core/src/commands/index.ts new file mode 100644 index 0000000000..54a3af1a74 --- /dev/null +++ b/packages/core/src/commands/index.ts @@ -0,0 +1,57 @@ +import { app } from "@arkecosystem/core-container"; +import { buildPeerOptions } from "../utils"; + +export async function startRelay(options, version) { + await app.setUp(version, options, { + exclude: ["@arkecosystem/core-forger"], + options: { + "@arkecosystem/core-p2p": buildPeerOptions(options), + "@arkecosystem/core-blockchain": { + networkStart: options.networkStart, + }, + }, + skipPlugins: options.skipPlugins, + }); + + return app; +} + +export async function startForger(options, version) { + await app.setUp(version, options, { + include: [ + "@arkecosystem/core-event-emitter", + "@arkecosystem/core-logger", + "@arkecosystem/core-logger-winston", + "@arkecosystem/core-forger", + ], + options: { + "@arkecosystem/core-forger": { + bip38: options.bip38 || process.env.CORE_FORGER_BIP38, + address: options.address, + password: options.password || process.env.CORE_FORGER_PASSWORD, + }, + }, + skipPlugins: options.skipPlugins, + }); + + return app; +} + +export async function startRelayAndForger(options, version) { + await app.setUp(version, options, { + options: { + "@arkecosystem/core-p2p": buildPeerOptions(options), + "@arkecosystem/core-blockchain": { + networkStart: options.networkStart, + }, + "@arkecosystem/core-forger": { + bip38: options.bip38 || process.env.CORE_FORGER_BIP38, + address: options.address, + password: options.password || process.env.CORE_FORGER_PASSWORD, + }, + }, + skipPlugins: options.skipPlugins, + }); + + return app; +} diff --git a/packages/core/src/config/devnet/delegates.json b/packages/core/src/config/devnet/delegates.json new file mode 100644 index 0000000000..6b44cbfeff --- /dev/null +++ b/packages/core/src/config/devnet/delegates.json @@ -0,0 +1,3 @@ +{ + "secrets": [] +} diff --git a/packages/core/src/config/devnet/peers.json b/packages/core/src/config/devnet/peers.json new file mode 100644 index 0000000000..dd8d460934 --- /dev/null +++ b/packages/core/src/config/devnet/peers.json @@ -0,0 +1,25 @@ +{ + "list": [ + { + "ip": "167.114.29.51", + "port": 4002 + }, + { + "ip": "167.114.29.52", + "port": 4002 + }, + { + "ip": "167.114.29.53", + "port": 4002 + }, + { + "ip": "167.114.29.54", + "port": 4002 + }, + { + "ip": "167.114.29.55", + "port": 4002 + } + ], + "sources": ["https://raw.githubusercontent.com/ArkEcosystem/peers/master/devnet.json"] +} diff --git a/packages/core/src/config/devnet/plugins.js b/packages/core/src/config/devnet/plugins.js new file mode 100644 index 0000000000..cfd80f5cf9 --- /dev/null +++ b/packages/core/src/config/devnet/plugins.js @@ -0,0 +1,87 @@ +module.exports = { + "@arkecosystem/core-event-emitter": {}, + "@arkecosystem/core-logger-winston": { + transports: { + console: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + dailyRotate: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + }, + }, + "@arkecosystem/core-database-postgres": { + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_${process.env.CORE_NETWORK_NAME}`, + user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + password: process.env.CORE_DB_PASSWORD || "password", + }, + }, + "@arkecosystem/core-transaction-pool": { + enabled: !process.env.CORE_TRANSACTION_POOL_DISABLED, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + dynamicFees: { + enabled: true, + minFeePool: 1000, + minFeeBroadcast: 1000, + addonBytes: { + transfer: 100, + secondSignature: 250, + delegateRegistration: 400000, + vote: 100, + multiSignature: 500, + ipfs: 250, + timelockTransfer: 500, + multiPayment: 500, + delegateResignation: 400000, + }, + }, + }, + "@arkecosystem/core-p2p": { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4002, + minimumNetworkReach: 5, + coldStart: 5, + }, + "@arkecosystem/core-blockchain": { + fastRebuild: false, + }, + "@arkecosystem/core-api": { + enabled: !process.env.CORE_API_DISABLED, + host: process.env.CORE_API_HOST || "0.0.0.0", + port: process.env.CORE_API_PORT || 4003, + whitelist: ["*"], + }, + "@arkecosystem/core-webhooks": { + enabled: process.env.CORE_WEBHOOKS_ENABLED, + server: { + enabled: process.env.CORE_WEBHOOKS_API_ENABLED, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + }, + "@arkecosystem/core-graphql": { + enabled: process.env.CORE_GRAPHQL_ENABLED, + host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", + port: process.env.CORE_GRAPHQL_PORT || 4005, + }, + "@arkecosystem/core-forger": { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4002}`], + }, + "@arkecosystem/core-json-rpc": { + enabled: process.env.CORE_JSON_RPC_ENABLED, + host: process.env.CORE_JSON_RPC_HOST || "0.0.0.0", + port: process.env.CORE_JSON_RPC_PORT || 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + "@arkecosystem/core-snapshots": {}, +}; diff --git a/packages/core/src/config/mainnet/delegates.json b/packages/core/src/config/mainnet/delegates.json new file mode 100644 index 0000000000..6b44cbfeff --- /dev/null +++ b/packages/core/src/config/mainnet/delegates.json @@ -0,0 +1,3 @@ +{ + "secrets": [] +} diff --git a/packages/core/src/config/mainnet/peers.json b/packages/core/src/config/mainnet/peers.json new file mode 100644 index 0000000000..3e03eeaf98 --- /dev/null +++ b/packages/core/src/config/mainnet/peers.json @@ -0,0 +1,261 @@ +{ + "list": [ + { + "ip": "5.196.105.32", + "port": 4001 + }, + { + "ip": "5.196.105.33", + "port": 4001 + }, + { + "ip": "5.196.105.34", + "port": 4001 + }, + { + "ip": "5.196.105.35", + "port": 4001 + }, + { + "ip": "5.196.105.36", + "port": 4001 + }, + { + "ip": "5.196.105.37", + "port": 4001 + }, + { + "ip": "5.196.105.38", + "port": 4001 + }, + { + "ip": "5.196.105.39", + "port": 4001 + }, + { + "ip": "178.32.65.136", + "port": 4001 + }, + { + "ip": "178.32.65.137", + "port": 4001 + }, + { + "ip": "178.32.65.138", + "port": 4001 + }, + { + "ip": "178.32.65.139", + "port": 4001 + }, + { + "ip": "178.32.65.140", + "port": 4001 + }, + { + "ip": "178.32.65.141", + "port": 4001 + }, + { + "ip": "178.32.65.142", + "port": 4001 + }, + { + "ip": "178.32.65.143", + "port": 4001 + }, + { + "ip": "5.196.105.40", + "port": 4001 + }, + { + "ip": "5.196.105.41", + "port": 4001 + }, + { + "ip": "5.196.105.42", + "port": 4001 + }, + { + "ip": "5.196.105.43", + "port": 4001 + }, + { + "ip": "5.196.105.44", + "port": 4001 + }, + { + "ip": "5.196.105.45", + "port": 4001 + }, + { + "ip": "5.196.105.46", + "port": 4001 + }, + { + "ip": "5.196.105.47", + "port": 4001 + }, + { + "ip": "54.38.120.32", + "port": 4001 + }, + { + "ip": "54.38.120.33", + "port": 4001 + }, + { + "ip": "54.38.120.34", + "port": 4001 + }, + { + "ip": "54.38.120.35", + "port": 4001 + }, + { + "ip": "54.38.120.36", + "port": 4001 + }, + { + "ip": "54.38.120.37", + "port": 4001 + }, + { + "ip": "54.38.120.38", + "port": 4001 + }, + { + "ip": "54.38.120.39", + "port": 4001 + }, + { + "ip": "151.80.125.32", + "port": 4001 + }, + { + "ip": "151.80.125.33", + "port": 4001 + }, + { + "ip": "151.80.125.34", + "port": 4001 + }, + { + "ip": "151.80.125.35", + "port": 4001 + }, + { + "ip": "151.80.125.36", + "port": 4001 + }, + { + "ip": "151.80.125.37", + "port": 4001 + }, + { + "ip": "151.80.125.38", + "port": 4001 + }, + { + "ip": "151.80.125.39", + "port": 4001 + }, + { + "ip": "213.32.41.104", + "port": 4001 + }, + { + "ip": "213.32.41.105", + "port": 4001 + }, + { + "ip": "213.32.41.106", + "port": 4001 + }, + { + "ip": "213.32.41.107", + "port": 4001 + }, + { + "ip": "213.32.41.108", + "port": 4001 + }, + { + "ip": "213.32.41.109", + "port": 4001 + }, + { + "ip": "213.32.41.110", + "port": 4001 + }, + { + "ip": "213.32.41.111", + "port": 4001 + }, + { + "ip": "5.135.22.92", + "port": 4001 + }, + { + "ip": "5.135.22.93", + "port": 4001 + }, + { + "ip": "5.135.22.94", + "port": 4001 + }, + { + "ip": "5.135.22.95", + "port": 4001 + }, + { + "ip": "5.135.52.96", + "port": 4001 + }, + { + "ip": "5.135.52.97", + "port": 4001 + }, + { + "ip": "5.135.52.98", + "port": 4001 + }, + { + "ip": "5.135.52.99", + "port": 4001 + }, + { + "ip": "51.255.105.52", + "port": 4001 + }, + { + "ip": "51.255.105.53", + "port": 4001 + }, + { + "ip": "51.255.105.54", + "port": 4001 + }, + { + "ip": "51.255.105.55", + "port": 4001 + }, + { + "ip": "46.105.160.104", + "port": 4001 + }, + { + "ip": "46.105.160.105", + "port": 4001 + }, + { + "ip": "46.105.160.106", + "port": 4001 + }, + { + "ip": "46.105.160.107", + "port": 4001 + } + ], + "sources": ["https://raw.githubusercontent.com/ArkEcosystem/peers/master/mainnet.json"] +} diff --git a/packages/core/src/config/mainnet/plugins.js b/packages/core/src/config/mainnet/plugins.js new file mode 100644 index 0000000000..fd61cb3d97 --- /dev/null +++ b/packages/core/src/config/mainnet/plugins.js @@ -0,0 +1,85 @@ +module.exports = { + "@arkecosystem/core-event-emitter": {}, + "@arkecosystem/core-logger-winston": { + transports: { + console: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + dailyRotate: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + }, + }, + "@arkecosystem/core-database-postgres": { + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_${process.env.CORE_NETWORK_NAME}`, + user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + password: process.env.CORE_DB_PASSWORD || "password", + }, + }, + "@arkecosystem/core-transaction-pool": { + enabled: !process.env.CORE_TRANSACTION_POOL_DISABLED, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + dynamicFees: { + enabled: true, + minFeePool: 3000, + minFeeBroadcast: 3000, + addonBytes: { + transfer: 100, + secondSignature: 250, + delegateRegistration: 400000, + vote: 100, + multiSignature: 500, + ipfs: 250, + timelockTransfer: 500, + multiPayment: 500, + delegateResignation: 400000, + }, + }, + }, + "@arkecosystem/core-p2p": { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4001, + }, + "@arkecosystem/core-blockchain": { + fastRebuild: false, + }, + "@arkecosystem/core-api": { + enabled: !process.env.CORE_API_DISABLED, + host: process.env.CORE_API_HOST || "0.0.0.0", + port: process.env.CORE_API_PORT || 4003, + whitelist: ["*"], + }, + "@arkecosystem/core-webhooks": { + enabled: process.env.CORE_WEBHOOKS_ENABLED, + server: { + enabled: process.env.CORE_WEBHOOKS_API_ENABLED, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + }, + "@arkecosystem/core-graphql": { + enabled: process.env.CORE_GRAPHQL_ENABLED, + host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", + port: process.env.CORE_GRAPHQL_PORT || 4005, + }, + "@arkecosystem/core-forger": { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4001}`], + }, + "@arkecosystem/core-json-rpc": { + enabled: process.env.CORE_JSON_RPC_ENABLED, + host: process.env.CORE_JSON_RPC_HOST || "0.0.0.0", + port: process.env.CORE_JSON_RPC_PORT || 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + "@arkecosystem/core-snapshots": {}, +}; diff --git a/packages/core/src/config/testnet/delegates.json b/packages/core/src/config/testnet/delegates.json new file mode 100644 index 0000000000..822250478e --- /dev/null +++ b/packages/core/src/config/testnet/delegates.json @@ -0,0 +1,55 @@ +{ + "secrets": [ + "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", + "venue below waste gather spin cruise title still boost mother flash tuna", + "craft imitate step mixture patch forest volcano business charge around girl confirm", + "fatal hat sail asset chase barrel pluck bag approve coral slab bright", + "flash thank strike stove grain remove match reflect excess present beyond matrix", + "various present shine domain outdoor neck soup diesel limit express genuine tuna", + "hurdle pulse sheriff anchor two hope income pattern hazard bacon book night", + "glow boss party require silk interest pyramid marriage try wisdom snow grab", + "direct palace screen shuffle world fit produce rubber jelly gather river ordinary", + "wall ketchup shed word twist flip knock liar merge rural ill pond", + "measure blue volcano month orphan only cupboard found laugh peasant drama monitor", + "scissors sort pause medal target diesel reveal stock maze party gauge vacant", + "hand anchor hip pyramid taxi vote celery clap tribe damage shrimp brave", + "merge thunder detect stove else bottom favorite doll learn festival basic basic", + "educate attitude rely combine treat balcony west reopen coil west grab depth", + "advance silver advance squeeze load stone middle garden perfect invest field lounge", + "prison tobacco acquire stone dignity palace note decade they current lesson robot", + "team impact stadium year security steak harsh vacant fire pelican until olympic", + "walk intact ice prevent fit trial frog glory monkey once grunt gentle", + "same lens parrot suspect just sunset frown exercise lemon two mistake robust", + "skill insect issue crazy erase okay govern upgrade bounce dress motor athlete", + "peasant alert hard deposit naive follow page fiscal normal awful wedding history", + "resemble abandon same total oppose noise dune order fatal rhythm pink science", + "wide mesh ketchup acquire bright day mountain final below hamster scout drive", + "half weasel poet better rocket fan help left blade soda argue system", + "target sort neutral address language spike measure jaguar glance strong drop zone", + "race total stage trap wool believe twin pudding claim claim eternal miss", + "parade isolate wing vague magic husband acid skin skate path fence rib", + "neither fine dry priority example obtain bread reopen afford coyote milk minor", + "token atom lemon game charge area goose hotel excess endless spice oblige", + "pledge buffalo finish pipe mule popular bind clinic draft salon swamp purpose", + "west hat hold stand unique panther cable extend spell shaft injury reopen", + "van impulse pole install profit excuse give auction expire remain skate input", + "wrist maze potato april survey burden bamboo knee foot carry speak prison", + "three toddler copy owner pencil minimum doctor orange bottom ice detail design", + "ceiling warrior person thing whisper jeans black cricket drift ahead tornado typical", + "obvious mutual tone usual valve credit soccer mention also clown main box", + "valve slot soft green scale menu anxiety live drill legend upgrade chimney", + "twist comfort mule weather print oven cabin seek punch rival prepare sphere", + "say tumble glass argue aware service force caution until grocery hammer fetch", + "idea illegal empty frozen canvas arctic number poet rely track size obscure", + "chalk try large tower shed warfare blade clerk fame second charge tobacco", + "category nice verb fox start able brass climb boss luggage voice whale", + "favorite emotion trumpet visual welcome spend fine lock image review garage opera", + "waste axis humor auction next salmon much margin useful glimpse insect rotate", + "remember rose genuine police guard old flavor parent gain cross twelve first", + "coil tray elder mask circle crush anger electric harbor onion grab will", + "shove airport bus gather radio derive below horse canvas crime tribe adjust", + "retire lend burden cricket able sheriff output grocery empty scorpion flat inquiry", + "agree grain record shift fossil summer hunt mutual net vast behind pilot", + "decide rhythm oyster lady they merry betray jelly coyote solve episode then" + ] +} diff --git a/packages/core/src/config/testnet/peers.json b/packages/core/src/config/testnet/peers.json new file mode 100644 index 0000000000..13faa40da6 --- /dev/null +++ b/packages/core/src/config/testnet/peers.json @@ -0,0 +1,8 @@ +{ + "list": [ + { + "ip": "127.0.0.1", + "port": 4000 + } + ] +} diff --git a/packages/core/src/config/testnet/plugins.js b/packages/core/src/config/testnet/plugins.js new file mode 100644 index 0000000000..efe107fe78 --- /dev/null +++ b/packages/core/src/config/testnet/plugins.js @@ -0,0 +1,87 @@ +module.exports = { + "@arkecosystem/core-event-emitter": {}, + "@arkecosystem/core-logger-winston": { + transports: { + console: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + dailyRotate: { + options: { + level: process.env.CORE_LOG_LEVEL || "debug", + }, + }, + }, + }, + "@arkecosystem/core-database-postgres": { + connection: { + host: process.env.CORE_DB_HOST || "localhost", + port: process.env.CORE_DB_PORT || 5432, + database: process.env.CORE_DB_DATABASE || `${process.env.CORE_TOKEN}_${process.env.CORE_NETWORK_NAME}`, + user: process.env.CORE_DB_USERNAME || process.env.CORE_TOKEN, + password: process.env.CORE_DB_PASSWORD || "password", + }, + }, + "@arkecosystem/core-transaction-pool": { + enabled: true, + maxTransactionsPerSender: process.env.CORE_TRANSACTION_POOL_MAX_PER_SENDER || 300, + allowedSenders: [], + dynamicFees: { + enabled: true, + minFeePool: 1000, + minFeeBroadcast: 1000, + addonBytes: { + transfer: 100, + secondSignature: 250, + delegateRegistration: 400000, + vote: 100, + multiSignature: 500, + ipfs: 250, + timelockTransfer: 500, + multiPayment: 500, + delegateResignation: 400000, + }, + }, + }, + "@arkecosystem/core-p2p": { + host: process.env.CORE_P2P_HOST || "0.0.0.0", + port: process.env.CORE_P2P_PORT || 4000, + minimumNetworkReach: 5, + coldStart: 5, + }, + "@arkecosystem/core-blockchain": { + fastRebuild: false, + }, + "@arkecosystem/core-api": { + enabled: !process.env.CORE_API_DISABLED, + host: process.env.CORE_API_HOST || "0.0.0.0", + port: process.env.CORE_API_PORT || 4003, + whitelist: ["*"], + }, + "@arkecosystem/core-webhooks": { + enabled: process.env.CORE_WEBHOOKS_ENABLED, + server: { + enabled: process.env.CORE_WEBHOOKS_API_ENABLED, + host: process.env.CORE_WEBHOOKS_HOST || "0.0.0.0", + port: process.env.CORE_WEBHOOKS_PORT || 4004, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + }, + "@arkecosystem/core-graphql": { + enabled: process.env.CORE_GRAPHQL_ENABLED, + host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", + port: process.env.CORE_GRAPHQL_PORT || 4005, + }, + "@arkecosystem/core-forger": { + hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4000}`], + }, + "@arkecosystem/core-json-rpc": { + enabled: process.env.CORE_JSON_RPC_ENABLED, + host: process.env.CORE_JSON_RPC_HOST || "0.0.0.0", + port: process.env.CORE_JSON_RPC_PORT || 8080, + allowRemote: false, + whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], + }, + "@arkecosystem/core-snapshots": {}, +}; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000000..1a646d6076 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,87 @@ +#!/usr/bin/env node + +import { bip38, configManager, crypto } from "@arkecosystem/crypto"; +import app from "commander"; +import fs from "fs"; +import wif from "wif"; +import { startForger, startRelay, startRelayAndForger } from "./commands"; + +// tslint:disable-next-line:no-var-requires +const { version } = require("../package.json"); + +app.version(version); + +function registerCommand(name: string, description: string): any { + return app + .command(name) + .description(description) + .option("-d, --data ", "data directory") + .option("-c, --config ", "core config") + .option("-t, --token ", "token name", "ark") + .option("-n, --network ", "token network") + .option("-r, --remote ", "remote peer for config") + .option("--network-start", "force genesis network start", false) + .option("--disable-discovery", "disable any peer discovery") + .option("--skip-discovery", "skip the initial peer discovery") + .option("--ignore-minimum-network-reach", "skip the network reach check") + .option("--launch-mode ", "remote peer for config"); +} + +registerCommand("start", "start a relay node and the forger") + .option("-b, --bip38 ", "forger bip38") + .option("-p, --password ", "forger password") + .action(async options => startRelayAndForger(options, version)); + +registerCommand("relay", "start a relay node").action(async options => startRelay(options, version)); + +registerCommand("forger", "start the forger") + .option("-b, --bip38 ", "forger bip38") + .option("-p, --password ", "forger password") + .action(async options => startForger(options, version)); + +registerCommand("forger-plain", "set the delegate secret") + .option("-s, --secret ", "forger secret") + .action(async options => { + const delegatesConfig = `${options.config}/delegates.json`; + if (!options.config || !fs.existsSync(delegatesConfig)) { + // tslint:disable-next-line:no-console + console.error("Missing or invalid delegates config path"); + process.exit(1); + } + const delegates = require(delegatesConfig); + delegates.secrets = [options.secret]; + delete delegates.bip38; + + fs.writeFileSync(delegatesConfig, JSON.stringify(delegates, null, 2)); + }); + +registerCommand("forger-bip38", "encrypt the delegate passphrase using bip38") + .option("-s, --secret ", "forger secret") + .option("-p, --password ", "bip38 password") + .action(async options => { + const delegatesConfig = `${options.config}/delegates.json`; + if (!options.config || !fs.existsSync(delegatesConfig)) { + // tslint:disable-next-line:no-console + console.error("Missing or invalid delegates config path"); + process.exit(1); + } + + configManager.setFromPreset(options.network); + + const keys = crypto.getKeys(options.secret); + // @ts-ignore + const decoded = wif.decode(crypto.keysToWIF(keys)); + + const delegates = require(delegatesConfig); + delegates.bip38 = bip38.encrypt(decoded.privateKey, decoded.compressed, options.password); + delegates.secrets = []; // remove the plain text secrets in favour of bip38 + + fs.writeFileSync(delegatesConfig, JSON.stringify(delegates, null, 2)); + }); + +app.command("*").action(env => { + app.help(); + process.exit(0); +}); + +app.parse(process.argv); diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts new file mode 100644 index 0000000000..268b1bafc5 --- /dev/null +++ b/packages/core/src/utils.ts @@ -0,0 +1,15 @@ +export function buildPeerOptions(options) { + const config = { + networkStart: options.networkStart, + disableDiscovery: options.disableDiscovery, + skipDiscovery: options.skipDiscovery, + ignoreMinimumNetworkReach: options.ignoreMinimumNetworkReach, + }; + + if (options.launchMode === "seed") { + config.skipDiscovery = true; + config.ignoreMinimumNetworkReach = true; + } + + return config; +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/crypto/CHANGELOG.md b/packages/crypto/CHANGELOG.md deleted file mode 100644 index 48f9c5c061..0000000000 --- a/packages/crypto/CHANGELOG.md +++ /dev/null @@ -1,95 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). - -## Unreleased - -## 0.2.6 - 2018-12-06 - -### Fixed - -- Perform second-signature checks in the `canApply` logic of multi-signatures - -## 0.2.5 - 2018-11-19 - -### Fixed - -- Added signWithWif to sign mixin used for vote builder - -## 0.2.4 - 2018-11-09 - -### Added - -- Address Identity -- Keys Identity -- Private Key Identity -- Public Key Identity -- WIF Identity - -### Fixed - -- Comparison of upper/lower-case public keys -- Validate the vendor field length -- Use network version in transaction builder when signing with mixin - -## 0.2.3 - 2018-10-26 - -### Added - -- Allow Message signing with WIF - -### Fixed - -- Use network WIF as default for WIF operations - -## 0.2.2 - 2018-10-23 - -### Added - -- Message signing -- Message verification - -## 0.2.1 - 2018-10-18 - -### Added - -- Sign transactions via WIF -- HDWallet handling - -### Changed - -- Exclude the network from the signing object -- Handle numerical values as `BigNumber` instances -- Change `transaction.serialized` from `Buffer` to hex - -### Fixed - -- Limit decimals to 0 to avoid floating numbers -- Properly verify block payload length -- Broken verification of faulty type 1 and 4 -- Broken multisignature serialization - -## 0.2.0 - 2018-09-17 - -### Changed - -- Improved overall performance of the crypto by calling the `secp256k1` methods directly instead of using a BTC package - -## 0.1.2 - 2018-08-10 - -### Fixed - -- Webpack build - -### Removed - -- Old and unused methods - -## 0.1.1 - 2018-06-14 - -### Added - -- initial release diff --git a/packages/crypto/LICENSE b/packages/crypto/LICENSE deleted file mode 100644 index d6dd75272f..0000000000 --- a/packages/crypto/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) Ark Ecosystem - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/crypto/README.md b/packages/crypto/README.md index 1e5cfead81..462718f2cd 100644 --- a/packages/crypto/README.md +++ b/packages/crypto/README.md @@ -36,12 +36,13 @@ If you discover a security vulnerability within this package, please send an e-m ## Credits -- [François-Xavier Thoorens](https://github.com/fix) -- [Brian Faust](https://github.com/faustbrian) -- [Lúcio Rubens](https://github.com/luciorubeens) -- [Alex Barnsley](https://github.com/alexbarnsley) -- [Juan A. Martín](https://github.com/j-a-m-l) -- [All Contributors](../../../../contributors) +- [Alex Barnsley](https://github.com/alexbarnsley) +- [Brian Faust](https://github.com/faustbrian) +- [François-Xavier Thoorens](https://github.com/fix) +- [Joshua Noack](https://github.com/supaiku0) +- [Juan A. Martín](https://github.com/j-a-m-l) +- [Lúcio Rubens](https://github.com/luciorubeens) +- [All Contributors](../../../../contributors) ## License diff --git a/packages/crypto/__tests__/builder/builder.test.js b/packages/crypto/__tests__/builder/builder.test.js deleted file mode 100644 index c0908c419d..0000000000 --- a/packages/crypto/__tests__/builder/builder.test.js +++ /dev/null @@ -1,7 +0,0 @@ -const transactionBuilder = require('../../lib/builder') - -describe('Builder', () => { - it('should be instantiated', () => { - expect(transactionBuilder).toBeObject() - }) -}) diff --git a/packages/crypto/__tests__/builder/transaction-builder-factory.test.ts b/packages/crypto/__tests__/builder/transaction-builder-factory.test.ts new file mode 100644 index 0000000000..16fc9d32b7 --- /dev/null +++ b/packages/crypto/__tests__/builder/transaction-builder-factory.test.ts @@ -0,0 +1,55 @@ +import "jest-extended"; + +import { DelegateRegistrationBuilder } from "../../src/builder/transactions/delegate-registration"; +import { DelegateResignationBuilder } from "../../src/builder/transactions/delegate-resignation"; +import { IPFSBuilder } from "../../src/builder/transactions/ipfs"; +import { MultiPaymentBuilder } from "../../src/builder/transactions/multi-payment"; +import { MultiSignatureBuilder } from "../../src/builder/transactions/multi-signature"; +import { SecondSignatureBuilder } from "../../src/builder/transactions/second-signature"; +import { TimelockTransferBuilder } from "../../src/builder/transactions/timelock-transfer"; +import { TransferBuilder } from "../../src/builder/transactions/transfer"; +import { VoteBuilder } from "../../src/builder/transactions/vote"; + +import { transactionBuilder, TransactionBuilderFactory } from "../../src/builder"; + +describe("Transaction Builder Factory", () => { + it("should be instantiated", () => { + expect(transactionBuilder).toBeInstanceOf(TransactionBuilderFactory); + }); + + it("should create DelegateRegistrationBuilder", () => { + expect(transactionBuilder.delegateRegistration()).toBeInstanceOf(DelegateRegistrationBuilder); + }); + + it("should create DelegateResignationBuilder", () => { + expect(transactionBuilder.delegateResignation()).toBeInstanceOf(DelegateResignationBuilder); + }); + + it("should create IPFSBuilder", () => { + expect(transactionBuilder.ipfs()).toBeInstanceOf(IPFSBuilder); + }); + + it("should create MultiPaymentBuilder", () => { + expect(transactionBuilder.multiPayment()).toBeInstanceOf(MultiPaymentBuilder); + }); + + it("should create MultiSignatureBuilder", () => { + expect(transactionBuilder.multiSignature()).toBeInstanceOf(MultiSignatureBuilder); + }); + + it("should create SecondSignatureBuilder", () => { + expect(transactionBuilder.secondSignature()).toBeInstanceOf(SecondSignatureBuilder); + }); + + it("should create TimelockTransferBuilder", () => { + expect(transactionBuilder.timelockTransfer()).toBeInstanceOf(TimelockTransferBuilder); + }); + + it("should create TransferBuilder", () => { + expect(transactionBuilder.transfer()).toBeInstanceOf(TransferBuilder); + }); + + it("should create VoteBuilder", () => { + expect(transactionBuilder.vote()).toBeInstanceOf(VoteBuilder); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/__shared__/transaction-builder.ts b/packages/crypto/__tests__/builder/transactions/__shared__/transaction-builder.ts new file mode 100644 index 0000000000..3f78f7e816 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/__shared__/transaction-builder.ts @@ -0,0 +1,207 @@ +import { TransactionBuilder } from "../../../../src/builder/transactions/transaction"; +import { crypto, slots } from "../../../../src/crypto"; +import { Transaction } from "../../../../src/models/transaction"; +import { Bignum } from "../../../../src/utils/bignum"; + +export const transactionBuilder = >(provider: () => TransactionBuilder) => { + describe("TransactionBuilder", () => { + describe("inherits = require(TransactionBuilder", () => { + it("should have the essential properties", () => { + const builder = provider(); + + expect(builder).toHaveProperty("data.id", null); + expect(builder).toHaveProperty("data.timestamp"); + expect(builder).toHaveProperty("data.version", 0x01); + + expect(builder).toHaveProperty("data.type"); + expect(builder).toHaveProperty("data.fee"); + }); + + describe("builder", () => { + let timestamp; + let data; + + beforeEach(() => { + timestamp = slots.getTime(); + + data = { + id: "fake-id", + amount: 0, + fee: 0, + recipientId: "DK2v39r3hD9Lw8R5fFFHjUyCtXm1VETi42", + senderPublicKey: "035440a82cb44faef75c3d7d881696530aac4d50da314b91795740cdbeaba9113c", + timestamp, + type: 0, + version: 0x03, + }; + }); + + it("should return a Transaction model with the builder data", () => { + const builder = provider(); + + builder.data = data; + + const transaction = builder.build(); + + expect(transaction).toBeInstanceOf(Transaction); + expect(transaction.amount).toEqual(Bignum.ZERO); + expect(transaction.fee).toEqual(Bignum.ZERO); + expect(transaction.recipientId).toBe("DK2v39r3hD9Lw8R5fFFHjUyCtXm1VETi42"); + expect(transaction.senderPublicKey).toBe( + "035440a82cb44faef75c3d7d881696530aac4d50da314b91795740cdbeaba9113c", + ); + expect(transaction.timestamp).toBe(timestamp); + expect(transaction.type).toBe(0); + expect(transaction.version).toBe(0x03); + }); + + it("could merge and override the builder data", () => { + const builder = provider(); + + builder.data = data; + + const transaction = builder.build({ + amount: 33, + fee: 1000, + }); + + expect(transaction).toBeInstanceOf(Transaction); + expect(transaction.amount).toEqual(new Bignum(33)); + expect(transaction.fee).toEqual(new Bignum(1000)); + expect(transaction.recipientId).toBe("DK2v39r3hD9Lw8R5fFFHjUyCtXm1VETi42"); + expect(transaction.senderPublicKey).toBe( + "035440a82cb44faef75c3d7d881696530aac4d50da314b91795740cdbeaba9113c", + ); + expect(transaction.timestamp).toBe(timestamp); + expect(transaction.version).toBe(0x03); + }); + }); + + describe("fee", () => { + it("should set the fee", () => { + const builder = provider(); + + builder.fee(255); + expect(builder.data.fee).toBe(255); + }); + }); + + describe("amount", () => { + it("should set the amount", () => { + const builder = provider(); + + builder.amount(255); + expect(builder.data.amount).toBe(255); + }); + }); + + describe("recipientId", () => { + it("should set the recipient id", () => { + const builder = provider(); + + builder.recipientId("fake"); + expect(builder.data.recipientId).toBe("fake"); + }); + }); + + describe("senderPublicKey", () => { + it("should set the sender public key", () => { + const builder = provider(); + + builder.senderPublicKey("fake"); + expect(builder.data.senderPublicKey).toBe("fake"); + }); + }); + }); + + describe("sign", () => { + it("signs this transaction with the keys of the passphrase", () => { + const builder = provider(); + const keys = { + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + }; + crypto.getKeys = jest.fn(() => keys); + crypto.sign = jest.fn(); + + builder.sign("dummy pass"); + + expect(crypto.getKeys).toHaveBeenCalledWith("dummy pass"); + expect(crypto.sign).toHaveBeenCalledWith((builder as any).getSigningObject(), keys); + }); + + it("establishes the public key of the sender", () => { + const builder = provider(); + const keys = { + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + }; + crypto.getKeys = jest.fn(() => keys); + crypto.sign = jest.fn(); + builder.sign("my real pass"); + expect(builder.data.senderPublicKey).toBe(keys.publicKey); + }); + }); + + describe("signWithWif", () => { + it("signs this transaction with keys from a wif", () => { + const builder = provider(); + const keys = { + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + }; + crypto.getKeysFromWIF = jest.fn(() => keys); + crypto.sign = jest.fn(); + + builder.network(23).signWithWif("dummy pass"); + + expect(crypto.getKeysFromWIF).toHaveBeenCalledWith("dummy pass", { + wif: 170, + }); + expect(crypto.sign).toHaveBeenCalledWith((builder as any).getSigningObject(), keys); + }); + + it("establishes the public key of the sender", () => { + const builder = provider(); + const keys = { + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + }; + crypto.getKeysFromWIF = jest.fn(() => keys); + crypto.sign = jest.fn(); + builder.signWithWif("my real pass"); + expect(builder.data.senderPublicKey).toBe(keys.publicKey); + }); + }); + + describe("secondSign", () => { + it("signs this transaction with the keys of the second passphrase", () => { + const builder = provider(); + let keys; + crypto.getKeys = jest.fn(pass => { + keys = { publicKey: `${pass} public key` }; + return keys; + }); + crypto.secondSign = jest.fn(); + + builder.secondSign("my very real second pass"); + + expect(crypto.getKeys).toHaveBeenCalledWith("my very real second pass"); + expect(crypto.secondSign).toHaveBeenCalledWith((builder as any).getSigningObject(), keys); + }); + }); + + describe("secondSignWithWif", () => { + it("signs this transaction with the keys of a second wif", () => { + const builder = provider(); + let keys; + crypto.getKeysFromWIF = jest.fn(pass => { + keys = { publicKey: `${pass} public key` }; + return keys; + }); + crypto.secondSign = jest.fn(); + + builder.network(23).secondSignWithWif("my very real second pass", null); + + expect(crypto.getKeysFromWIF).toHaveBeenCalledWith("my very real second pass", { wif: 170 }); + expect(crypto.secondSign).toHaveBeenCalledWith((builder as any).getSigningObject(), keys); + }); + }); + }); +}; diff --git a/packages/crypto/__tests__/builder/transactions/__shared__/transaction.js b/packages/crypto/__tests__/builder/transactions/__shared__/transaction.js deleted file mode 100644 index 608cb270e5..0000000000 --- a/packages/crypto/__tests__/builder/transactions/__shared__/transaction.js +++ /dev/null @@ -1,216 +0,0 @@ -const TransactionBuilder = require('../../../../lib/builder/transactions/transaction') -const Bignum = require('../../../../lib/utils/bignum') -const { crypto, slots } = require('../../../../lib/crypto') -const configManager = require('../../../../lib/managers/config') -const Transaction = require('../../../../lib/models/transaction') - -module.exports = () => { - let builder - - beforeEach(() => { - builder = global.builder - }) - - describe('inherits = require(TransactionBuilder', () => { - it('as an instance', () => { - expect(builder).toBeInstanceOf(TransactionBuilder) - }) - - it('should have the essential properties', () => { - expect(builder).toHaveProperty('data.id', null) - expect(builder).toHaveProperty('data.timestamp') - expect(builder).toHaveProperty('data.version', 0x01) - expect(builder).toHaveProperty( - 'data.network', - configManager.get('pubKeyHash'), - ) - - expect(builder).toHaveProperty('data.type') - expect(builder).toHaveProperty('data.fee') - }) - - describe('builder', () => { - let timestamp - let data - - beforeEach(() => { - timestamp = slots.getTime() - - data = { - id: 'fake-id', - amount: 0, - fee: 0, - recipientId: 'DK2v39r3hD9Lw8R5fFFHjUyCtXm1VETi42', - senderPublicKey: - '035440a82cb44faef75c3d7d881696530aac4d50da314b91795740cdbeaba9113c', - timestamp, - type: 0, - version: 0x03, - } - }) - - it('should return a Transaction model with the builder data', () => { - builder.data = data - - const transaction = builder.build() - - expect(transaction).toBeInstanceOf(Transaction) - expect(transaction.amount).toEqual(Bignum.ZERO) - expect(transaction.fee).toEqual(Bignum.ZERO) - expect(transaction.recipientId).toBe( - 'DK2v39r3hD9Lw8R5fFFHjUyCtXm1VETi42', - ) - expect(transaction.senderPublicKey).toBe( - '035440a82cb44faef75c3d7d881696530aac4d50da314b91795740cdbeaba9113c', - ) - expect(transaction.timestamp).toBe(timestamp) - expect(transaction.type).toBe(0) - expect(transaction.version).toBe(0x03) - }) - - it('could merge and override the builder data', () => { - builder.data = data - - const transaction = builder.build({ - amount: 33, - fee: 1000, - }) - - expect(transaction).toBeInstanceOf(Transaction) - expect(transaction.amount).toEqual(new Bignum(33)) - expect(transaction.fee).toEqual(new Bignum(1000)) - expect(transaction.recipientId).toBe( - 'DK2v39r3hD9Lw8R5fFFHjUyCtXm1VETi42', - ) - expect(transaction.senderPublicKey).toBe( - '035440a82cb44faef75c3d7d881696530aac4d50da314b91795740cdbeaba9113c', - ) - expect(transaction.timestamp).toBe(timestamp) - expect(transaction.version).toBe(0x03) - }) - }) - - describe('fee', () => { - it('should set the fee', () => { - builder.fee(255) - expect(builder.data.fee).toBe(255) - }) - }) - - describe('amount', () => { - it('should set the amount', () => { - builder.amount(255) - expect(builder.data.amount).toBe(255) - }) - }) - - describe('recipientId', () => { - it('should set the recipient id', () => { - builder.recipientId('fake') - expect(builder.data.recipientId).toBe('fake') - }) - }) - - describe('senderPublicKey', () => { - it('should set the sender public key', () => { - builder.senderPublicKey('fake') - expect(builder.data.senderPublicKey).toBe('fake') - }) - }) - }) - - describe('sign', () => { - it('signs this transaction with the keys of the passphrase', () => { - const keys = { - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - } - crypto.getKeys = jest.fn(() => keys) - crypto.sign = jest.fn() - const signingObject = builder.__getSigningObject() - - builder.sign('dummy pass') - - expect(crypto.getKeys).toHaveBeenCalledWith('dummy pass') - expect(crypto.sign).toHaveBeenCalledWith(signingObject, keys) - }) - - it('establishes the public key of the sender', () => { - const keys = { - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - } - crypto.getKeys = jest.fn(() => keys) - crypto.sign = jest.fn() - builder.sign('my real pass') - expect(builder.data.senderPublicKey).toBe(keys.publicKey) - }) - }) - - describe('signWithWif', () => { - it('signs this transaction with keys from a wif', () => { - const keys = { - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - } - crypto.getKeysFromWIF = jest.fn(() => keys) - crypto.sign = jest.fn() - const signingObject = builder.__getSigningObject() - - builder.network(23).signWithWif('dummy pass') - - expect(crypto.getKeysFromWIF).toHaveBeenCalledWith('dummy pass', { - wif: 170, - }) - expect(crypto.sign).toHaveBeenCalledWith(signingObject, keys) - }) - - it('establishes the public key of the sender', () => { - const keys = { - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - } - crypto.getKeysFromWIF = jest.fn(() => keys) - crypto.sign = jest.fn() - builder.signWithWif('my real pass') - expect(builder.data.senderPublicKey).toBe(keys.publicKey) - }) - }) - - describe('secondSign', () => { - it('signs this transaction with the keys of the second passphrase', () => { - let keys - crypto.getKeys = jest.fn(pass => { - keys = { publicKey: `${pass} public key` } - return keys - }) - crypto.secondSign = jest.fn() - const signingObject = builder.__getSigningObject() - - builder.secondSign('my very real second pass') - - expect(crypto.getKeys).toHaveBeenCalledWith('my very real second pass') - expect(crypto.secondSign).toHaveBeenCalledWith(signingObject, keys) - }) - }) - - describe('secondSignWithWif', () => { - it('signs this transaction with the keys of a second wif', () => { - let keys - crypto.getKeysFromWIF = jest.fn(pass => { - keys = { publicKey: `${pass} public key` } - return keys - }) - crypto.secondSign = jest.fn() - const signingObject = builder.__getSigningObject() - - builder.network(23).secondSignWithWif('my very real second pass') - - expect(crypto.getKeysFromWIF).toHaveBeenCalledWith( - 'my very real second pass', - { wif: 170 }, - ) - expect(crypto.secondSign).toHaveBeenCalledWith(signingObject, keys) - }) - }) -} diff --git a/packages/crypto/__tests__/builder/transactions/delegate-registration.test.js b/packages/crypto/__tests__/builder/transactions/delegate-registration.test.js deleted file mode 100644 index 51fee59edd..0000000000 --- a/packages/crypto/__tests__/builder/transactions/delegate-registration.test.js +++ /dev/null @@ -1,134 +0,0 @@ -const ark = require('../../../lib/client') -const crypto = require('../../../lib/crypto/crypto') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().delegateRegistration() - - global.builder = builder -}) - -describe('Delegate Registration Transaction', () => { - describe('verify', () => { - it('should be valid with a signature', () => { - const actual = builder.usernameAsset('homer').sign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - - it('should be valid with a second signature', () => { - const actual = builder - .usernameAsset('homer') - .sign('dummy passphrase') - .secondSign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - }) - - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty( - 'data.type', - TRANSACTION_TYPES.DELEGATE_REGISTRATION, - ) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.DELEGATE_REGISTRATION), - ) - expect(builder).toHaveProperty('data.recipientId', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.asset', { delegate: {} }) - }) - - it('should not have the username yet', () => { - expect(builder).not.toHaveProperty('data.username') - }) - - describe('usernameAsset', () => { - it('establishes the username of the asset', () => { - builder.usernameAsset('homer') - expect(builder.data.asset.delegate.username).toBe('homer') - }) - }) - - describe('sign', () => { - it('establishes the public key of the delegate (on the asset property)', () => { - crypto.getKeys = jest.fn(pass => ({ publicKey: `${pass} public key` })) - crypto.sign = jest.fn(() => 'signature') - builder.sign('bad pass') - expect(builder.data.asset.delegate.publicKey).toBe('bad pass public key') - }) - }) - - // FIXME problems with ark-js V1 - describe('getStruct', () => { - beforeEach(() => { - builder = builder.usernameAsset('homer') - }) - it('should fail if the transaction is not signed', () => { - try { - expect(() => builder.getStruct()).toThrow(/transaction.*sign/) - expect('fail').toBe('this should fail when no error is thrown') - } catch (_error) { - expect(() => builder.sign('example pass').getStruct()).not.toThrow() - } - }) - - describe('when is signed', () => { - beforeEach(() => { - builder.sign('any pass') - }) - - // NOTE: V2 - it.skip('generates and returns the bytes as hex', () => { - expect(builder.getStruct().hex).toBe( - crypto.getBytes(builder.data).toString('hex'), - ) - }) - it('returns the id', () => { - expect(builder.getStruct().id).toBe( - crypto.getId(builder.data).toString('hex'), - ) - }) - it('returns the signature', () => { - expect(builder.getStruct().signature).toBe(builder.data.signature) - }) - it('returns the second signature', () => { - expect(builder.getStruct().secondSignature).toBe( - builder.data.secondSignature, - ) - }) - it('returns the timestamp', () => { - expect(builder.getStruct().timestamp).toBe(builder.data.timestamp) - }) - it('returns the transaction type', () => { - expect(builder.getStruct().type).toBe(builder.data.type) - }) - it('returns the fee', () => { - expect(builder.getStruct().fee).toBe(builder.data.fee) - }) - it('returns the sender public key', () => { - expect(builder.getStruct().senderPublicKey).toBe( - builder.data.senderPublicKey, - ) - }) - - it('returns the amount', () => { - expect(builder.getStruct().amount).toBe(builder.data.amount) - }) - it('returns the recipient id', () => { - expect(builder.getStruct().recipientId).toBe(builder.data.recipientId) - }) - it('returns the asset', () => { - expect(builder.getStruct().asset).toBe(builder.data.asset) - }) - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/delegate-registration.test.ts b/packages/crypto/__tests__/builder/transactions/delegate-registration.test.ts new file mode 100644 index 0000000000..25a57724e5 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/delegate-registration.test.ts @@ -0,0 +1,118 @@ +import "jest-extended"; +import { DelegateRegistrationBuilder } from "../../../src/builder/transactions/delegate-registration"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { crypto } from "../../../src/crypto/crypto"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: DelegateRegistrationBuilder; + +describe("Delegate Registration Transaction", () => { + describe("verify", () => { + beforeEach(() => { + builder = client.getBuilder().delegateRegistration(); + }); + + it("should be valid with a signature", () => { + const actual = builder.usernameAsset("homer").sign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + + it("should be valid with a second signature", () => { + const actual = builder + .usernameAsset("homer") + .sign("dummy passphrase") + .secondSign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + }); + + describe("properties", () => { + beforeEach(() => { + builder = client.getBuilder().delegateRegistration(); + }); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.DelegateRegistration); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.DelegateRegistration)); + expect(builder).toHaveProperty("data.recipientId", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.asset", { delegate: {} }); + }); + + it("should not have the username yet", () => { + expect(builder).not.toHaveProperty("data.username"); + }); + }); + + describe("usernameAsset", () => { + beforeEach(() => { + builder = client.getBuilder().delegateRegistration(); + }); + it("establishes the username of the asset", () => { + builder.usernameAsset("homer"); + expect(builder.data.asset.delegate.username).toBe("homer"); + }); + }); + + // FIXME problems with ark-js V1 + describe("getStruct", () => { + beforeEach(() => { + builder = client + .getBuilder() + .delegateRegistration() + .usernameAsset("homer"); + }); + + it("should fail if the transaction is not signed", () => { + expect(() => builder.getStruct()).toThrow(/transaction.*sign/); + }); + + describe("when is signed", () => { + beforeEach(() => { + builder.sign("any pass"); + }); + + it("returns the id", () => { + expect(builder.getStruct().id).toBe( + // @ts-ignore + crypto.getId(builder.data).toString("hex"), + ); + }); + it("returns the signature", () => { + expect(builder.getStruct().signature).toBe(builder.data.signature); + }); + it("returns the second signature", () => { + expect(builder.getStruct().secondSignature).toBe(builder.data.secondSignature); + }); + it("returns the timestamp", () => { + expect(builder.getStruct().timestamp).toBe(builder.data.timestamp); + }); + it("returns the transaction type", () => { + expect(builder.getStruct().type).toBe(builder.data.type); + }); + it("returns the fee", () => { + expect(builder.getStruct().fee).toBe(builder.data.fee); + }); + it("returns the sender public key", () => { + expect(builder.getStruct().senderPublicKey).toBe(builder.data.senderPublicKey); + }); + + it("returns the amount", () => { + expect(builder.getStruct().amount).toBe(builder.data.amount); + }); + it("returns the recipient id", () => { + expect(builder.getStruct().recipientId).toBe(builder.data.recipientId); + }); + it("returns the asset", () => { + expect(builder.getStruct().asset).toBe(builder.data.asset); + }); + }); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/delegate-resignation.test.js b/packages/crypto/__tests__/builder/transactions/delegate-resignation.test.js deleted file mode 100644 index 6a036a0a74..0000000000 --- a/packages/crypto/__tests__/builder/transactions/delegate-resignation.test.js +++ /dev/null @@ -1,27 +0,0 @@ -const ark = require('../../../lib/client') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().delegateResignation() - - global.builder = builder -}) - -describe('Delegate Resignation Transaction', () => { - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty( - 'data.type', - TRANSACTION_TYPES.DELEGATE_RESIGNATION, - ) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.DELEGATE_RESIGNATION), - ) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/delegate-resignation.test.ts b/packages/crypto/__tests__/builder/transactions/delegate-resignation.test.ts new file mode 100644 index 0000000000..dfe6587d40 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/delegate-resignation.test.ts @@ -0,0 +1,21 @@ +import "jest-extended"; +import { DelegateResignationBuilder } from "../../../src/builder/transactions/delegate-resignation"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: DelegateResignationBuilder; + +beforeEach(() => { + builder = client.getBuilder().delegateResignation(); +}); + +describe("Delegate Resignation Transaction", () => { + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.DelegateResignation); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.DelegateResignation)); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/ipfs.test.js b/packages/crypto/__tests__/builder/transactions/ipfs.test.js deleted file mode 100644 index 1512e85e70..0000000000 --- a/packages/crypto/__tests__/builder/transactions/ipfs.test.js +++ /dev/null @@ -1,53 +0,0 @@ -const ark = require('../../../lib/client') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().ipfs() - - global.builder = builder -}) - -describe('IPFS Transaction', () => { - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty('data.type', TRANSACTION_TYPES.IPFS) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.IPFS), - ) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty('data.vendorFieldHex', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.asset', {}) - }) - - it('should not have the IPFS hash yet', () => { - expect(builder).not.toHaveProperty('data.ipfsHash') - }) - - describe('ipfsHash', () => { - it('establishes the IPFS hash', () => { - builder.ipfsHash('zyx') - expect(builder.data.ipfsHash).toBe('zyx') - }) - }) - - describe('vendorField', () => { - // TODO This is test is OK, but the Subject Under Test might be wrong, - // so it is better to not assume that this is the desired behaviour - it('should generate and set the vendorFieldHex', () => { - const data = 'hash' - const hex = Buffer.from(data, 0).toString('hex') - const paddedHex = hex.padStart(128, '0') - - builder.data.ipfsHash = data - builder.vendorField(0) - expect(builder.data.vendorFieldHex).toBe(paddedHex) - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/ipfs.test.ts b/packages/crypto/__tests__/builder/transactions/ipfs.test.ts new file mode 100644 index 0000000000..a21fffe909 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/ipfs.test.ts @@ -0,0 +1,47 @@ +import "jest-extended"; +import { IPFSBuilder } from "../../../src/builder/transactions/ipfs"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: IPFSBuilder; + +beforeEach(() => { + builder = client.getBuilder().ipfs(); +}); + +describe("IPFS Transaction", () => { + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.Ipfs); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.Ipfs)); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.vendorFieldHex", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.asset", {}); + }); + + it("should not have the IPFS hash yet", () => { + expect(builder).not.toHaveProperty("data.ipfsHash"); + }); + + it("establishes the IPFS hash", () => { + builder.ipfsHash("zyx"); + expect(builder.data.ipfsHash).toBe("zyx"); + }); + + // TODO This is test is OK, but the Subject Under Test might be wrong, + // so it is better to not assume that this is the desired behaviour + it("should generate and set the vendorFieldHex", () => { + const data = "hash"; + // @ts-ignore + const hex: any = Buffer.from(data, 0).toString("hex"); + const paddedHex = hex.padStart(128, "0"); + + builder.data.ipfsHash = data; + builder.vendorField(""); + expect(builder.data.vendorFieldHex).toBe(paddedHex); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/multi-payment.test.js b/packages/crypto/__tests__/builder/transactions/multi-payment.test.js deleted file mode 100644 index f957c70315..0000000000 --- a/packages/crypto/__tests__/builder/transactions/multi-payment.test.js +++ /dev/null @@ -1,51 +0,0 @@ -const ark = require('../../../lib/client') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().multiPayment() - - global.builder = builder -}) - -describe('Multi Payment Transaction', () => { - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty('data.type', TRANSACTION_TYPES.MULTI_PAYMENT) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.MULTI_PAYMENT), - ) - expect(builder).toHaveProperty('data.payments', {}) - expect(builder).toHaveProperty('data.vendorFieldHex', null) - }) - - describe('vendorField', () => { - it('should set the vendorField', () => { - const data = 'dummy' - builder.vendorField(data) - expect(builder.data.vendorField).toBe(data) - }) - }) - - describe('addPayment', () => { - it('should add new payments', () => { - builder.addPayment('address', 'amount') - builder.addPayment('address', 'amount') - builder.addPayment('address', 'amount') - - expect(builder.data.payments).toEqual({ - address1: 'address', - address2: 'address', - address3: 'address', - amount1: 'amount', - amount2: 'amount', - amount3: 'amount', - }) - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/multi-payment.test.ts b/packages/crypto/__tests__/builder/transactions/multi-payment.test.ts new file mode 100644 index 0000000000..7a7342acd1 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/multi-payment.test.ts @@ -0,0 +1,62 @@ +import "jest-extended"; +import { MultiPaymentBuilder } from "../../../src/builder/transactions/multi-payment"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { MaximumPaymentCountExceededError } from "../../../src/errors"; +import { feeManager } from "../../../src/managers/fee"; +import { Bignum } from "../../../src/utils"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: MultiPaymentBuilder; + +beforeEach(() => { + builder = client.getBuilder().multiPayment(); +}); + +describe("Multi Payment Transaction", () => { + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.MultiPayment); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.MultiPayment)); + expect(builder).toHaveProperty("data.asset.payments", []); + expect(builder).toHaveProperty("data.vendorFieldHex", null); + }); + + describe("vendorField", () => { + it("should set the vendorField", () => { + const data = "dummy"; + builder.vendorField(data); + expect(builder.data.vendorField).toBe(data); + }); + }); + + describe("addPayment", () => { + it("should add new payments", () => { + builder.addPayment("address", 1); + builder.addPayment("address", 2); + builder.addPayment("address", 3); + + expect(builder.data.asset.payments).toEqual([ + { + amount: new Bignum(1), + recipientId: "address", + }, + { + amount: new Bignum(2), + recipientId: "address", + }, + { + amount: new Bignum(3), + recipientId: "address", + }, + ]); + }); + + it("should throw if we want to add more payments than max authorized", () => { + builder.data.asset.payments = new Array(2258); + + expect(() => builder.addPayment("address", 2)).toThrow(MaximumPaymentCountExceededError); + }); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/multi-signature.test.js b/packages/crypto/__tests__/builder/transactions/multi-signature.test.js deleted file mode 100644 index 2098a0de27..0000000000 --- a/packages/crypto/__tests__/builder/transactions/multi-signature.test.js +++ /dev/null @@ -1,100 +0,0 @@ -const ark = require('../../../lib/client') -const crypto = require('../../../lib/crypto/crypto') -const feeManager = require('../../../lib/managers/fee') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().multiSignature() - - global.builder = builder -}) - -describe('Multi Signature Transaction', () => { - describe('verify', () => { - it('should be valid with a signature', () => { - const actual = builder - .multiSignatureAsset({ - keysgroup: [ - '+0376982a97dadbc65e694743d386084548a65431a82ce935ac9d957b1cffab2784', - '+03793904e0df839809bc89f2839e1ae4f8b1ea97ede6592b7d1e4d0ee194ca2998', - '+03e710267cdbc87cf8c2f32a6c3f22e1d1ce22ba30e1915360f511a2b16df8c5a5', - ], - lifetime: 72, - min: 2, - }) - .sign('dummy passphrase') - .multiSignatureSign('multi passphrase 1') - .multiSignatureSign('multi passphrase 2') - .multiSignatureSign('multi passphrase 3') - - expect(actual.build().verify()).toBeTrue() - }) - }) - - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty( - 'data.type', - TRANSACTION_TYPES.MULTI_SIGNATURE, - ) - expect(builder).toHaveProperty('data.fee', 0) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty('data.recipientId', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.asset') - expect(builder).toHaveProperty('data.asset.multisignature', {}) - }) - - describe('multiSignatureAsset', () => { - const multiSignatureFee = feeManager.get(TRANSACTION_TYPES.MULTI_SIGNATURE) - const multisignature = { - keysgroup: ['key a', 'key b', 'key c'], - lifetime: 1, - min: 1, - } - - it('establishes the multi-signature on the asset', () => { - builder.multiSignatureAsset(multisignature) - expect(builder.data.asset.multisignature).toBe(multisignature) - }) - - it('calculates and establish the fee', () => { - builder.multiSignatureAsset(multisignature) - expect(builder.data.fee).toBe(4 * multiSignatureFee) - }) - }) - - describe('sign', () => { - it('establishes the recipient id', () => { - const pass = 'dummy pass' - - crypto.getKeys = jest.fn(() => ({ - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - })) - crypto.sign = jest.fn() - - builder.sign(pass) - expect(builder.data.recipientId).toBe( - 'D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F', - ) - }) - }) - - describe('multiSignatureSign', () => { - it('adds the signature to the transaction', () => { - const pass = 'dummy pass' - const signature = `${pass} signature` - - crypto.getKeys = jest.fn(value => ({ publicKey: `${value} public key` })) - crypto.sign = jest.fn(() => signature) - - builder.multiSignatureSign(pass) - expect(builder.data.signatures).toIncludeAllMembers([signature]) - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/multi-signature.test.ts b/packages/crypto/__tests__/builder/transactions/multi-signature.test.ts new file mode 100644 index 0000000000..932871dd7a --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/multi-signature.test.ts @@ -0,0 +1,95 @@ +import "jest-extended"; +import { MultiSignatureBuilder } from "../../../src/builder/transactions/multi-signature"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { crypto } from "../../../src/crypto/crypto"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: MultiSignatureBuilder; + +beforeEach(() => { + builder = client.getBuilder().multiSignature(); +}); + +describe("Multi Signature Transaction", () => { + describe("verify", () => { + it.skip("should be valid with a signature", () => { + const actual = builder + .multiSignatureAsset({ + keysgroup: [ + "+0376982a97dadbc65e694743d386084548a65431a82ce935ac9d957b1cffab2784", + "+03793904e0df839809bc89f2839e1ae4f8b1ea97ede6592b7d1e4d0ee194ca2998", + "+03e710267cdbc87cf8c2f32a6c3f22e1d1ce22ba30e1915360f511a2b16df8c5a5", + ], + lifetime: 72, + min: 2, + }) + .sign("dummy passphrase") + .multiSignatureSign("multi passphrase 1") + .multiSignatureSign("multi passphrase 2") + .multiSignatureSign("multi passphrase 3"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + }); + + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.MultiSignature); + expect(builder).toHaveProperty("data.fee", 0); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.recipientId", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.asset"); + expect(builder).toHaveProperty("data.asset.multisignature", {}); + }); + + describe("multiSignatureAsset", () => { + const multiSignatureFee = feeManager.get(TransactionTypes.MultiSignature); + const multisignature = { + keysgroup: ["key a", "key b", "key c"], + lifetime: 1, + min: 1, + }; + + it("establishes the multi-signature on the asset", () => { + builder.multiSignatureAsset(multisignature); + expect(builder.data.asset.multisignature).toBe(multisignature); + }); + + it("calculates and establish the fee", () => { + builder.multiSignatureAsset(multisignature); + expect(builder.data.fee).toBe(4 * multiSignatureFee); + }); + }); + + describe("sign", () => { + it("establishes the recipient id", () => { + const pass = "dummy pass"; + + crypto.getKeys = jest.fn(() => ({ + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + })); + crypto.sign = jest.fn(); + + builder.sign(pass); + expect(builder.data.recipientId).toBe("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F"); + }); + }); + + describe("multiSignatureSign", () => { + it("adds the signature to the transaction", () => { + const pass = "dummy pass"; + const signature = `${pass} signature`; + + crypto.getKeys = jest.fn(value => ({ publicKey: `${value} public key` })); + crypto.sign = jest.fn(() => signature); + + builder.multiSignatureSign(pass); + expect(builder.data.signatures).toIncludeAllMembers([signature]); + }); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/second-signature.test.js b/packages/crypto/__tests__/builder/transactions/second-signature.test.js deleted file mode 100644 index 321076b2fd..0000000000 --- a/packages/crypto/__tests__/builder/transactions/second-signature.test.js +++ /dev/null @@ -1,54 +0,0 @@ -const ark = require('../../../lib/client') -const crypto = require('../../../lib/crypto/crypto') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().secondSignature() - - global.builder = builder -}) - -describe('Second Signature Transaction', () => { - describe('verify', () => { - it('should be valid with a signature', () => { - const actual = builder - .signatureAsset('signature') - .sign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - }) - - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty( - 'data.type', - TRANSACTION_TYPES.SECOND_SIGNATURE, - ) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.SECOND_SIGNATURE), - ) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty('data.recipientId', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.asset') - expect(builder).toHaveProperty('data.asset.signature', {}) - }) - - describe('signatureAsset', () => { - it('establishes the signature on the asset', () => { - crypto.getKeys = jest.fn(pass => ({ publicKey: `${pass} public key` })) - crypto.sign = jest.fn() - - builder.signatureAsset('bad pass') - - expect(builder.data.asset.signature.publicKey).toBe('bad pass public key') - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/second-signature.test.ts b/packages/crypto/__tests__/builder/transactions/second-signature.test.ts new file mode 100644 index 0000000000..18f9faa3d8 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/second-signature.test.ts @@ -0,0 +1,47 @@ +import "jest-extended"; +import { SecondSignatureBuilder } from "../../../src/builder/transactions/second-signature"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { crypto } from "../../../src/crypto/crypto"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: SecondSignatureBuilder; + +beforeEach(() => { + builder = client.getBuilder().secondSignature(); +}); + +describe("Second Signature Transaction", () => { + describe("verify", () => { + it("should be valid with a signature", () => { + const actual = builder.signatureAsset("signature").sign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + }); + + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.SecondSignature); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.SecondSignature)); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.recipientId", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.asset"); + expect(builder).toHaveProperty("data.asset.signature", {}); + }); + + describe("signatureAsset", () => { + it("establishes the signature on the asset", () => { + crypto.getKeys = jest.fn(pass => ({ publicKey: `${pass} public key` })); + crypto.sign = jest.fn(); + + builder.signatureAsset("bad pass"); + + expect(builder.data.asset.signature.publicKey).toBe("bad pass public key"); + }); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/timelock-transfer.test.js b/packages/crypto/__tests__/builder/transactions/timelock-transfer.test.js deleted file mode 100644 index bfd26a3b6a..0000000000 --- a/packages/crypto/__tests__/builder/transactions/timelock-transfer.test.js +++ /dev/null @@ -1,52 +0,0 @@ -const ark = require('../../../lib/client') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().timelockTransfer() - - global.builder = builder -}) - -describe('Timelock Transfer Transaction', () => { - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty( - 'data.type', - TRANSACTION_TYPES.TIMELOCK_TRANSFER, - ) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.TIMELOCK_TRANSFER), - ) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty('data.recipientId', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.timelockType', 0x00) - expect(builder).toHaveProperty('data.timelock', null) - }) - - describe('timelock', () => { - it('establishes the time lock', () => { - builder.timelock('time lock') - expect(builder.data.timelock).toBe('time lock') - }) - - it('establishes the time lock type', () => { - builder.timelock(null, 'time lock type') - expect(builder.data.timelockType).toBe('time lock type') - }) - }) - - describe('vendorField', () => { - it('should set the vendorField', () => { - const data = 'dummy' - builder.vendorField(data) - expect(builder.data.vendorField).toBe(data) - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/timelock-transfer.test.ts b/packages/crypto/__tests__/builder/transactions/timelock-transfer.test.ts new file mode 100644 index 0000000000..1fa5c3af69 --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/timelock-transfer.test.ts @@ -0,0 +1,42 @@ +import "jest-extended"; +import { TimelockTransferBuilder } from "../../../src/builder/transactions/timelock-transfer"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: TimelockTransferBuilder; + +beforeEach(() => { + builder = client.getBuilder().timelockTransfer(); +}); + +describe("Timelock Transfer Transaction", () => { + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.TimelockTransfer); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.TimelockTransfer)); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.recipientId", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.timelockType", 0x00); + expect(builder).toHaveProperty("data.timelock", null); + }); + + describe("timelock", () => { + it("establishes the time-lock & time-lock type", () => { + builder.timelock(2000, 0); + expect(builder.data.timelock).toBe(2000); + expect(builder.data.timelockType).toBe(0); + }); + }); + + describe("vendorField", () => { + it("should set the vendorField", () => { + const data = "dummy"; + builder.vendorField(data); + expect(builder.data.vendorField).toBe(data); + }); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/transfer.test.js b/packages/crypto/__tests__/builder/transactions/transfer.test.js deleted file mode 100644 index b24abcd006..0000000000 --- a/packages/crypto/__tests__/builder/transactions/transfer.test.js +++ /dev/null @@ -1,109 +0,0 @@ -const ark = require('../../../lib/client') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const { crypto } = require('../../../lib/crypto') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().transfer() - - global.builder = builder -}) - -describe('Transfer Transaction', () => { - describe('verify', () => { - it('should be valid with a signature', () => { - const actual = builder - .recipientId('D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F') - .amount(1) - .vendorField('dummy') - .sign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - - it('should be valid with a second signature', () => { - const actual = builder - .recipientId('D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F') - .amount(1) - .vendorField('dummy') - .sign('dummy passphrase') - .secondSign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - }) - - describe('signWithWif', () => { - it('should sign a transaction and match signed with a passphrase', () => { - const passphrase = 'sample passphrase' - const network = 23 - const keys = crypto.getKeys(passphrase) - const wif = crypto.keysToWIF(keys, { wif: 170 }) - - const wifTransaction = builder - .amount(10) - .fee(10) - .network(network) - - const passphraseTransaction = ark.getBuilder().transfer() - passphraseTransaction.data = { ...wifTransaction.data } - - wifTransaction.signWithWif(wif, 170) - passphraseTransaction.sign(passphrase) - - expect(wifTransaction.data.signature).toBe( - passphraseTransaction.data.signature, - ) - }) - }) - - describe('secondSignWithWif', () => { - it('should sign a transaction and match signed with a passphrase', () => { - const passphrase = 'first passphrase' - const secondPassphrase = 'second passphrase' - const network = 23 - const keys = crypto.getKeys(secondPassphrase) - const wif = crypto.keysToWIF(keys, { wif: 170 }) - - const wifTransaction = builder - .amount(10) - .fee(10) - .network(network) - .sign(passphrase) - - const passphraseTransaction = ark.getBuilder().transfer() - passphraseTransaction.data = { ...wifTransaction.data } - - wifTransaction.secondSignWithWif(wif, 170) - passphraseTransaction.secondSign(secondPassphrase) - - expect(wifTransaction.data.signSignature).toBe( - passphraseTransaction.data.signSignature, - ) - }) - }) - - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty('data.type', TRANSACTION_TYPES.TRANSFER) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.TRANSFER), - ) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty('data.recipientId', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.expiration', 0) - }) - - describe('vendorField', () => { - it('should set the vendorField', () => { - builder.vendorField('fake') - expect(builder.data.vendorField).toBe('fake') - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/transfer.test.ts b/packages/crypto/__tests__/builder/transactions/transfer.test.ts new file mode 100644 index 0000000000..e9cb10996e --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/transfer.test.ts @@ -0,0 +1,104 @@ +import "jest-extended"; +import { TransferBuilder } from "../../../src/builder/transactions/transfer"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { crypto } from "../../../src/crypto"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: TransferBuilder; + +beforeEach(() => { + builder = client.getBuilder().transfer(); +}); + +describe("Transfer Transaction", () => { + describe("verify", () => { + it("should be valid with a signature", () => { + const actual = builder + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(1) + .vendorField("dummy") + .sign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + + it("should be valid with a second signature", () => { + const actual = builder + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(1) + .vendorField("dummy") + .sign("dummy passphrase") + .secondSign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + }); + + describe("signWithWif", () => { + it("should sign a transaction and match signed with a passphrase", () => { + const passphrase = "sample passphrase"; + const network = 23; + const keys = crypto.getKeys(passphrase); + const wif = crypto.keysToWIF(keys, { wif: 170 }); + + const wifTransaction = builder + .amount(10) + .fee(10) + .network(network); + + const passphraseTransaction = client.getBuilder().transfer(); + passphraseTransaction.data = { ...wifTransaction.data }; + + wifTransaction.signWithWif(wif, 170); + passphraseTransaction.sign(passphrase); + + expect(wifTransaction.data.signature).toBe(passphraseTransaction.data.signature); + }); + }); + + describe("secondSignWithWif", () => { + it("should sign a transaction and match signed with a passphrase", () => { + const passphrase = "first passphrase"; + const secondPassphrase = "second passphrase"; + const network = 23; + const keys = crypto.getKeys(secondPassphrase); + const wif = crypto.keysToWIF(keys, { wif: 170 }); + + const wifTransaction = builder + .amount(10) + .fee(10) + .network(network) + .sign(passphrase); + + const passphraseTransaction = client.getBuilder().transfer(); + passphraseTransaction.data = { ...wifTransaction.data }; + + wifTransaction.secondSignWithWif(wif, 170); + passphraseTransaction.secondSign(secondPassphrase); + + expect(wifTransaction.data.signSignature).toBe(passphraseTransaction.data.signSignature); + }); + }); + + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.Transfer); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.Transfer)); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.recipientId", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.expiration", 0); + }); + + describe("vendorField", () => { + it("should set the vendorField", () => { + builder.vendorField("fake"); + expect(builder.data.vendorField).toBe("fake"); + }); + }); +}); diff --git a/packages/crypto/__tests__/builder/transactions/vote.test.js b/packages/crypto/__tests__/builder/transactions/vote.test.js deleted file mode 100644 index 8697e48223..0000000000 --- a/packages/crypto/__tests__/builder/transactions/vote.test.js +++ /dev/null @@ -1,95 +0,0 @@ -const ark = require('../../../lib/client') -const crypto = require('../../../lib/crypto/crypto') -const { TRANSACTION_TYPES } = require('../../../lib/constants') -const feeManager = require('../../../lib/managers/fee') -const transactionBuilderTests = require('./__shared__/transaction') - -let builder - -beforeEach(() => { - builder = ark.getBuilder().vote() - - global.builder = builder -}) - -describe('Vote Transaction', () => { - describe('verify', () => { - it('should be valid with a signature', () => { - const actual = builder - .votesAsset([ - '+02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - ]) - .sign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - - it('should be valid with a second signature', () => { - const actual = builder - .votesAsset([ - '+02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - ]) - .sign('dummy passphrase') - .secondSign('dummy passphrase') - - expect(actual.build().verify()).toBeTrue() - }) - }) - - transactionBuilderTests() - - it('should have its specific properties', () => { - expect(builder).toHaveProperty('data.type', TRANSACTION_TYPES.VOTE) - expect(builder).toHaveProperty( - 'data.fee', - feeManager.get(TRANSACTION_TYPES.VOTE), - ) - expect(builder).toHaveProperty('data.amount', 0) - expect(builder).toHaveProperty('data.recipientId', null) - expect(builder).toHaveProperty('data.senderPublicKey', null) - expect(builder).toHaveProperty('data.asset') - expect(builder).toHaveProperty('data.asset.votes', []) - }) - - describe('votesAsset', () => { - it('establishes the votes asset', () => { - const votes = ['+dummy-1'] - builder.votesAsset(votes) - expect(builder.data.asset.votes).toBe(votes) - }) - }) - - describe('sign', () => { - it('establishes the recipient id', () => { - const pass = 'dummy pass' - - crypto.getKeys = jest.fn(() => ({ - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - })) - crypto.sign = jest.fn() - - builder.sign(pass) - expect(builder.data.recipientId).toBe( - 'D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F', - ) - }) - }) - - describe('signWithWif', () => { - it('establishes the recipient id', () => { - const pass = 'dummy pass' - - crypto.getKeysFromWIF = jest.fn(() => ({ - publicKey: - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - })) - crypto.signWithWif = jest.fn() - - builder.signWithWif(pass) - expect(builder.data.recipientId).toBe( - 'D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F', - ) - }) - }) -}) diff --git a/packages/crypto/__tests__/builder/transactions/vote.test.ts b/packages/crypto/__tests__/builder/transactions/vote.test.ts new file mode 100644 index 0000000000..3c81743d3b --- /dev/null +++ b/packages/crypto/__tests__/builder/transactions/vote.test.ts @@ -0,0 +1,84 @@ +import "jest-extended"; +import { VoteBuilder } from "../../../src/builder/transactions/vote"; +import { client } from "../../../src/client"; +import { TransactionTypes } from "../../../src/constants"; +import { crypto } from "../../../src/crypto"; +import { feeManager } from "../../../src/managers/fee"; +import { transactionBuilder } from "./__shared__/transaction-builder"; + +let builder: VoteBuilder; + +beforeEach(() => { + builder = client.getBuilder().vote(); +}); + +describe("Vote Transaction", () => { + describe("verify", () => { + it("should be valid with a signature", () => { + const actual = builder + .votesAsset(["+02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"]) + .sign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + + it("should be valid with a second signature", () => { + const actual = builder + .votesAsset(["+02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"]) + .sign("dummy passphrase") + .secondSign("dummy passphrase"); + + expect(actual.build().verify()).toBeTrue(); + expect(actual.verify()).toBeTrue(); + }); + }); + + transactionBuilder(() => builder); + + it("should have its specific properties", () => { + expect(builder).toHaveProperty("data.type", TransactionTypes.Vote); + expect(builder).toHaveProperty("data.fee", feeManager.get(TransactionTypes.Vote)); + expect(builder).toHaveProperty("data.amount", 0); + expect(builder).toHaveProperty("data.recipientId", null); + expect(builder).toHaveProperty("data.senderPublicKey", null); + expect(builder).toHaveProperty("data.asset"); + expect(builder).toHaveProperty("data.asset.votes", []); + }); + + describe("votesAsset", () => { + it("establishes the votes asset", () => { + const votes = ["+dummy-1"]; + builder.votesAsset(votes); + expect(builder.data.asset.votes).toBe(votes); + }); + }); + + describe("sign", () => { + it("establishes the recipient id", () => { + const pass = "dummy pass"; + + crypto.getKeys = jest.fn(() => ({ + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + })); + crypto.sign = jest.fn(); + + builder.sign(pass); + expect(builder.data.recipientId).toBe("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F"); + }); + }); + + describe("signWithWif", () => { + it("establishes the recipient id", () => { + const pass = "dummy pass"; + + crypto.getKeysFromWIF = jest.fn(() => ({ + publicKey: "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + })); + // builder.signWithWif = jest.fn(); + + builder.signWithWif(pass); + expect(builder.data.recipientId).toBe("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F"); + }); + }); +}); diff --git a/packages/crypto/__tests__/client.test.js b/packages/crypto/__tests__/client.test.js deleted file mode 100644 index 1e1faf701d..0000000000 --- a/packages/crypto/__tests__/client.test.js +++ /dev/null @@ -1,7 +0,0 @@ -const ark = require('../lib/client') - -describe('Client', () => { - it('should be instantiated', () => { - expect(ark).toBeObject() - }) -}) diff --git a/packages/crypto/__tests__/client.test.ts b/packages/crypto/__tests__/client.test.ts new file mode 100644 index 0000000000..b8ce67766e --- /dev/null +++ b/packages/crypto/__tests__/client.test.ts @@ -0,0 +1,22 @@ +import "jest-extended"; +import { transactionBuilder } from "../src/builder"; +import { client, Client } from "../src/client"; +import { configManager, feeManager } from "../src/managers"; + +describe("Client", () => { + it("should be instantiated", () => { + expect(client).toBeInstanceOf(Client); + }); + + it("should getFeeManager()", () => { + expect(client.getFeeManager()).toBe(feeManager); + }); + + it("should getConfigManager()", () => { + expect(client.getConfigManager()).toBe(configManager); + }); + + it("should getBuilder()", () => { + expect(client.getBuilder()).toBe(transactionBuilder); + }); +}); diff --git a/packages/crypto/__tests__/constants.test.js b/packages/crypto/__tests__/constants.test.js deleted file mode 100644 index d39662141d..0000000000 --- a/packages/crypto/__tests__/constants.test.js +++ /dev/null @@ -1,51 +0,0 @@ -const constants = require('../lib/constants') - -describe('Constants', () => { - it('arktoshi is valid', () => { - expect(constants.ARKTOSHI).toBeDefined() - expect(constants.ARKTOSHI).toBe(100000000) - }) - - it('transaction types are defined', () => { - expect(constants.TRANSACTION_TYPES).toBeDefined() - expect(constants.TRANSACTION_TYPES).toBeFrozen() - - expect(constants.TRANSACTION_TYPES.TRANSFER).toBeDefined() - expect(constants.TRANSACTION_TYPES.TRANSFER).toBe(0) - - expect(constants.TRANSACTION_TYPES.SECOND_SIGNATURE).toBeDefined() - expect(constants.TRANSACTION_TYPES.SECOND_SIGNATURE).toBe(1) - - expect(constants.TRANSACTION_TYPES.DELEGATE_REGISTRATION).toBeDefined() - expect(constants.TRANSACTION_TYPES.DELEGATE_REGISTRATION).toBe(2) - - expect(constants.TRANSACTION_TYPES.VOTE).toBeDefined() - expect(constants.TRANSACTION_TYPES.VOTE).toBe(3) - - expect(constants.TRANSACTION_TYPES.MULTI_SIGNATURE).toBeDefined() - expect(constants.TRANSACTION_TYPES.MULTI_SIGNATURE).toBe(4) - - expect(constants.TRANSACTION_TYPES.IPFS).toBeDefined() - expect(constants.TRANSACTION_TYPES.IPFS).toBe(5) - - expect(constants.TRANSACTION_TYPES.TIMELOCK_TRANSFER).toBeDefined() - expect(constants.TRANSACTION_TYPES.TIMELOCK_TRANSFER).toBe(6) - - expect(constants.TRANSACTION_TYPES.MULTI_PAYMENT).toBeDefined() - expect(constants.TRANSACTION_TYPES.MULTI_PAYMENT).toBe(7) - - expect(constants.TRANSACTION_TYPES.DELEGATE_RESIGNATION).toBeDefined() - expect(constants.TRANSACTION_TYPES.DELEGATE_RESIGNATION).toBe(8) - }) - - it('configurations are defined', () => { - expect(constants.CONFIGURATIONS).toBeDefined() - expect(constants.CONFIGURATIONS).toBeFrozen() - - expect(constants.CONFIGURATIONS.ARK.MAINNET).toBeDefined() - expect(constants.CONFIGURATIONS.ARK.MAINNET).toBeObject() - - expect(constants.CONFIGURATIONS.ARK.DEVNET).toBeDefined() - expect(constants.CONFIGURATIONS.ARK.DEVNET).toBeObject() - }) -}) diff --git a/packages/crypto/__tests__/constants.test.ts b/packages/crypto/__tests__/constants.test.ts new file mode 100644 index 0000000000..c8f2aa4b1c --- /dev/null +++ b/packages/crypto/__tests__/constants.test.ts @@ -0,0 +1,40 @@ +import "jest-extended"; +import * as constants from "../src/constants"; + +describe("Constants", () => { + it("arktoshi is valid", () => { + expect(constants.ARKTOSHI).toBeDefined(); + expect(constants.ARKTOSHI).toBe(100000000); + }); + + it("transaction types are defined", () => { + expect(constants.TransactionTypes).toBeDefined(); + + expect(constants.TransactionTypes.Transfer).toBeDefined(); + expect(constants.TransactionTypes.Transfer).toBe(0); + + expect(constants.TransactionTypes.SecondSignature).toBeDefined(); + expect(constants.TransactionTypes.SecondSignature).toBe(1); + + expect(constants.TransactionTypes.DelegateRegistration).toBeDefined(); + expect(constants.TransactionTypes.DelegateRegistration).toBe(2); + + expect(constants.TransactionTypes.Vote).toBeDefined(); + expect(constants.TransactionTypes.Vote).toBe(3); + + expect(constants.TransactionTypes.MultiSignature).toBeDefined(); + expect(constants.TransactionTypes.MultiSignature).toBe(4); + + expect(constants.TransactionTypes.Ipfs).toBeDefined(); + expect(constants.TransactionTypes.Ipfs).toBe(5); + + expect(constants.TransactionTypes.TimelockTransfer).toBeDefined(); + expect(constants.TransactionTypes.TimelockTransfer).toBe(6); + + expect(constants.TransactionTypes.MultiPayment).toBeDefined(); + expect(constants.TransactionTypes.MultiPayment).toBe(7); + + expect(constants.TransactionTypes.DelegateResignation).toBeDefined(); + expect(constants.TransactionTypes.DelegateResignation).toBe(8); + }); +}); diff --git a/packages/crypto/__tests__/crypto/bip38.test.ts b/packages/crypto/__tests__/crypto/bip38.test.ts new file mode 100644 index 0000000000..399f257789 --- /dev/null +++ b/packages/crypto/__tests__/crypto/bip38.test.ts @@ -0,0 +1,128 @@ +import "jest-extended"; + +import bs58check from "bs58check"; +import ByteBuffer from "bytebuffer"; +import wif from "wif"; +import { bip38 } from "../../src/crypto"; + +import * as errors from "../../src/errors"; +import fixtures from "./fixtures/bip38.json"; + +describe("BIP38", () => { + describe("decrypt", () => { + fixtures.valid.forEach(fixture => { + it(`should decrypt '${fixture.description}'`, () => { + const result = bip38.decrypt(fixture.bip38, fixture.passphrase); + expect(wif.encode(0x80, result.privateKey, result.compressed)).toEqual(fixture.wif); + }); + }); + + fixtures.invalid.verify.forEach(fixture => { + it(`should not decrypt '${fixture.description}'`, () => { + try { + bip38.decrypt(fixture.base58, "foobar"); + } catch (error) { + expect(error).toBeInstanceOf(errors[fixture.error.type] || Error); + expect(error.message).toEqual(fixture.error.message); + } + }); + }); + + it("should throw if compression flag is different than 0xe0 0xc0", () => { + jest.spyOn(bs58check, "decode").mockImplementation(() => { + const byteBuffer = new ByteBuffer(512, true); + byteBuffer.writeUint8(0x01); + byteBuffer.writeUint8(0x42); // type + byteBuffer.writeUint8(0x01); // flag + + const buffer: any = Buffer.from(byteBuffer.flip().toBuffer()); + // force length to be 39 + Object.defineProperty(buffer, "length", { + get: jest.fn(() => 39), + set: jest.fn(), + }); + return buffer; + }); + + expect(() => bip38.decrypt("", "")).toThrow(errors.Bip38CompressionError); + + jest.restoreAllMocks(); + }); + }); + + describe("encrypt", () => { + fixtures.valid.forEach(fixture => { + if (fixture.decryptOnly) { + return; + } + + it(`should encrypt '${fixture.description}'`, () => { + const buffer = bs58check.decode(fixture.wif); + const actual = bip38.encrypt(buffer.slice(1, 33), !!buffer[33], fixture.passphrase); + expect(actual).toEqual(fixture.bip38); + }); + }); + + it("should throw if private key buffer length is different than 32", () => { + const byteBuffer = new ByteBuffer(512, true); + byteBuffer.writeUint8(0x01); + const buffer = Buffer.from(byteBuffer.toBuffer()); + + expect(() => bip38.encrypt(buffer, true, "")).toThrow(errors.PrivateKeyLengthError); + }); + }); + + describe("verify", () => { + fixtures.valid.forEach(fixture => { + it(`should verify '${fixture.bip38}'`, () => { + expect(bip38.verify(fixture.bip38)).toBeTrue(); + }); + }); + + fixtures.invalid.verify.forEach(fixture => { + it(`should not verify '${fixture.description}'`, () => { + expect(bip38.verify(fixture.base58)).toBeFalse(); + }); + }); + + it("should return false if encrypted WIF flag is different than 0xc0 0xe0", () => { + jest.spyOn(bs58check, "decodeUnsafe").mockImplementation(() => { + const byteBuffer = new ByteBuffer(512, true); + byteBuffer.writeUint8(0x01); + byteBuffer.writeUint8(0x42); // type + byteBuffer.writeUint8(0x01); // flag + + const buffer: any = Buffer.from(byteBuffer.flip().toBuffer()); + Object.defineProperty(buffer, "length", { + get: jest.fn(() => 39), + set: jest.fn(), + }); + return buffer; + }); + + expect(bip38.verify("yo")).toBeFalse(); + + jest.restoreAllMocks(); + }); + + it("should return false if encrypted EC mult flag is different than 0x24", () => { + jest.spyOn(bs58check, "decodeUnsafe").mockImplementation(() => { + const byteBuffer = new ByteBuffer(512, true); + byteBuffer.writeUint8(0x01); + byteBuffer.writeUint8(0x43); // type + byteBuffer.writeUint8(0x01); // flag + + const buffer: any = Buffer.from(byteBuffer.flip().toBuffer()); + Object.defineProperty(buffer, "length", { + get: jest.fn(() => 39), + set: jest.fn(), + }); + return buffer; + }); + + expect(bip38.verify("yo")).toBeFalse(); + + jest.restoreAllMocks(); + }); + }); +}); diff --git a/packages/crypto/__tests__/crypto/crypto.test.js b/packages/crypto/__tests__/crypto/crypto.test.js deleted file mode 100644 index 899a7ff45e..0000000000 --- a/packages/crypto/__tests__/crypto/crypto.test.js +++ /dev/null @@ -1,411 +0,0 @@ -/* eslint max-len: "off" */ - -const crypto = require('../../lib/crypto/crypto') -const configManager = require('../../lib/managers/config') -const { TRANSACTION_TYPES, CONFIGURATIONS } = require('../../lib/constants') - -beforeEach(() => configManager.setConfig(CONFIGURATIONS.ARK.DEVNET)) - -describe('crypto.js', () => { - describe('getBytes', () => { - let bytes = null - - it('should be a function', () => { - expect(crypto.getBytes).toBeFunction() - }) - - // it('should return Buffer of simply transaction and buffer must be 292 length', () => { - // const transaction = { - // type: 0, - // amount: 1000, - // fee: 2000, - // recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - // timestamp: 141738, - // asset: {}, - // senderPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - // signature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a' // eslint-disable-line max-len - // } - - // bytes = crypto.getBytes(transaction) - // expect(bytes).toBeObject() - // expect(bytes.toString('hex') + transaction.signature).toHaveLength(292) - // }) - - it('should return Buffer of simply transaction and buffer must be 202 length', () => { - const transaction = { - type: 0, - amount: 1000, - fee: 2000, - recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - timestamp: 141738, - asset: {}, - senderPublicKey: - '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - signature: - '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', // eslint-disable-line max-len - id: '13987348420913138422', - } - - bytes = crypto.getBytes(transaction) - expect(bytes).toBeObject() - expect(bytes.length).toBe(202) - expect(bytes.toString('hex')).toBe( - '00aa2902005d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09171dfc69b54c7fe901e91d5a9ab78388645e2427ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e803000000000000d007000000000000618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', - ) - }) - - // it('should return Buffer of transaction with second signature and buffer must be 420 length', () => { - // const transaction = { - // type: 0, - // amount: 1000, - // fee: 2000, - // recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - // timestamp: 141738, - // asset: {}, - // senderPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - // signature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', // eslint-disable-line max-len - // signSignature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a' // eslint-disable-line max-len - // } - - // bytes = crypto.getBytes(transaction) - // expect(bytes).toBeObject() - // expect(bytes.toString('hex') + transaction.signature + transaction.signSignature).toHaveLength(420) - // }) - - it('should return Buffer of transaction with second signature and buffer must be 266 length', () => { - const transaction = { - version: 1, - type: 0, - amount: 1000, - fee: 2000, - recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - timestamp: 141738, - asset: {}, - senderPublicKey: - '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - signature: - '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', // eslint-disable-line max-len - signSignature: - '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', // eslint-disable-line max-len - id: '13987348420913138422', - } - - bytes = crypto.getBytes(transaction) - expect(bytes).toBeObject() - expect(bytes.length).toBe(266) - expect(bytes.toString('hex')).toBe( - '00aa2902005d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09171dfc69b54c7fe901e91d5a9ab78388645e2427ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e803000000000000d007000000000000618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', - ) - }) - }) - - describe('getHash', () => { - it('should be a function', () => { - expect(crypto.getHash).toBeFunction() - }) - - it('should return Buffer and Buffer most be 32 bytes length', () => { - const transaction = { - version: 1, - type: 0, - amount: 1000, - fee: 2000, - recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - timestamp: 141738, - asset: {}, - senderPublicKey: - '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - signature: - '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', // eslint-disable-line max-len - } - - const result = crypto.getHash(transaction) - expect(result).toBeObject() - expect(result).toHaveLength(32) - expect(result.toString('hex')).toBe( - '952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea', - ) - }) - }) - - describe('getId', () => { - it('should be a function', () => { - expect(crypto.getId).toBeFunction() - }) - - it('should return string id and be equal to 952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea', () => { - const transaction = { - type: 0, - amount: 1000, - fee: 2000, - recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - timestamp: 141738, - asset: {}, - senderPublicKey: - '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', - signature: - '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', // eslint-disable-line max-len - } - - const id = crypto.getId(transaction) // old id - expect(id).toBeString() - expect(id).toBe( - '952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea', - ) - }) - }) - - describe('getFee', () => { - it('should be a function', () => { - expect(crypto.getFee).toBeFunction() - }) - - it('should return 10000000', () => { - const fee = crypto.getFee({ type: TRANSACTION_TYPES.TRANSFER }) - expect(fee).toBeNumber() - expect(fee).toBe(10000000) - }) - }) - - describe('sign', () => { - it('should be a function', () => { - expect(crypto.sign).toBeFunction() - }) - - it('should return a valid signature', () => { - const keys = crypto.getKeys('secret') - const transaction = { - type: 0, - amount: 1000, - fee: 2000, - recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', - timestamp: 141738, - asset: {}, - senderPublicKey: keys.publicKey, - } - const signature = crypto.sign(transaction, keys) - expect(signature.toString('hex')).toBe( - '3045022100f5c4ec7b3f9a2cb2e785166c7ae185abbff0aa741cbdfe322cf03b914002efee02206261cd419ea9074b5d4a007f1e2fffe17a38338358f2ac5fcc65d810dbe773fe', - ) - }) - }) - - describe('secondSign', () => { - it('should be a function', () => { - expect(crypto.secondSign).toBeFunction() - }) - }) - - describe('getKeys', () => { - it('should be a function', () => { - expect(crypto.getKeys).toBeFunction() - }) - - it('should return two keys in hex', () => { - const keys = crypto.getKeys('secret') - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - - expect(keys.publicKey).toBeString() - expect(keys.publicKey).toMatch( - Buffer.from(keys.publicKey, 'hex').toString('hex'), - ) - - expect(keys.privateKey).toBeString() - expect(keys.privateKey).toMatch( - Buffer.from(keys.privateKey, 'hex').toString('hex'), - ) - }) - - it('should return address', () => { - const keys = crypto.getKeys( - 'SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov', - ) - const address = crypto.getAddress(keys.publicKey.toString('hex')) - expect(address).toBe('DUMjDrT8mgqGLWZtkCqzvy7yxWr55mBEub') - }) - }) - - describe('getKeysFromWIF', () => { - it('should be a function', () => { - expect(crypto.getKeysFromWIF).toBeFunction() - }) - - it('should return two keys in hex', () => { - const keys = crypto.getKeysFromWIF( - 'SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov', - ) - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - - expect(keys.publicKey).toBeString() - expect(keys.publicKey).toMatch( - Buffer.from(keys.publicKey, 'hex').toString('hex'), - ) - - expect(keys.privateKey).toBeString() - expect(keys.privateKey).toMatch( - Buffer.from(keys.privateKey, 'hex').toString('hex'), - ) - }) - - it('should return address', () => { - const keys = crypto.getKeysFromWIF( - 'SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov', - ) - const address = crypto.getAddress(keys.publicKey.toString('hex')) - expect(address).toBe('DCAaPzPAhhsMkHfQs7fZvXFW2EskDi92m8') - }) - - it('should get keys from compressed WIF', () => { - const keys = crypto.getKeysFromWIF( - 'SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4', - ) - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - expect(keys).toHaveProperty('compressed', true) - }) - - it('should get keys from uncompressed WIF', () => { - const keys = crypto.getKeysFromWIF( - '6hgnAG19GiMUf75C43XteG2mC8esKTiX9PYbKTh4Gca9MELRWmg', - ) - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - expect(keys).toHaveProperty('compressed', false) - }) - }) - - describe('keysToWIF', () => { - it('should be a function', () => { - expect(crypto.keysToWIF).toBeFunction() - }) - - it('should get keys from WIF', () => { - const wifKey = 'SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4' - const keys = crypto.getKeysFromWIF(wifKey) - const actual = crypto.keysToWIF(keys) - - expect(keys.compressed).toBeTruthy() - expect(actual).toBe(wifKey) - }) - - it('should get address from compressed WIF (mainnet)', () => { - const keys = crypto.getKeysFromWIF( - 'SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4', - CONFIGURATIONS.ARK.MAINNET, - ) - const address = crypto.getAddress( - keys.publicKey, - CONFIGURATIONS.ARK.MAINNET.pubKeyHash, - ) - expect(keys.compressed).toBeTruthy() - expect(address).toBe('APnrtb2JGa6WjrRik9W3Hjt6h71mD6Zgez') - }) - - it('should get address from compressed WIF (devnet)', () => { - const keys = crypto.getKeysFromWIF( - 'SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4', - CONFIGURATIONS.ARK.DEVNET, - ) - const address = crypto.getAddress( - keys.publicKey, - CONFIGURATIONS.ARK.DEVNET.pubKeyHash, - ) - expect(keys.compressed).toBeTruthy() - expect(address).toBe('DDA5nM7KEqLeTtQKv5qGgcnc6dpNBKJNTS') - }) - }) - - describe('getAddress', () => { - it('should be a function', () => { - expect(crypto.getAddress).toBeFunction() - }) - - it('should generate address by publicKey', () => { - const keys = crypto.getKeys('secret') - const address = crypto.getAddress(keys.publicKey) - - expect(address).toBeString() - expect(address).toBe('D7seWn8JLVwX4nHd9hh2Lf7gvZNiRJ7qLk') - }) - - it('should generate address by publicKey - second test', () => { - const keys = crypto.getKeys( - 'secret second test to be sure it works correctly', - ) - const address = crypto.getAddress(keys.publicKey) - - expect(address).toBeString() - expect(address).toBe('DDp4SYpnuzFPuN4W79PYY762d7FtW3DFFN') - }) - - it('should not throw an error if the publicKey is valid', () => { - try { - const validKeys = [ - '02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af', - 'a'.repeat(66), - ] - for (const validKey of validKeys) { - crypto.getAddress(validKey) - } - } catch (error) { - throw new Error( - 'Should not have failed to call getAddress with a valid publicKey', - ) - } - }) - - it('should throw an error if the publicKey is invalid', () => { - const invalidKeys = [ - 'invalid', - 'a'.repeat(65), - 'a'.repeat(67), - 'z'.repeat(66), - ] - for (const invalidKey of invalidKeys) { - expect(() => crypto.getAddress(invalidKey)).toThrow( - new Error(`publicKey '${invalidKey}' is invalid`), - ) - } - }) - }) - - describe('verify', () => { - it('should be a function', () => { - expect(crypto.verify).toBeFunction() - }) - }) - - describe('verifySecondSignature', () => { - it('should be a function', () => { - expect(crypto.verifySecondSignature).toBeFunction() - }) - }) - - describe('validate address on different networks', () => { - it('should validate MAINNET addresses', () => { - configManager.setConfig(CONFIGURATIONS.ARK.MAINNET) - - expect( - crypto.validateAddress('AdVSe37niA3uFUPgCgMUH2tMsHF4LpLoiX'), - ).toBeTrue() - }) - - it('should validate DEVNET addresses', () => { - configManager.setConfig(CONFIGURATIONS.ARK.DEVNET) - - expect( - crypto.validateAddress('DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN'), - ).toBeTrue() - }) - }) -}) diff --git a/packages/crypto/__tests__/crypto/crypto.test.ts b/packages/crypto/__tests__/crypto/crypto.test.ts new file mode 100644 index 0000000000..b95e7a7731 --- /dev/null +++ b/packages/crypto/__tests__/crypto/crypto.test.ts @@ -0,0 +1,461 @@ +import "jest-extended"; +import { TransactionTypes } from "../../src/constants"; +import { crypto } from "../../src/crypto/crypto"; +import { PublicKeyError, TransactionVersionError } from "../../src/errors"; +import { configManager } from "../../src/managers/config"; +import { ITransactionData } from "../../src/models"; + +const networkMainnet = configManager.getPreset("mainnet"); +const networkDevnet = configManager.getPreset("devnet"); + +beforeEach(() => configManager.setFromPreset("devnet")); + +describe("crypto.js", () => { + describe("getBytes", () => { + let bytes = null; + + // it('should return Buffer of simply transaction and buffer must be 292 length', () => { + // const transaction = { + // type: 0, + // amount: 1000, + // fee: 2000, + // recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', + // timestamp: 141738, + // asset: {}, + // senderPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', + // signature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a' + // } + + // bytes = crypto.getBytes(transaction) + // expect(bytes).toBeObject() + // expect(bytes.toString('hex') + transaction.signature).toHaveLength(292) + // }) + + it("should return Buffer of simply transaction and buffer must be 202 length", () => { + const transaction = { + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", + signature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + id: "13987348420913138422", + }; + + bytes = crypto.getBytes(transaction); + expect(bytes).toBeObject(); + expect(bytes.length).toBe(202); + expect(bytes.toString("hex")).toBe( + "00aa2902005d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09171dfc69b54c7fe901e91d5a9ab78388645e2427ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e803000000000000d007000000000000618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + ); + }); + + // it('should return Buffer of transaction with second signature and buffer must be 420 length', () => { + // const transaction = { + // type: 0, + // amount: 1000, + // fee: 2000, + // recipientId: 'AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff', + // timestamp: 141738, + // asset: {}, + // senderPublicKey: '5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09', + // signature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a', + // signSignature: '618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a' + // } + + // bytes = crypto.getBytes(transaction) + // expect(bytes).toBeObject() + // expect(bytes.toString('hex') + transaction.signature + transaction.signSignature).toHaveLength(420) + // }) + + it("should return Buffer of transaction with second signature and buffer must be 266 length", () => { + const transaction = { + version: 1, + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", + signature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + signSignature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + id: "13987348420913138422", + }; + + bytes = crypto.getBytes(transaction); + expect(bytes).toBeObject(); + expect(bytes.length).toBe(266); + expect(bytes.toString("hex")).toBe( + "00aa2902005d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09171dfc69b54c7fe901e91d5a9ab78388645e2427ea00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e803000000000000d007000000000000618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + ); + }); + + it("should throw for unsupported version", () => { + const transaction = { + version: 110, + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", + signature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + signSignature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + id: "13987348420913138422", + }; + + expect(() => crypto.getBytes(transaction)).toThrow(TransactionVersionError); + }); + }); + + describe("getHash", () => { + const transaction = { + version: 1, + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", + signature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + }; + + it("should return Buffer and Buffer most be 32 bytes length", () => { + const result = crypto.getHash(transaction); + expect(result).toBeObject(); + expect(result).toHaveLength(32); + expect(result.toString("hex")).toBe("952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea"); + }); + + it("should throw for unsupported versions", () => { + expect(() => crypto.getHash(Object.assign({}, transaction, { version: 110 }))).toThrow( + TransactionVersionError, + ); + }); + }); + + describe("getId", () => { + const transaction = { + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: "5d036a858ce89f844491762eb89e2bfbd50a4a0a0da658e4b2628b25b117ae09", + signature: + "618a54975212ead93df8c881655c625544bce8ed7ccdfe6f08a42eecfb1adebd051307be5014bb051617baf7815d50f62129e70918190361e5d4dd4796541b0a", + }; + + it("should return string id and be equal to 952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea", () => { + const id = crypto.getId(transaction); // old id + expect(id).toBeString(); + expect(id).toBe("952e33b66c35a3805015657c008e73a0dee1efefd9af8c41adb59fe79745ccea"); + }); + + it("should throw for unsupported version", () => { + expect(() => crypto.getId(Object.assign({}, transaction, { version: 110 }))).toThrow( + TransactionVersionError, + ); + }); + }); + + describe("getFee", () => { + it("should return 10000000", () => { + const fee = crypto.getFee({ type: TransactionTypes.Transfer } as ITransactionData); + expect(fee).toBeNumber(); + expect(fee).toBe(10000000); + }); + }); + + describe("sign", () => { + const keys = crypto.getKeys("secret"); + const transaction = { + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: keys.publicKey, + }; + + it("should return a valid signature", () => { + const signature = crypto.sign(transaction, keys); + // @ts-ignore + expect(signature.toString("hex")).toBe( + "3045022100f5c4ec7b3f9a2cb2e785166c7ae185abbff0aa741cbdfe322cf03b914002efee02206261cd419ea9074b5d4a007f1e2fffe17a38338358f2ac5fcc65d810dbe773fe", + ); + }); + + it("should throw for unsupported versions", () => { + expect(() => { + crypto.sign(Object.assign({}, transaction, { version: 110 }), keys); + }).toThrow(TransactionVersionError); + }); + }); + + describe("verify", () => { + const keys = crypto.getKeys("secret"); + const transaction: any = { + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: keys.publicKey, + }; + const signature = crypto.sign(transaction, keys); + + const otherPublicKey = "0203bc6522161803a4cd9d8c7b7e3eb5b29f92106263a3979e3e02d27a70e830b4"; + + it("should return true on a valid signature", () => { + expect(crypto.verify(transaction)).toBeTrue(); + }); + + it("should return false on an invalid signature", () => { + expect(crypto.verify(Object.assign({}, transaction, { senderPublicKey: otherPublicKey }))).toBeFalse(); + }); + + it("should return false on a missing signature", () => { + const transactionWithoutSignature = Object.assign({}, transaction); + delete transactionWithoutSignature.signature; + + expect(crypto.verify(transactionWithoutSignature)).toBeFalse(); + }); + }); + + describe("verifySecondSignature", () => { + const keys1 = crypto.getKeys("secret"); + const keys2 = crypto.getKeys("secret too"); + const transaction: any = { + type: 0, + amount: 1000, + fee: 2000, + recipientId: "AJWRd23HNEhPLkK1ymMnwnDBX2a7QBZqff", + timestamp: 141738, + asset: {}, + senderPublicKey: keys1.publicKey, + }; + const secondSignature = crypto.secondSign(transaction, keys2); + transaction.signSignature = secondSignature; + const otherPublicKey = "0203bc6522161803a4cd9d8c7b7e3eb5b29f92106263a3979e3e02d27a70e830b4"; + + it("should return true on a valid signature", () => { + expect(crypto.verifySecondSignature(transaction, keys2.publicKey)).toBeTrue(); + }); + + it("should return false on an invalid second signature", () => { + expect(crypto.verifySecondSignature(transaction, otherPublicKey)).toBeFalse(); + }); + + it("should return false on a missing second signature", () => { + const transactionWithoutSignature = Object.assign({}, transaction); + delete transactionWithoutSignature.secondSignature; + delete transactionWithoutSignature.signSignature; + + expect(crypto.verifySecondSignature(transactionWithoutSignature, keys2.publicKey)).toBeFalse(); + }); + + it("should call this.getHash without parameters skipSignature and skipSecondSignature if transaction.version != 1", () => { + const transactionV2 = Object.assign({}, transaction, { version: 2 }); + delete transactionV2.secondSignature; + delete transactionV2.signSignature; + const getHashMock = jest.spyOn(crypto, "getHash").mockImplementation(() => ""); + + crypto.verifySecondSignature(transactionV2, keys2.publicKey); + expect(getHashMock).toHaveBeenLastCalledWith(transactionV2); + }); + }); + + describe("getKeys", () => { + it("should return two keys in hex", () => { + const keys = crypto.getKeys("secret"); + + expect(keys).toBeObject(); + expect(keys).toHaveProperty("publicKey"); + expect(keys).toHaveProperty("privateKey"); + + expect(keys.publicKey).toBeString(); + expect(keys.publicKey).toMatch(Buffer.from(keys.publicKey, "hex").toString("hex")); + + expect(keys.privateKey).toBeString(); + expect(keys.privateKey).toMatch(Buffer.from(keys.privateKey, "hex").toString("hex")); + }); + + it("should return address", () => { + const keys = crypto.getKeys("SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov"); + // @ts-ignore + const address = crypto.getAddress(keys.publicKey.toString("hex")); + expect(address).toBe("DUMjDrT8mgqGLWZtkCqzvy7yxWr55mBEub"); + }); + }); + + describe("getKeysByPrivateKey", () => { + it("should get keys from private key", () => { + const expectedKeys = { + publicKey: "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + privateKey: "c13bcd9a3dd64cabb27fcf2f4a471d35ffc3c114bb1278de745e6ff82a72eda8", + compressed: true, + }; + const keys = crypto.getKeysByPrivateKey(expectedKeys.privateKey); + + expect(keys).toEqual(expectedKeys); + }); + }); + + describe("getKeysFromWIF", () => { + it("should return two keys in hex", () => { + const keys = crypto.getKeysFromWIF("SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov"); + + expect(keys).toBeObject(); + expect(keys).toHaveProperty("publicKey"); + expect(keys).toHaveProperty("privateKey"); + + expect(keys.publicKey).toBeString(); + expect(keys.publicKey).toMatch(Buffer.from(keys.publicKey, "hex").toString("hex")); + + expect(keys.privateKey).toBeString(); + expect(keys.privateKey).toMatch(Buffer.from(keys.privateKey, "hex").toString("hex")); + }); + + it("should return address", () => { + const keys = crypto.getKeysFromWIF("SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov"); + // @ts-ignore + const address = crypto.getAddress(keys.publicKey.toString("hex")); + expect(address).toBe("DCAaPzPAhhsMkHfQs7fZvXFW2EskDi92m8"); + }); + + it("should get keys from compressed WIF", () => { + const keys = crypto.getKeysFromWIF("SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4"); + + expect(keys).toBeObject(); + expect(keys).toHaveProperty("publicKey"); + expect(keys).toHaveProperty("privateKey"); + expect(keys).toHaveProperty("compressed", true); + }); + + it("should get keys from uncompressed WIF", () => { + const keys = crypto.getKeysFromWIF("6hgnAG19GiMUf75C43XteG2mC8esKTiX9PYbKTh4Gca9MELRWmg"); + + expect(keys).toBeObject(); + expect(keys).toHaveProperty("publicKey"); + expect(keys).toHaveProperty("privateKey"); + expect(keys).toHaveProperty("compressed", false); + }); + }); + + describe("keysToWIF", () => { + it("should get keys from WIF", () => { + const wifKey = "SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4"; + const keys = crypto.getKeysFromWIF(wifKey); + const actual = crypto.keysToWIF(keys); + + expect(keys.compressed).toBeTruthy(); + expect(actual).toBe(wifKey); + }); + + it("should get address from compressed WIF (mainnet)", () => { + const keys = crypto.getKeysFromWIF( + "SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4", + networkMainnet.network, + ); + const address = crypto.getAddress(keys.publicKey, networkMainnet.network.pubKeyHash); + expect(keys.compressed).toBeTruthy(); + expect(address).toBe("APnrtb2JGa6WjrRik9W3Hjt6h71mD6Zgez"); + }); + + it("should get address from compressed WIF (devnet)", () => { + const keys = crypto.getKeysFromWIF( + "SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4", + networkDevnet.network, + ); + const address = crypto.getAddress(keys.publicKey, networkDevnet.network.pubKeyHash); + expect(keys.compressed).toBeTruthy(); + expect(address).toBe("DDA5nM7KEqLeTtQKv5qGgcnc6dpNBKJNTS"); + }); + }); + + describe("getAddress", () => { + it("should generate address by publicKey", () => { + const keys = crypto.getKeys("secret"); + const address = crypto.getAddress(keys.publicKey); + + expect(address).toBeString(); + expect(address).toBe("D7seWn8JLVwX4nHd9hh2Lf7gvZNiRJ7qLk"); + }); + + it("should generate address by publicKey - second test", () => { + const keys = crypto.getKeys("secret second test to be sure it works correctly"); + const address = crypto.getAddress(keys.publicKey); + + expect(address).toBeString(); + expect(address).toBe("DDp4SYpnuzFPuN4W79PYY762d7FtW3DFFN"); + }); + + it("should not throw an error if the publicKey is valid", () => { + try { + const validKeys = [ + "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af", + "a".repeat(66), + ]; + for (const validKey of validKeys) { + crypto.getAddress(validKey); + } + } catch (error) { + throw new Error("Should not have failed to call getAddress with a valid publicKey"); + } + }); + + it("should throw an error if the publicKey is invalid", () => { + const invalidKeys = ["invalid", "a".repeat(65), "a".repeat(67), "z".repeat(66)]; + for (const invalidKey of invalidKeys) { + expect(() => crypto.getAddress(invalidKey)).toThrow(PublicKeyError); + } + }); + }); + + describe("validate address on different networks", () => { + it("should validate MAINNET addresses", () => { + configManager.setConfig(networkMainnet); + + expect(crypto.validateAddress("AdVSe37niA3uFUPgCgMUH2tMsHF4LpLoiX")).toBeTrue(); + }); + + it("should validate DEVNET addresses", () => { + configManager.setConfig(networkDevnet); + + expect(crypto.validateAddress("DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN")).toBeTrue(); + }); + }); + + describe("validate public key on different networks", () => { + it("should validate MAINNET public keys", () => { + configManager.setConfig(networkMainnet); + + expect( + crypto.validatePublicKey("02b54f00d9de5a3ace28913fe78a15afcfe242926e94d9b517d06d2705b261f992"), + ).toBeTrue(); + }); + + it("should validate DEVNET public keys", () => { + configManager.setConfig(networkDevnet); + + expect( + crypto.validatePublicKey("03b906102928cf97c6ddeb59cefb0e1e02105a22ab1acc3b4906214a16d494db0a"), + ).toBeTrue(); + }); + }); +}); diff --git a/packages/crypto/__tests__/crypto/fixtures/bip38.json b/packages/crypto/__tests__/crypto/fixtures/bip38.json new file mode 100644 index 0000000000..532c8d684c --- /dev/null +++ b/packages/crypto/__tests__/crypto/fixtures/bip38.json @@ -0,0 +1,142 @@ +{ + "valid": [ + { + "passphrase": "TestingOneTwoThree", + "bip38": "6PRVWUbkzzsbcVac2qwfssoUJAN1Xhrg6bNk8J7Nzm5H7kxEbn2Nh2ZoGg", + "wif": "5KN7MzqK5wt2TP1fQCYyHBtDrXdJuXbUzm4A9rKAteGu3Qi5CVR", + "address": "1Jq6MksXQVWzrznvZzxkV6oY57oWXD9TXB", + "description": "no EC multiply / no compression #1" + }, + { + "passphrase": "Satoshi", + "bip38": "6PRNFFkZc2NZ6dJqFfhRoFNMR9Lnyj7dYGrzdgXXVMXcxoKTePPX1dWByq", + "wif": "5HtasZ6ofTHP6HCwTqTkLDuLQisYPah7aUnSKfC7h4hMUVw2gi5", + "address": "1AvKt49sui9zfzGeo8EyL8ypvAhtR2KwbL", + "description": "no EC multiply / no compression #2" + }, + { + "passphrase": "TestingOneTwoThree", + "bip38": "6PYNKZ1EAgYgmQfmNVamxyXVWHzK5s6DGhwP4J5o44cvXdoY7sRzhtpUeo", + "wif": "L44B5gGEpqEDRS9vVPz7QT35jcBG2r3CZwSwQ4fCewXAhAhqGVpP", + "address": "164MQi977u9GUteHr4EPH27VkkdxmfCvGW", + "description": "no EC multiply / compression #1" + }, + { + "passphrase": "Satoshi", + "bip38": "6PYLtMnXvfG3oJde97zRyLYFZCYizPU5T3LwgdYJz1fRhh16bU7u6PPmY7", + "wif": "KwYgW8gcxj1JWJXhPSu4Fqwzfhp5Yfi42mdYmMa4XqK7NJxXUSK7", + "address": "1HmPbwsvG5qJ3KJfxzsZRZWhbm1xBMuS8B", + "description": "no EC multiply / compression #2" + }, + { + "passphrase": "TestingOneTwoThree", + "bip38": "6PfQu77ygVyJLZjfvMLyhLMQbYnu5uguoJJ4kMCLqWwPEdfpwANVS76gTX", + "wif": "5K4caxezwjGCGfnoPTZ8tMcJBLB7Jvyjv4xxeacadhq8nLisLR2", + "address": "1PE6TQi6HTVNz5DLwB1LcpMBALubfuN2z2", + "description": "EC multiply / no compression, no lot sequence #1", + "decryptOnly": true, + "code": "passphrasepxFy57B9v8HtUsszJYKReoNDV6VHjUSGt8EVJmux9n1J3Ltf1gRxyDGXqnf9qm" + }, + { + "passphrase": "Satoshi", + "bip38": "6PfLGnQs6VZnrNpmVKfjotbnQuaJK4KZoPFrAjx1JMJUa1Ft8gnf5WxfKd", + "wif": "5KJ51SgxWaAYR13zd9ReMhJpwrcX47xTJh2D3fGPG9CM8vkv5sH", + "address": "1CqzrtZC6mXSAhoxtFwVjz8LtwLJjDYU3V", + "description": "EC multiply / no compression, no lot sequence #2", + "decryptOnly": true, + "code": "passphraseoRDGAXTWzbp72eVbtUDdn1rwpgPUGjNZEc6CGBo8i5EC1FPW8wcnLdq4ThKzAS" + }, + { + "passphrase": "MOLON LABE", + "bip38": "6PgNBNNzDkKdhkT6uJntUXwwzQV8Rr2tZcbkDcuC9DZRsS6AtHts4Ypo1j", + "wif": "5JLdxTtcTHcfYcmJsNVy1v2PMDx432JPoYcBTVVRHpPaxUrdtf8", + "address": "1Jscj8ALrYu2y9TD8NrpvDBugPedmbj4Yh", + "description": "EC multiply / no compression, lot sequence #1", + "decryptOnly": true, + "confirm": "cfrm38V8aXBn7JWA1ESmFMUn6erxeBGZGAxJPY4e36S9QWkzZKtaVqLNMgnifETYw7BPwWC9aPD", + "code": "passphraseaB8feaLQDENqCgr4gKZpmf4VoaT6qdjJNJiv7fsKvjqavcJxvuR1hy25aTu5sX", + "lot": 263183, + "seq": 1 + }, + { + "passphrase": "ΜΟΛΩΝ ΛΑΒΕ", + "bip38": "6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5mar2ngH", + "wif": "5KMKKuUmAkiNbA3DazMQiLfDq47qs8MAEThm4yL8R2PhV1ov33D", + "address": "1Lurmih3KruL4xDB5FmHof38yawNtP9oGf", + "description": "EC multiply / no compression, lot sequence #1", + "decryptOnly": true, + "confirm": "cfrm38V8G4qq2ywYEFfWLD5Cc6msj9UwsG2Mj4Z6QdGJAFQpdatZLavkgRd1i4iBMdRngDqDs51", + "code": "passphrased3z9rQJHSyBkNBwTRPkUGNVEVrUAcfAXDyRU1V28ie6hNFbqDwbFBvsTK7yWVK", + "lot": 806938, + "sequence": 1 + } + ], + "invalid": { + "decrypt": [], + "encrypt": [], + "verify": [ + { + "description": "Invalid base58", + "error": { + "type": "Error", + "message": "Invalid checksum" + }, + "base58": "6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5marXXXX" + }, + { + "description": "Length > 39", + "error": { + "type": "Bip38LengthError", + "message": "Expected length to be 39, but got 40." + }, + "hex": "0142c000000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "QmxDezFMDL7ExfYmsETsQXAtBbw5YE1CDyA8pm1AGpMpVVUpsVy1yXv4VTL" + }, + { + "description": "Length < 39", + "error": { + "type": "Bip38LengthError", + "message": "Expected length to be 39, but got 38." + }, + "hex": "0142c00000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "2DnNxWcx4Prn8wmjbkvtYGDALsq8BMWxQ33KnXkeH8vrxE41psDLXRmK3" + }, + { + "description": "prefix !== 0x01", + "error": { + "type": "Bip38PrefixError", + "message": "Expected prefix to be 1, but got 2." + }, + "hex": "0242c0000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "AfE1YY4Wr2FLAENaH9PVaLRdyk714V4rhwiJMSGyQCGFB3rhGDCs2R7c4s" + }, + { + "description": "flag !== 0xc0 && flag !== 0xe0", + "error": { + "type": "Bip38TypeError", + "message": "Expected type to be 66, but got 1." + }, + "hex": "0101ff000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "5JjnYkbFBmUnhGeDMVhR7aSitLToe1odEfXDBeg4RMK6JmAm9g7rkm7qY3" + }, + { + "description": "EC Mult: ~(flag & 0x24)", + "error": { + "type": "Bip38TypeError", + "message": "Expected type to be 66, but got 1." + }, + "hex": "0101db000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "5JbtdQFKSemRTqMuWrJgSfzE8AX2jdz1KiZuMmuUcv9iXha1s6UarQTciW" + }, + { + "description": "EC Mult: ~(flag & 0x24)", + "error": { + "type": "Bip38TypeError", + "message": "Expected type to be 66, but got 1." + }, + "hex": "010135000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "5HyV7HSYdHUgLf7w36mxMHDPH9muTgUYHEj6cEogKMuV7ae8VRM3VEg56w" + } + ] + } +} diff --git a/packages/crypto/__tests__/crypto/fixtures/crypto.json b/packages/crypto/__tests__/crypto/fixtures/crypto.json index 336ff8bc46..3b9a49a2db 100644 --- a/packages/crypto/__tests__/crypto/fixtures/crypto.json +++ b/packages/crypto/__tests__/crypto/fixtures/crypto.json @@ -1,7 +1,7 @@ { - "ripemd160": "a830d7beb04eb7549ce990fb7dc962e499a27230", - "sha1": "0a4d55a8d778e5022fab701977c5d840bbc486d0", - "sha256": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e", - "hash160": "bdfb69557966d026975bebe914692bf08490d8ca", - "hash256": "42a873ac3abd02122d27e80486c6fa1ef78694e8505fcec9cbcc8a7728ba8949" + "ripemd160": "a830d7beb04eb7549ce990fb7dc962e499a27230", + "sha1": "0a4d55a8d778e5022fab701977c5d840bbc486d0", + "sha256": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e", + "hash160": "bdfb69557966d026975bebe914692bf08490d8ca", + "hash256": "42a873ac3abd02122d27e80486c6fa1ef78694e8505fcec9cbcc8a7728ba8949" } diff --git a/packages/crypto/__tests__/crypto/hash-algorithms.test.ts b/packages/crypto/__tests__/crypto/hash-algorithms.test.ts new file mode 100644 index 0000000000..489ba4bea7 --- /dev/null +++ b/packages/crypto/__tests__/crypto/hash-algorithms.test.ts @@ -0,0 +1,28 @@ +import "jest-extended"; + +import { HashAlgorithms } from "../../src/crypto/hash-algorithms"; +import fixtures from "./fixtures/crypto.json"; + +const buffer = Buffer.from("Hello World"); + +describe("Crypto - Utils", () => { + it("should return valid ripemd160", () => { + expect(HashAlgorithms.ripemd160(buffer).toString("hex")).toEqual(fixtures.ripemd160); + }); + + it("should return valid sha1", () => { + expect(HashAlgorithms.sha1(buffer).toString("hex")).toEqual(fixtures.sha1); + }); + + it("should return valid sha256", () => { + expect(HashAlgorithms.sha256(buffer).toString("hex")).toEqual(fixtures.sha256); + }); + + it("should return valid hash160", () => { + expect(HashAlgorithms.hash160(buffer).toString("hex")).toEqual(fixtures.hash160); + }); + + it("should return valid hash256", () => { + expect(HashAlgorithms.hash256(buffer).toString("hex")).toEqual(fixtures.hash256); + }); +}); diff --git a/packages/crypto/__tests__/crypto/hdwallet.test.js b/packages/crypto/__tests__/crypto/hdwallet.test.js deleted file mode 100644 index 2792eb29cb..0000000000 --- a/packages/crypto/__tests__/crypto/hdwallet.test.js +++ /dev/null @@ -1,196 +0,0 @@ -const bip32 = require('bip32') -const { crypto, hdwallet } = require('../../lib/crypto') -const configManager = require('../../lib/managers/config') -const network = require('../../lib/networks/ark/mainnet.json') - -const mnemonic = 'sorry hawk one science reject employ museum ride into post machine attack bar seminar myself unhappy faculty differ grain fish chest bird muffin mesh' - -beforeEach(() => configManager.setConfig(network)) - -describe('HDWallet', () => { - describe('bip32', () => { - it('can create a BIP32 wallet external address', () => { - const path = "m/0'/0/0" - const root = bip32.fromSeed( - Buffer.from( - 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', - 'hex', - ), - ) - - const child1 = root.derivePath(path) - - // option 2, manually - const child2 = root - .deriveHardened(0) - .derive(0) - .derive(0) - - expect(crypto.getAddress(child1.publicKey.toString('hex'))).toBe( - 'AZXdSTRFGHPokX6yfXTfHcTzzHKncioj31', - ) - expect(crypto.getAddress(child2.publicKey.toString('hex'))).toBe( - 'AZXdSTRFGHPokX6yfXTfHcTzzHKncioj31', - ) - }) - }) - - describe('bip44', () => { - it('can create a BIP44, ark, account 0, external address', () => { - /* eslint quotes: ["error", "single", { avoidEscape: true }] */ - const path = "m/44'/111'/0'/0/0" - const root = bip32.fromSeed( - Buffer.from( - 'dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd', - 'hex', - ), - ) - - const child1 = root.derivePath(path) - - // option 2, manually - const child2 = root - .deriveHardened(44) - .deriveHardened(111) - .deriveHardened(0) - .derive(0) - .derive(0) - - expect(crypto.getAddress(child1.publicKey.toString('hex'))).toBe( - 'AKdstZSrxzeF54e1M41fQzqGqjod9ydG3E', - ) - expect(crypto.getAddress(child2.publicKey.toString('hex'))).toBe( - 'AKdstZSrxzeF54e1M41fQzqGqjod9ydG3E', - ) - }) - }) - - describe('fromMnemonic', () => { - it('should be a function', () => { - expect(hdwallet.fromMnemonic).toBeFunction() - }) - - it('should return the root node', () => { - const root = hdwallet.fromMnemonic(mnemonic) - expect(root.constructor.name).toBe('BIP32') - }) - - it('should derive path', () => { - const root = hdwallet.fromMnemonic(mnemonic) - const node = root.derivePath("44'/1'/0'/0/0") - expect(node.publicKey.toString('hex')).toBe( - '02126148679f22c162afa24a264a6b6722a61aab622248f2f536da289f48a9291f', - ) - expect(node.privateKey.toString('hex')).toBe( - 'b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5', - ) - }) - }) - - describe('getKeys', () => { - it('should be a function', () => { - expect(hdwallet.fromKeys).toBeFunction() - }) - - it('should return keys from a node', () => { - const root = hdwallet.fromMnemonic(mnemonic) - const node = root.derivePath("44'/1'/0'/0/0") - const keys = hdwallet.getKeys(node) - expect(keys.publicKey).toBe( - '02126148679f22c162afa24a264a6b6722a61aab622248f2f536da289f48a9291f', - ) - expect(keys.privateKey).toBe( - 'b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5', - ) - expect(keys.compressed).toBeTrue() - }) - }) - - describe('fromKeys', () => { - it('should be a function', () => { - expect(hdwallet.fromKeys).toBeFunction() - }) - - it('should return node from keys', () => { - const keys = { - publicKey: '', - privateKey: - 'b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5', - compressed: true, - } - - const chainCode = Buffer.from( - '2bbe729fab21bf8bca70763caf7fe85752726a363b494dea7a65e51e2d423d7b', - 'hex', - ) - const node = hdwallet.fromKeys(keys, chainCode) - expect(node.publicKey.toString('hex')).toBe( - '02126148679f22c162afa24a264a6b6722a61aab622248f2f536da289f48a9291f', - ) - expect(node.privateKey.toString('hex')).toBe( - 'b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5', - ) - }) - }) - - describe('deriveSlip44', () => { - it('should be a function', () => { - expect(hdwallet.deriveSlip44).toBeFunction() - }) - - it('should derive path', () => { - const root = hdwallet.fromMnemonic(mnemonic) - - const actual = hdwallet - .deriveSlip44(root) - .deriveHardened(0) - .derive(0) - .derive(0) - - const expected = root - .deriveHardened(44) - .deriveHardened(111) - .deriveHardened(0) - .derive(0) - .derive(0) - - expect(crypto.getAddress(actual.publicKey.toString('hex'))).toBe( - 'AHQhEsLWX5BbvvK836f1rUyZZZ77YikYq5', - ) - expect(actual.publicKey.toString('hex')).toBe( - '0330d7c2df15da16c72ac524f7548b2bca689beb0527ce54a50d3b79e4e91a8e9b', - ) - expect(actual.privateKey.toString('hex')).toBe( - '693bef1f16bad3c8096191af2362dae95873468fc5de30510b61d36fb3f1e33c', - ) - - expect(actual.publicKey).toEqual(expected.publicKey) - expect(actual.privateKey).toEqual(expected.privateKey) - }) - }) - - describe('deriveNetwork', () => { - it('should be a function', () => { - expect(hdwallet.deriveNetwork).toBeFunction() - }) - - it('should derive path', () => { - const root = hdwallet.fromMnemonic(mnemonic) - - const actual = hdwallet - .deriveNetwork(root) - .deriveHardened(0) - .derive(0) - - expect(crypto.getAddress(actual.publicKey.toString('hex'))).toBe( - 'AKjBp5V1xG9c5PQqUvtvtoGjvnyA3wLVpA', - ) - expect(actual.publicKey.toString('hex')).toBe( - '0281d69cadc9cf1bbbadd69503f071ce5de3826cee702e67a21d86f4fbe2d61b77', - ) - expect(actual.privateKey.toString('hex')).toBe( - 'de9b9b025d65b61a997c100419df05d1a26a4053f668e42e7ab2747ac6eed997', - ) - }) - }) -}) diff --git a/packages/crypto/__tests__/crypto/hdwallet.test.ts b/packages/crypto/__tests__/crypto/hdwallet.test.ts new file mode 100644 index 0000000000..1b07c315eb --- /dev/null +++ b/packages/crypto/__tests__/crypto/hdwallet.test.ts @@ -0,0 +1,161 @@ +import "jest-extended"; + +import bip32 from "bip32"; +import { crypto, HDWallet } from "../../src/crypto"; +import { configManager } from "../../src/managers/config"; +import { mainnet } from "../../src/networks"; + +const mnemonic = + "sorry hawk one science reject employ museum ride into post machine attack bar seminar myself unhappy faculty differ grain fish chest bird muffin mesh"; + +beforeEach(() => configManager.setConfig(mainnet)); + +describe("HDWallet", () => { + describe("bip32", () => { + it("can create a BIP32 wallet external address", () => { + const path = "m/0'/0/0"; + const root = bip32.fromSeed( + Buffer.from("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", "hex"), + ); + + const child1 = root.derivePath(path); + + // option 2, manually + const child2 = root + .deriveHardened(0) + .derive(0) + .derive(0); + + expect(crypto.getAddress(child1.publicKey.toString("hex"))).toBe("AZXdSTRFGHPokX6yfXTfHcTzzHKncioj31"); + expect(crypto.getAddress(child2.publicKey.toString("hex"))).toBe("AZXdSTRFGHPokX6yfXTfHcTzzHKncioj31"); + }); + }); + + describe("bip44", () => { + it("can create a BIP44, ark, account 0, external address", () => { + const path = "m/44'/111'/0'/0/0"; + const root = bip32.fromSeed( + Buffer.from("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", "hex"), + ); + + const child1 = root.derivePath(path); + + // option 2, manually + const child2 = root + .deriveHardened(44) + .deriveHardened(111) + .deriveHardened(0) + .derive(0) + .derive(0); + + expect(crypto.getAddress(child1.publicKey.toString("hex"))).toBe("AKdstZSrxzeF54e1M41fQzqGqjod9ydG3E"); + expect(crypto.getAddress(child2.publicKey.toString("hex"))).toBe("AKdstZSrxzeF54e1M41fQzqGqjod9ydG3E"); + }); + }); + + describe("fromMnemonic", () => { + it("should return the root node", () => { + const root = HDWallet.fromMnemonic(mnemonic); + expect(root.constructor.name).toBe("BIP32"); + }); + + it("should derive path", () => { + const root = HDWallet.fromMnemonic(mnemonic); + const node = root.derivePath("44'/1'/0'/0/0"); + expect(node.publicKey.toString("hex")).toBe( + "02126148679f22c162afa24a264a6b6722a61aab622248f2f536da289f48a9291f", + ); + expect(node.privateKey.toString("hex")).toBe( + "b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5", + ); + }); + }); + + describe("getKeys", () => { + it("should return keys from a node", () => { + const root = HDWallet.fromMnemonic(mnemonic); + const node = root.derivePath("44'/1'/0'/0/0"); + const keys = HDWallet.getKeys(node); + expect(keys.publicKey).toBe("02126148679f22c162afa24a264a6b6722a61aab622248f2f536da289f48a9291f"); + expect(keys.privateKey).toBe("b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5"); + expect(keys.compressed).toBeTrue(); + }); + }); + + describe("fromKeys", () => { + it("should return node from keys", () => { + const keys = { + publicKey: "", + privateKey: "b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5", + compressed: true, + }; + + const chainCode = Buffer.from("2bbe729fab21bf8bca70763caf7fe85752726a363b494dea7a65e51e2d423d7b", "hex"); + const node = HDWallet.fromKeys(keys, chainCode); + expect(node.publicKey.toString("hex")).toBe( + "02126148679f22c162afa24a264a6b6722a61aab622248f2f536da289f48a9291f", + ); + expect(node.privateKey.toString("hex")).toBe( + "b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5", + ); + }); + + it("should throw if keys are not compressed", () => { + const keys = { + publicKey: "", + privateKey: "b6f84081b674cf1f765ac182aaabd94d944c367d214263d1f7f3102d1cec98d5", + compressed: false, + }; + + const chainCode = Buffer.from("2bbe729fab21bf8bca70763caf7fe85752726a363b494dea7a65e51e2d423d7b", "hex"); + expect(() => HDWallet.fromKeys(keys, chainCode)).toThrow("BIP32 only allows compressed keys."); + }); + }); + + describe("deriveSlip44", () => { + it("should derive path", () => { + const root = HDWallet.fromMnemonic(mnemonic); + + const actual = HDWallet.deriveSlip44(root) + .deriveHardened(0) + .derive(0) + .derive(0); + + const expected = root + .deriveHardened(44) + .deriveHardened(111) + .deriveHardened(0) + .derive(0) + .derive(0); + + expect(crypto.getAddress(actual.publicKey.toString("hex"))).toBe("AHQhEsLWX5BbvvK836f1rUyZZZ77YikYq5"); + expect(actual.publicKey.toString("hex")).toBe( + "0330d7c2df15da16c72ac524f7548b2bca689beb0527ce54a50d3b79e4e91a8e9b", + ); + expect(actual.privateKey.toString("hex")).toBe( + "693bef1f16bad3c8096191af2362dae95873468fc5de30510b61d36fb3f1e33c", + ); + + expect(actual.publicKey).toEqual(expected.publicKey); + expect(actual.privateKey).toEqual(expected.privateKey); + }); + }); + + describe("deriveNetwork", () => { + it("should derive path", () => { + const root = HDWallet.fromMnemonic(mnemonic); + + const actual = HDWallet.deriveNetwork(root) + .deriveHardened(0) + .derive(0); + + expect(crypto.getAddress(actual.publicKey.toString("hex"))).toBe("AKjBp5V1xG9c5PQqUvtvtoGjvnyA3wLVpA"); + expect(actual.publicKey.toString("hex")).toBe( + "0281d69cadc9cf1bbbadd69503f071ce5de3826cee702e67a21d86f4fbe2d61b77", + ); + expect(actual.privateKey.toString("hex")).toBe( + "de9b9b025d65b61a997c100419df05d1a26a4053f668e42e7ab2747ac6eed997", + ); + }); + }); +}); diff --git a/packages/crypto/__tests__/crypto/message.test.js b/packages/crypto/__tests__/crypto/message.test.js deleted file mode 100644 index 75f154107a..0000000000 --- a/packages/crypto/__tests__/crypto/message.test.js +++ /dev/null @@ -1,64 +0,0 @@ -const Message = require('../../lib/crypto/message') -const { crypto } = require('../../lib/crypto') - -const passphrase = 'sample passphrase' -const wif = crypto.keysToWIF(crypto.getKeys(passphrase), { wif: 170 }) -const signedMessageEntries = [ - [ - 'publicKey', - '03bb51bbf5bf84759452e33dd97cf72cc8904be07df07a946a0d84939400f17e87', - ], - [ - 'signature', - '304402204550cd28d369a7f6eccd399b315e42e054a2f21f6771983af4ed3c5f7c7fa83102200699fef72cc64e79ccba85a31666e9508c052038c71c04260264e3d2d11c7e08', - ], - ['message', 'test'], -] - -describe('Message', () => { - describe('sign', () => { - it('should be a function', () => { - expect(Message.sign).toBeFunction() - }) - - it('should sign a message', () => { - expect(Message.sign('test', passphrase)).toContainAllEntries( - signedMessageEntries, - ) - }) - }) - - describe('signWithWif', () => { - it('should be a function', () => { - expect(Message.signWithWif).toBeFunction() - }) - - it('should sign a message', () => { - expect(Message.signWithWif('test', wif)).toContainAllEntries( - signedMessageEntries, - ) - }) - - it('should sign a message and match passphrase', () => { - const signedMessage = Message.sign('test', passphrase) - const signedWifMessage = Message.signWithWif('test', wif) - expect(signedMessage).toEqual(signedWifMessage) - }) - }) - - describe('verify', () => { - it('should be a function', () => { - expect(Message.verify).toBeFunction() - }) - - it('should verify a signed message', () => { - const signedMessage = Message.sign('test', passphrase) - expect(Message.verify(signedMessage)).toBe(true) - }) - - it('should verify a signed wif message', () => { - const signedMessage = Message.signWithWif('test', wif) - expect(Message.verify(signedMessage)).toBe(true) - }) - }) -}) diff --git a/packages/crypto/__tests__/crypto/message.test.ts b/packages/crypto/__tests__/crypto/message.test.ts new file mode 100644 index 0000000000..bae473b8c9 --- /dev/null +++ b/packages/crypto/__tests__/crypto/message.test.ts @@ -0,0 +1,47 @@ +import "jest-extended"; + +import { crypto } from "../../src/crypto"; +import { Message } from "../../src/crypto/message"; + +const passphrase = "sample passphrase"; +const wif = crypto.keysToWIF(crypto.getKeys(passphrase), { wif: 170 }); +const signedMessageEntries: any = [ + ["publicKey", "03bb51bbf5bf84759452e33dd97cf72cc8904be07df07a946a0d84939400f17e87"], + [ + "signature", + "304402204550cd28d369a7f6eccd399b315e42e054a2f21f6771983af4ed3c5f7c7fa83102200699fef72cc64e79ccba85a31666e9508c052038c71c04260264e3d2d11c7e08", + ], + ["message", "test"], +]; + +describe("Message", () => { + describe("sign", () => { + it("should sign a message", () => { + expect(Message.sign("test", passphrase)).toContainAllEntries(signedMessageEntries); + }); + }); + + describe("signWithWif", () => { + it("should sign a message", () => { + expect(Message.signWithWif("test", wif)).toContainAllEntries(signedMessageEntries); + }); + + it("should sign a message and match passphrase", () => { + const signedMessage = Message.sign("test", passphrase); + const signedWifMessage = Message.signWithWif("test", wif); + expect(signedMessage).toEqual(signedWifMessage); + }); + }); + + describe("verify", () => { + it("should verify a signed message", () => { + const signedMessage = Message.sign("test", passphrase); + expect(Message.verify(signedMessage)).toBe(true); + }); + + it("should verify a signed wif message", () => { + const signedMessage = Message.signWithWif("test", wif); + expect(Message.verify(signedMessage)).toBe(true); + }); + }); +}); diff --git a/packages/crypto/__tests__/crypto/slots.test.js b/packages/crypto/__tests__/crypto/slots.test.js deleted file mode 100644 index 5fdc725472..0000000000 --- a/packages/crypto/__tests__/crypto/slots.test.js +++ /dev/null @@ -1,154 +0,0 @@ -const configManager = require('../../lib/managers/config') -const network = require('../../lib/networks/ark/devnet.json') -const slots = require('../../lib/crypto/slots') - -beforeEach(() => configManager.setConfig(network)) - -describe('Slots', () => { - describe('getHeight', () => { - it('should be a function', () => { - expect(slots.getHeight).toBeFunction() - }) - - it('should return the set height', () => { - expect(slots.getHeight()).toBe(1) - }) - }) - - describe('setHeight', () => { - it('should be a function', () => { - expect(slots.setHeight).toBeFunction() - }) - - it('should set the height', () => { - slots.setHeight(123) - - expect(slots.getHeight()).toBe(123) - }) - }) - - describe('resetHeight', () => { - it('should be a function', () => { - expect(slots.resetHeight).toBeFunction() - }) - - it('should reset the height', () => { - slots.setHeight(123) - - expect(slots.getHeight()).toBe(123) - - slots.resetHeight() - - expect(slots.getHeight()).toBe(1) - }) - }) - - describe('getEpochTime', () => { - it('should be a function', () => { - expect(slots.getEpochTime).toBeFunction() - }) - - it('return epoch datetime', () => { - expect(slots.getEpochTime()).toBeNumber() - }) - }) - - describe('beginEpochTime', () => { - it('should be a function', () => { - expect(slots.beginEpochTime).toBeFunction() - }) - - it('return epoch datetime', () => { - expect(slots.beginEpochTime().toISOString()).toBe( - '2017-03-21T13:00:00.000Z', - ) - }) - - it('return epoch unix', () => { - expect(slots.beginEpochTime().unix()).toBe(1490101200) - }) - }) - - describe('getTime', () => { - it('should be a function', () => { - expect(slots.getTime).toBeFunction() - }) - - it('return epoch time as number', () => { - const result = slots.getTime(1490101210000) - - expect(result).toBeNumber() - expect(result).toEqual(10) - }) - }) - - describe('getRealTime', () => { - it('should be a function', () => { - expect(slots.getRealTime).toBeFunction() - }) - - it('return return real time', () => { - expect(slots.getRealTime(10)).toBe(1490101210000) - }) - }) - - describe('getSlotNumber', () => { - it('should be a function', () => { - expect(slots.getSlotNumber).toBeFunction() - }) - - it('return slot number', () => { - expect(slots.getSlotNumber(10)).toBe(1) - }) - }) - - describe('getSlotTime', () => { - it('should be a function', () => { - expect(slots.getSlotTime).toBeFunction() - }) - - it('returns slot time', () => { - expect(slots.getSlotTime(19614)).toBe(156912) - }) - }) - - describe('getNextSlot', () => { - it('should be a function', () => { - expect(slots.getNextSlot).toBeFunction() - }) - - it('returns next slot', () => { - expect(slots.getNextSlot()).toBeNumber() - }) - }) - - describe('getLastSlot', () => { - it('should be a function', () => { - expect(slots.getLastSlot).toBeFunction() - }) - - it('returns last slot', () => { - expect(slots.getLastSlot(1)).toBe(52) - }) - }) - - describe('getConstant', () => { - it('should be a function', () => { - expect(slots.getConstant).toBeFunction() - }) - - it('returns constant', () => { - expect(slots.getConstant('epoch')).toBe('2017-03-21T13:00:00.000Z') - }) - }) - - describe('isForgingAllowed', () => { - it('should be a function', () => { - expect(slots.isForgingAllowed).toBeFunction() - }) - - it('returns boolean', () => { - expect(slots.isForgingAllowed()).toBeDefined() - }) - }) -}) diff --git a/packages/crypto/__tests__/crypto/slots.test.ts b/packages/crypto/__tests__/crypto/slots.test.ts new file mode 100644 index 0000000000..4a6daac807 --- /dev/null +++ b/packages/crypto/__tests__/crypto/slots.test.ts @@ -0,0 +1,102 @@ +import "jest-extended"; + +import { slots } from "../../src/crypto/slots"; +import { configManager } from "../../src/managers/config"; +import { devnet } from "../../src/networks"; + +beforeEach(() => configManager.setConfig(devnet)); + +describe("Slots", () => { + describe("getHeight", () => { + it("should return the set height", () => { + expect(slots.getHeight()).toBe(1); + }); + }); + + describe("setHeight", () => { + it("should set the height", () => { + slots.setHeight(123); + + expect(slots.getHeight()).toBe(123); + }); + }); + + describe("resetHeight", () => { + it("should reset the height", () => { + slots.setHeight(123); + + expect(slots.getHeight()).toBe(123); + + slots.resetHeight(); + + expect(slots.getHeight()).toBe(1); + }); + }); + + describe("getEpochTime", () => { + it("return epoch datetime", () => { + expect(slots.getEpochTime()).toBeNumber(); + }); + }); + + describe("beginEpochTime", () => { + it("return epoch datetime", () => { + expect(slots.beginEpochTime().toISOString()).toBe("2017-03-21T13:00:00.000Z"); + }); + + it("return epoch unix", () => { + expect(slots.beginEpochTime().unix()).toBe(1490101200); + }); + }); + + describe("getTime", () => { + it("return epoch time as number", () => { + const result = slots.getTime(1490101210000); + + expect(result).toBeNumber(); + expect(result).toEqual(10); + }); + }); + + describe("getRealTime", () => { + it("return real time", () => { + expect(slots.getRealTime(10)).toBe(1490101210000); + }); + + it("should call this.getTime when called without time", () => { + const getTime = jest.spyOn(slots, "getTime"); + slots.getRealTime(undefined); + expect(getTime).toHaveBeenCalledTimes(1); + }); + }); + + describe("getSlotNumber", () => { + it("return slot number", () => { + expect(slots.getSlotNumber(10)).toBe(1); + }); + }); + + describe("getSlotTime", () => { + it("returns slot time", () => { + expect(slots.getSlotTime(19614)).toBe(156912); + }); + }); + + describe("getNextSlot", () => { + it("returns next slot", () => { + expect(slots.getNextSlot()).toBeNumber(); + }); + }); + + describe("getLastSlot", () => { + it("returns last slot", () => { + expect(slots.getLastSlot(1)).toBe(52); + }); + }); + + describe("isForgingAllowed", () => { + it("returns boolean", () => { + expect(slots.isForgingAllowed()).toBeDefined(); + }); + }); +}); diff --git a/packages/crypto/__tests__/crypto/utils.test.js b/packages/crypto/__tests__/crypto/utils.test.js deleted file mode 100644 index f02c42b693..0000000000 --- a/packages/crypto/__tests__/crypto/utils.test.js +++ /dev/null @@ -1,30 +0,0 @@ -const crypto = require('../../lib/crypto/utils') -const fixtures = require('./fixtures/crypto.json') - -const buffer = Buffer.from('Hello World') - -describe('Crypto - Utils', () => { - it('should be instantiated', () => { - expect(crypto).toBeObject() - }) - - it('should return valid ripemd160', () => { - expect(crypto.ripemd160(buffer).toString('hex')).toEqual(fixtures.ripemd160) - }) - - it('should return valid sha1', () => { - expect(crypto.sha1(buffer).toString('hex')).toEqual(fixtures.sha1) - }) - - it('should return valid sha256', () => { - expect(crypto.sha256(buffer).toString('hex')).toEqual(fixtures.sha256) - }) - - it('should return valid hash160', () => { - expect(crypto.hash160(buffer).toString('hex')).toEqual(fixtures.hash160) - }) - - it('should return valid hash256', () => { - expect(crypto.hash256(buffer).toString('hex')).toEqual(fixtures.hash256) - }) -}) diff --git a/packages/crypto/__tests__/deserializers/block.test.ts b/packages/crypto/__tests__/deserializers/block.test.ts new file mode 100644 index 0000000000..c9c848d76c --- /dev/null +++ b/packages/crypto/__tests__/deserializers/block.test.ts @@ -0,0 +1,74 @@ +import { configManager } from "../../src/managers"; +import { dummyBlock2, dummyBlock3 } from "../fixtures/block"; + +let BlockDeserializer; +let BlockSerializer; + +describe("block deserializer", () => { + describe("deserialize", () => { + it("should get block id from outlook table", () => { + const outlookTableBlockId = "123456"; + const getPresetOrig = configManager.getPreset; + jest.spyOn(configManager, "getPreset").mockImplementation(network => { + const preset = getPresetOrig(network); + preset.exceptions.outlookTable = { + [dummyBlock3.id]: outlookTableBlockId, + }; + return preset; + }); + BlockDeserializer = require("../../src/deserializers").BlockDeserializer; + BlockSerializer = require("../../src/serializers").BlockSerializer; + + const deserialized = BlockDeserializer.deserialize( + BlockSerializer.serialize(dummyBlock3).toString("hex"), + true, + ); + + expect(deserialized.id).toEqual(outlookTableBlockId); + }); + + it("should correctly deserialize a block", () => { + const deserialized = BlockDeserializer.deserialize(dummyBlock2.serializedFull); + + const blockFields = [ + "id", + "timestamp", + "version", + "height", + "previousBlock", + "numberOfTransactions", + "totalAmount", + "totalFee", + "reward", + "payloadLength", + "payloadHash", + "generatorPublicKey", + "blockSignature", + ]; + blockFields.forEach(field => { + expect(deserialized[field].toString()).toEqual(dummyBlock2.data[field].toString()); + }); + + expect(deserialized.transactions).toHaveLength(dummyBlock2.data.transactions.length); + + const transactionFields = [ + "id", + "type", + "timestamp", + "senderPublicKey", + "fee", + "amount", + "recipientId", + "vendorField", + "signature", + ]; + deserialized.transactions.forEach(tx => { + const dummyBlockTx = dummyBlock2.data.transactions.find(dummyTx => dummyTx.id === tx.data.id); + expect(dummyBlockTx).toBeDefined(); + transactionFields.forEach(field => { + expect(tx.data[field].toString()).toEqual(dummyBlockTx[field].toString()); + }); + }); + }); + }); +}); diff --git a/packages/crypto/__tests__/deserializers/transaction.test.ts b/packages/crypto/__tests__/deserializers/transaction.test.ts new file mode 100644 index 0000000000..6120b72b23 --- /dev/null +++ b/packages/crypto/__tests__/deserializers/transaction.test.ts @@ -0,0 +1,301 @@ +import ByteBuffer from "bytebuffer"; +import { client } from "../../src/client"; +import { TransactionDeserializer } from "../../src/deserializers"; +import { TransactionSerializer } from "../../src/serializers"; +import { Bignum } from "../../src/utils"; + +describe("Transaction serializer / deserializer", () => { + const checkCommonFields = (deserialized, expected) => { + const fieldsToCheck = ["version", "network", "type", "timestamp", "senderPublicKey", "fee", "amount"]; + fieldsToCheck.forEach(field => { + expect(deserialized[field].toString()).toEqual(expected[field].toString()); + }); + }; + + describe("ser/deserialize - transfer", () => { + const transfer = client + .getBuilder() + .transfer() + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(10000) + .fee(50000000) + .vendorField("yo") + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + it("should ser/deserialize giving back original fields", () => { + const serialized = TransactionSerializer.serialize(transfer).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, transfer); + + expect(deserialized.vendorField).toBe(transfer.vendorField); + expect(deserialized.recipientId).toBe(transfer.recipientId); + }); + + it("should ser/deserialize giving back original fields - with vendorFieldHex", () => { + delete transfer.vendorField; + const vendorField = "cool vendor field"; + transfer.vendorFieldHex = new Buffer(vendorField).toString("hex"); + + const serialized = TransactionSerializer.serialize(transfer).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, transfer); + + expect(deserialized.vendorField).toBe(vendorField); + expect(deserialized.recipientId).toBe(transfer.recipientId); + }); + }); + + describe("ser/deserialize - second signature", () => { + it("should ser/deserialize giving back original fields", () => { + const secondSignature = client + .getBuilder() + .secondSignature() + .signatureAsset("signature") + .fee(50000000) + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + const serialized = TransactionSerializer.serialize(secondSignature).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, secondSignature); + + expect(deserialized.asset).toEqual(secondSignature.asset); + }); + }); + + describe("ser/deserialize - delegate registration", () => { + it("should ser/deserialize giving back original fields", () => { + const delegateRegistration = client + .getBuilder() + .delegateRegistration() + .usernameAsset("homer") + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + const serialized = TransactionSerializer.serialize(delegateRegistration).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, delegateRegistration); + + expect(deserialized.asset).toEqual(delegateRegistration.asset); + }); + }); + + describe("ser/deserialize - vote", () => { + it("should ser/deserialize giving back original fields", () => { + const vote = client + .getBuilder() + .vote() + .votesAsset(["+02bcfa0951a92e7876db1fb71996a853b57f996972ed059a950d910f7d541706c9"]) + .fee(50000000) + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + const serialized = TransactionSerializer.serialize(vote).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, vote); + + expect(deserialized.asset).toEqual(vote.asset); + }); + }); + + describe("ser/deserialize - multi signature", () => { + const multiSignature = client + .getBuilder() + .multiSignature() + .multiSignatureAsset({ + keysgroup: [ + "+0376982a97dadbc65e694743d386084548a65431a82ce935ac9d957b1cffab2784", + "+03793904e0df839809bc89f2839e1ae4f8b1ea97ede6592b7d1e4d0ee194ca2998", + ], + lifetime: 72, + min: 2, + }) + .version(1) + .network(30) + .sign("dummy passphrase") + .multiSignatureSign("multi passphrase 1") + .multiSignatureSign("multi passphrase 2") + .getStruct(); + + it("should ser/deserialize giving back original fields", () => { + const serialized = TransactionSerializer.serialize(multiSignature).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, multiSignature); + + expect(deserialized.asset).toEqual(multiSignature.asset); + }); + + it("should ser/deserialize giving back original fields - v2 keysgroup", () => { + multiSignature.asset.multisignature.keysgroup = [ + "0376982a97dadbc65e694743d386084548a65431a82ce935ac9d957b1cffab2784", + "03793904e0df839809bc89f2839e1ae4f8b1ea97ede6592b7d1e4d0ee194ca2998", + ]; + multiSignature.version = 2; + + const serialized = TransactionSerializer.serialize(multiSignature).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, multiSignature); + + expect(deserialized.asset).toEqual(multiSignature.asset); + }); + }); + + describe("ser/deserialize - ipfs", () => { + it("should ser/deserialize giving back original fields", () => { + const ipfs = client + .getBuilder() + .ipfs() + .fee(50000000) + .version(1) + .network(30) + .dag("da304502") + .sign("dummy passphrase") + .getStruct(); + + const serialized = TransactionSerializer.serialize(ipfs).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, ipfs); + + expect(deserialized.asset).toEqual(ipfs.asset); + }); + }); + + describe("ser/deserialize - timelock transfer", () => { + it("should ser/deserialize giving back original fields", () => { + const timelockTransfer = client + .getBuilder() + .timelockTransfer() + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(10000) + .fee(50000000) + .version(1) + .network(30) + .timelock(12, 0x00) + .sign("dummy passphrase") + .getStruct(); + + // expect(timelockTransfer).toEqual({}) + const serialized = TransactionSerializer.serialize(timelockTransfer).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, timelockTransfer); + + expect(deserialized.timelockType).toEqual(timelockTransfer.timelockType); + expect(deserialized.timelock).toEqual(timelockTransfer.timelock); + }); + }); + + describe("ser/deserialize - multi payment", () => { + it("should ser/deserialize giving back original fields", () => { + const multiPayment = client + .getBuilder() + .multiPayment() + .fee(50000000) + .version(1) + .network(30) + .addPayment("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F", 1555) + .addPayment("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F", 5000) + .sign("dummy passphrase") + .getStruct(); + + const serialized = TransactionSerializer.serialize(multiPayment).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, multiPayment); + + expect(deserialized.asset).toEqual(multiPayment.asset); + }); + }); + + describe("ser/deserialize - delegate resignation", () => { + it("should ser/deserialize giving back original fields", () => { + const delegateResignation = client + .getBuilder() + .delegateResignation() + .fee(50000000) + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + const serialized = TransactionSerializer.serialize(delegateResignation).toString("hex"); + const deserialized = TransactionDeserializer.deserialize(serialized); + + checkCommonFields(deserialized, delegateResignation); + }); + }); + + describe("deserialize - others", () => { + it("should throw if type is not supported", () => { + const serializeWrongType = transaction => { + // copy-paste from transaction serializer, common stuff + const buffer = new ByteBuffer(512, true); + buffer.writeByte(0xff); // fill, to disambiguate from v1 + buffer.writeByte(transaction.version || 0x01); // version + buffer.writeByte(transaction.network); + buffer.writeByte(transaction.type); + buffer.writeUint32(transaction.timestamp); + buffer.append(transaction.senderPublicKey, "hex"); + buffer.writeUint64(+new Bignum(transaction.fee).toFixed()); + buffer.writeByte(0x00); + + return Buffer.from(buffer.flip().toBuffer()); + }; + const transactionWrongType = client + .getBuilder() + .transfer() + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(10000) + .fee(50000000) + .vendorField("yo") + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + transactionWrongType.type = 55; + + const serialized = serializeWrongType(transactionWrongType).toString("hex"); + expect(() => TransactionDeserializer.deserialize(serialized)).toThrow( + `Type ${transactionWrongType.type} not supported.`, + ); + }); + }); + + describe("serialize - others", () => { + it("should throw if type is not supported", () => { + const transactionWrongType = client + .getBuilder() + .transfer() + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(10000) + .fee(50000000) + .vendorField("yo") + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + transactionWrongType.type = 55; + + expect(() => TransactionSerializer.serialize(transactionWrongType)).toThrow( + `Type ${transactionWrongType.type} not supported.`, + ); + }); + }); +}); diff --git a/packages/crypto/__tests__/fixtures/block.ts b/packages/crypto/__tests__/fixtures/block.ts new file mode 100644 index 0000000000..f8b223e113 --- /dev/null +++ b/packages/crypto/__tests__/fixtures/block.ts @@ -0,0 +1,327 @@ +export const dummyBlock = { + id: "7176646138626297930", + version: 0, + height: 2243161, + timestamp: 24760440, + previousBlock: "3112633353705641986", + numberOfTransactions: 7, + totalAmount: "3890300", + totalFee: "70000000", + reward: "200000000", + payloadLength: 224, + payloadHash: "3784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282", + generatorPublicKey: "020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a325", + blockSignature: + "3045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", + transactions: [ + { + type: 0, + amount: 555760, + fee: 10000000, + recipientId: "DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY", + timestamp: 24760418, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb978", + signSignature: + "30450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717", + id: "170543154a3b79459cbaa529f9f62b6f1342682799eb549dbf09fcca2d1f9c11", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555750, + fee: 10000000, + recipientId: "DGExsNogZR7JFa2656ZFP9TMWJYJh5djzQ", + timestamp: 24760416, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727", + signSignature: + "304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9", + id: "1da153f37eceda233ff1b407ac18e47b3cae47c14cdcd5297d929618a916c4a7", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555770, + fee: 10000000, + recipientId: "DHGK5np6LuMMErfRfC5CmjpGu3ME85c25n", + timestamp: 24760420, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a", + signSignature: + "3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099f", + id: "1e255f07dc25ce22d900ea81663c8f00d05a7b7c061e6fc3c731b05d642fa0b9", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555750, + fee: 10000000, + recipientId: "D7pcLJNGe197ibmWEmT8mM9KKU1htrcDyW", + timestamp: 24760417, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11", + signSignature: + "304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79", + id: "66336c61d6ec623f8a1d2fd156a0fac16a4fe93bb3fba337859355c2119923a8", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555760, + fee: 10000000, + recipientId: "DD4yhwzryQdNGqKtezmycToQv63g27Tqqq", + timestamp: 24760418, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "30450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e8", + signSignature: + "30440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64", + id: "78db36f7d79f51c67d7210ee3819dfb8d0d47b16a7484ebf55c5a055b17209a3", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555760, + fee: 10000000, + recipientId: "D5LiYGXL5keycWuTF6AFFwSRc6Mt4uEHMu", + timestamp: 24760419, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e", + signSignature: + "3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739", + id: "83c80bb58777bb43f5037544b44ef69f191d3548fd1b2a00bed368f9f0d694c5", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555750, + fee: 10000000, + recipientId: "DPopNLwMvv4zSjdZnqUk8HFH13Mcb7NbEK", + timestamp: 24760416, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "3045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4", + signSignature: + "304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb", + id: "d2faf992fdd5da96d6d15038b6ddb65230338fa2096e45e44da51daad5e2f3ca", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + ], +}; + +export const dummyBlock2 = { + data: { + id: "7176646138626297930", + version: 0, + height: 2243161, + timestamp: 24760440, + previousBlock: "3112633353705641986", + numberOfTransactions: 7, + totalAmount: "3890300", + totalFee: "70000000", + reward: "200000000", + payloadLength: 224, + payloadHash: "3784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282", + generatorPublicKey: "020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a325", + blockSignature: + "3045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", + transactions: [ + { + type: 0, + amount: 555760, + fee: 10000000, + recipientId: "DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY", + timestamp: 24760418, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb978", + signSignature: + "30450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717", + id: "170543154a3b79459cbaa529f9f62b6f1342682799eb549dbf09fcca2d1f9c11", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555750, + fee: 10000000, + recipientId: "DGExsNogZR7JFa2656ZFP9TMWJYJh5djzQ", + timestamp: 24760416, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727", + signSignature: + "304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9", + id: "1da153f37eceda233ff1b407ac18e47b3cae47c14cdcd5297d929618a916c4a7", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555770, + fee: 10000000, + recipientId: "DHGK5np6LuMMErfRfC5CmjpGu3ME85c25n", + timestamp: 24760420, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a", + signSignature: + "3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099f", + id: "1e255f07dc25ce22d900ea81663c8f00d05a7b7c061e6fc3c731b05d642fa0b9", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555750, + fee: 10000000, + recipientId: "D7pcLJNGe197ibmWEmT8mM9KKU1htrcDyW", + timestamp: 24760417, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11", + signSignature: + "304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79", + id: "66336c61d6ec623f8a1d2fd156a0fac16a4fe93bb3fba337859355c2119923a8", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555760, + fee: 10000000, + recipientId: "DD4yhwzryQdNGqKtezmycToQv63g27Tqqq", + timestamp: 24760418, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "30450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e8", + signSignature: + "30440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64", + id: "78db36f7d79f51c67d7210ee3819dfb8d0d47b16a7484ebf55c5a055b17209a3", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555760, + fee: 10000000, + recipientId: "D5LiYGXL5keycWuTF6AFFwSRc6Mt4uEHMu", + timestamp: 24760419, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e", + signSignature: + "3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739", + id: "83c80bb58777bb43f5037544b44ef69f191d3548fd1b2a00bed368f9f0d694c5", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + { + type: 0, + amount: 555750, + fee: 10000000, + recipientId: "DPopNLwMvv4zSjdZnqUk8HFH13Mcb7NbEK", + timestamp: 24760416, + asset: {}, + vendorField: "Goose Voter - True Block Weight", + senderPublicKey: "0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0", + signature: + "3045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4", + signSignature: + "304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb", + id: "d2faf992fdd5da96d6d15038b6ddb65230338fa2096e45e44da51daad5e2f3ca", + senderId: "DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg", + hop: 2, + broadcast: false, + blockId: "7176646138626297930", + }, + ], + }, + serialized: + "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29", + serializedFull: + "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29ff000000fe00000000010000ff000000ff000000ff000000ff000000ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e40fad23d21da7a4fd4decb5c49726ea22f5e6bf6304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb97830450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e79c579fb08f448879c22fe965906b4e3b88d02ed304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9ff011e0064d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874fa7a080000000000000000001e84fee45dde2b11525afe192a2e991d014ff93a36304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099fff011e0061d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e1d69583ede5ee82d220e74bffb36bae2ce762dfb3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e56f9a37a859f4f84e93ce7593e809b15a524db2930450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e830440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64ff011e0063d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e0232a083c16aba4362dddec1b3050ffdd6d43f2e3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001eccc4fce0dc95f9951ee40c09a7ae807746cf51403045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb", +}; + +export const dummyBlock3 = { + id: "7242383292164246617", + version: 0, + timestamp: 46583338, + height: 3, + reward: "0", + previousBlock: "17882607875259085966", + numberOfTransactions: 0, + totalAmount: "0", + totalFee: "0", + payloadLength: 0, + payloadHash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + generatorPublicKey: "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + blockSignature: + "304402204087bb1d2c82b9178b02b9b3f285de260cdf0778643064fe6c7aef27321d49520220594c57009c1fca543350126d277c6adeb674c00685a464c3e4bf0d634dc37e39", + createdAt: "2018-09-11T16:48:58.431Z", +}; diff --git a/packages/crypto/__tests__/fixtures/multi-transaction.ts b/packages/crypto/__tests__/fixtures/multi-transaction.ts new file mode 100644 index 0000000000..9aa165dbc0 --- /dev/null +++ b/packages/crypto/__tests__/fixtures/multi-transaction.ts @@ -0,0 +1,59 @@ +export const multiTransaction = { + version: 1, + network: 30, + type: 4, + timestamp: 25921690, + senderPublicKey: "02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece", + fee: 9000000000, + vendorFieldHex: "776c386c777473386a65", + asset: { + multisignature: { + min: 15, + lifetime: 72, + keysgroup: [ + "+034a7aca6841cfbdc688f09d55345f21c7ffbd1844693fa68d607fc94f729cbbea", + "+02fd6743ddfdc7c5bac24145e449c2e4f2d569b5297dd7bf088c3bc219f582a2f0", + "+02f9c51812f4be127b9f1f21cb4e146eca6aecc85739a243db0f1064981deda216", + "+0214d60ca95cd87a097ed6e6e42281acb68ae1815c8f494b8ff18d24dc9e072171", + "+02a14634e04e80b05acd56bc361af98498d76fbf5233f8d62773ceaa07172ddaa6", + "+021a9ba0e72f02b8fa7bad386582ec1d6c05b7664c892bf2a86035a85350f37203", + "+02e3ba373c6c352316b748e75358ead36504b0ef5139d215fb6a83a330c4eb60d5", + "+0309039bfa18d6fd790edb79438783b27fbcab06040a2fdaa66fb81ad53ca8db5f", + "+0393d12aff5962fa9065487959719a81c5d991e7c48a823039acd9254c2b673841", + "+03d3265264f06fe1dd752798403a73e537eb461fc729c83a84b579e8434adfe7c4", + "+02acfa496a6c12cb9acc3219993b17c62d19f4b570996c12a37d6e89eaa9716859", + "+03136f2101f1767b0d63d9545410bcaf3a941b2b6f06851093f3c679e0d31ca1cd", + "+02e6ec3941be86177bf0b998589c07da1b73e990466fdaee347c972c10f61b3797", + "+037dcd05d921a9f2ddd11960fec2ea9904fc55cad030549a6c5f5a41b2e35e56d2", + "+03206f7ae26f14cffb62b8c28b5e632952cdeb84b7c74ac0c2198b08bd84ee4f23", + ], + }, + }, + signature: + "3045022100c59d8efdd95010b0368963e492e8f7da1a3f6993b0723724f84aeccf658ea9a30220103d2b4ac07ad808b4ea79829a0081992586c6ef73892cfa7510ab37f0093bcd", + secondSignature: + "3044022059ab88d3fe83d6b52ddd5e863696416168f9cac6de962368587ad7343542f99f02207a95ca473006ab57dee6b06e06f8fa8f1b260548b9226614a0e1f760cc910479", + signatures: [ + "3045022100e9d0015e6e50e4852ad057ae6f83d0bf3e28fd722d1acf58f1a069a8f5043cf002201ee5b35b7f61af853802020fecf629ab59309c09d7e002b20636bdbb26ea92e3", + "3044022042c796e7f447462e60dfeeb8233d5fde3026e9c0aeda4544ae47995694d815a802204a183ae7d885fd875f6795d047364a73802c0d967d32cd797ee765ac10715f2d", + "30450221008a000e62a63298f401c616ae7222df9c187c92906736c0f879ba1162647e25c00220612cf87641075334b37f6e19590fcb45f9941aa50092fd429eda375caf5b8144", + "3045022100bd5ee0064df149406c7b4a64116c913181d7527a078972591280305b14686232022039e7437629b935ac39e2f547cbf191ffad4ab83805a360f50b49176dd5d604cc", + "30440220133a8dde6d87bcc8f627a57e7a1ad8a1f441d77246dee9d9d999851b4aeecbf102205c93de5881e6fd8ce808552b2ee91c6e144a734a9ef8640e0ca055904d5e5bd1", + "3045022100bb9de3351337532b42598a4bccba2f07a5a1dbbb23859094dd90eafa409c7bf3022071ff0989dc6f9c95b95784d3aa3386940a46372d3fb50b7f4e9d6372b0d3b73d", + "3044022075e2e0137f1567b7efeac40faf5b8d55ef32540c7d3931c27ae45748241a118e02203cbf5c251eec1e2c033b556315069bcc4f04960bfc705db6288530de28211fc9", + "30440220259d5ed227ec1cd6275f6e7c6dd299f73500f8b4aa21025296c87419e91e5900022045a70031c442c336f2b280a94e7058976c7f8ae6617fd4fbf41b913ac608c54a", + "3045022100f8537736fb7e25e9ccb4e5d6f3cf7f053d45dc7ff7d41914e4ab9f7b48b9347502204a46de5fd0cc03e2e1a559bc5ffc25a1fce5dc05d5f6f1794a0f3b9f38ebe406", + "3045022100d361e24290fddf2ad194c9a29200d88ccae83afc0f930e5fd5b2ecefcd2cebf3022065049135ced0fe238270511a748ba82be72f6298e2895d8b20680698bbc03ef5", + "30440220556b517965101ef1e0cf59eead99b802b053ac4a631111348d961df29952f2150220630ca079a5647d7d1f22b6f253d58b1f7fb128be9aeb33c9c1fdf53948e52db4", + "3044022047838187fcd1c79059ab53769678940e634400cf7431337a7e27e30bc17ac39702200d2cb974f44239b0edbb7c10096d5d7b682d0788dc3f8d0f5061373f4eefdff8", + "3044022057bbd8432154bf72244f987c5b4c49ba094a0e4abffc3644ba91c5ce1b79712a0220708a2743d4b4ab1d9379bcd60acf70b63e16cd24b95cd142a0b3ced0d43e6cb8", + "304502210097e1a36e47422455f030fa5f79153a4c49d25a2fb487837462b7f9d0fa3bd436022008a212ce0dfa18543987a1381b1261652d76e49f923e0b2ac79073b22367b3a5", + "304402201cbe68b0383c243cf61a9af9142746688b85f6ee106815906d9d8c4d717837aa022038c1eb4e06b90e24b44903de18c3f260e8be945b84126c9a048794f830b9f272", + ], + amount: 0, + signSignature: + "3044022059ab88d3fe83d6b52ddd5e863696416168f9cac6de962368587ad7343542f99f02207a95ca473006ab57dee6b06e06f8fa8f1b260548b9226614a0e1f760cc910479", + vendorField: "wl8lwts8je", + recipientId: "D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7", + id: "95199581d640eda97ea810fc3248e34f6e5ab1d8c9802e64a50b930f4ff044ab", +}; diff --git a/packages/crypto/__tests__/fixtures/transaction.ts b/packages/crypto/__tests__/fixtures/transaction.ts new file mode 100644 index 0000000000..6ecdb86978 --- /dev/null +++ b/packages/crypto/__tests__/fixtures/transaction.ts @@ -0,0 +1,29 @@ +export const transaction = { + version: 1, + network: 30, + type: 4, + timestamp: 48069935, + senderPublicKey: "023b31dd57d9025a538ed6433c8e0d339abba3084612e8bbbe09a240e0208b33c9", + fee: 2000000000, + asset: { + multisignature: { + keysgroup: [ + "+02db1d199f20038e569500895b3521a453b2924e4a07c75aa9f7bf2aa4ad71392d", + "+02a7442df1f6cbef57d84c9c0eff248f9af48370384987de90bdcebd000feccdb6", + "+037a9458c87080768f79c4320941fdc64c9fe580673f17358125b93e80bd0b1d27", + ], + min: 3, + lifetime: 72, + }, + }, + signature: + "3045022100874eed54c9b4e8e41e47cfa340b38b87d6a16469d67462b066aea51fb3a8918d02205d812e19f382cfee5f9ce3bc98abc050f5aaa8262d9a2bf24d5cb117840d2948", + signatures: [ + "3044022022fb3b1d48d9e4905ab566949d637f0832dd0ab6f2cb67a620496e23e83a86d902203182ad967d22db258f97f9fab6d3856c29738ae745eb2f40eb5d472722b794b9", + "3045022100aef482ecaea6ecaf8e6f86bd7ac474458e657614b3eb9e440789549d1ea85f6002205c75763411e0febb7d11a7ccf7cb826fc11ddbe3722b73f77e22e9f0919e179d", + "3045022100e1dff5c0a4289ffee8caa79fd25fe86f0ded4daaeb9f25e123ea327b01fdb9710220476da4d177652fe4a375e414089ce8c86800bcc4ca6ce0b6d974ef98d8c9d4cf", + ], + amount: 0, + recipientId: "DJLxkgm7JMortrGVh1ZrvDH39XALWLa83e", + id: "115d68cd8a8a9e589ddd51b721085831ce9472273b64b14e195e31dfbab4bf4e", +}; diff --git a/packages/crypto/__tests__/handlers/transactions/__fixtures__/transaction.js b/packages/crypto/__tests__/handlers/transactions/__fixtures__/transaction.js deleted file mode 100644 index f50d134948..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/__fixtures__/transaction.js +++ /dev/null @@ -1,18 +0,0 @@ -const Bignum = require('../../../../lib/utils/bignum') - -module.exports = { - version: 1, - id: '943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4', - blockid: '11233167632577333611', - type: 0, - timestamp: 36482198, - amount: new Bignum(100000000), - fee: new Bignum(10000000), - senderId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - recipientId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - senderPublicKey: - '034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126', - signature: - '304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b', // eslint-disable-line max-len - asset: {}, -} diff --git a/packages/crypto/__tests__/handlers/transactions/__fixtures__/transaction.ts b/packages/crypto/__tests__/handlers/transactions/__fixtures__/transaction.ts new file mode 100644 index 0000000000..7f80601d98 --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/__fixtures__/transaction.ts @@ -0,0 +1,17 @@ +import { Bignum } from "../../../../src/utils/bignum"; + +export const transaction = { + version: 1, + id: "943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4", + blockid: "11233167632577333611", + type: 0, + timestamp: 36482198, + amount: new Bignum(100000000), + fee: new Bignum(10000000), + senderId: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + recipientId: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + senderPublicKey: "034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126", + signature: + "304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b", + asset: {}, +}; diff --git a/packages/crypto/__tests__/handlers/transactions/__fixtures__/wallet.js b/packages/crypto/__tests__/handlers/transactions/__fixtures__/wallet.js deleted file mode 100644 index 07dc131a74..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/__fixtures__/wallet.js +++ /dev/null @@ -1,8 +0,0 @@ -const Bignum = require('../../../../lib/utils/bignum') - -module.exports = { - address: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - balance: new Bignum(4527654310), - publicKey: - '034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126', -} diff --git a/packages/crypto/__tests__/handlers/transactions/__fixtures__/wallet.ts b/packages/crypto/__tests__/handlers/transactions/__fixtures__/wallet.ts new file mode 100644 index 0000000000..b1d53103ad --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/__fixtures__/wallet.ts @@ -0,0 +1,7 @@ +import { Bignum } from "../../../../src/utils/bignum"; + +export const wallet = { + address: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + balance: new Bignum(4527654310), + publicKey: "034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126", +}; diff --git a/packages/crypto/__tests__/handlers/transactions/delegate-registration.test.js b/packages/crypto/__tests__/handlers/transactions/delegate-registration.test.js deleted file mode 100644 index 2323a82c49..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/delegate-registration.test.js +++ /dev/null @@ -1,79 +0,0 @@ -const Bignum = require('../../../lib/utils/bignum') -const handler = require('../../../lib/handlers/transactions/delegate-registration') - -let wallet -let transaction - -beforeEach(() => { - wallet = require('./__fixtures__/wallet') - - transaction = { - version: 1, - id: '943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4', - blockid: '11233167632577333611', - type: 2, - timestamp: 36482198, - amount: Bignum.ZERO, - fee: new Bignum(10000000), - senderId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - recipientId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - senderPublicKey: - '034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126', - signature: - '304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b', // eslint-disable-line max-len - asset: { - delegate: { - username: 'dummy', - publicKey: 'a'.repeat(66), - }, - }, - } -}) - -describe('DelegateRegistrationHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('DelegateRegistrationHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false if wallet already registered a username', () => { - wallet.username = 'dummy' - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Wallet already has a registered username') - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - - it('should set username', () => { - handler.apply(wallet, transaction) - - expect(wallet.username).toBe('dummy') - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - - it('should unset username', () => { - handler.revert(wallet, transaction) - - expect(wallet.username).toBeNull() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/delegate-registration.test.ts b/packages/crypto/__tests__/handlers/transactions/delegate-registration.test.ts new file mode 100644 index 0000000000..f570b7369b --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/delegate-registration.test.ts @@ -0,0 +1,75 @@ +import "jest-extended"; +import { DelegateRegistrationHandler } from "../../../src/handlers/transactions/delegate-registration"; +import { Bignum } from "../../../src/utils/bignum"; +import { wallet as originalWallet } from "./__fixtures__/wallet"; + +const handler = new DelegateRegistrationHandler(); + +let wallet; +let transaction; +let errors; + +beforeEach(() => { + wallet = originalWallet; + + transaction = { + version: 1, + id: "943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4", + blockid: "11233167632577333611", + type: 2, + timestamp: 36482198, + amount: Bignum.ZERO, + fee: new Bignum(10000000), + senderId: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + recipientId: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + senderPublicKey: "034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126", + signature: + "304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b", + asset: { + delegate: { + username: "dummy", + publicKey: ("a" as any).repeat(66), + }, + }, + }; + + errors = []; +}); + +describe("DelegateRegistrationHandler", () => { + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it("should be false if wallet already registered a username", () => { + wallet.username = "dummy"; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Wallet already has a registered username"); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); + + describe("apply", () => { + it("should set username", () => { + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.username).toBe("dummy"); + }); + }); + + describe("revert", () => { + it("should unset username", () => { + handler.revertTransactionForSender(wallet, transaction); + + expect(wallet.username).toBeNull(); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/delegate-resignation.test.js b/packages/crypto/__tests__/handlers/transactions/delegate-resignation.test.js deleted file mode 100644 index 80728652fb..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/delegate-resignation.test.js +++ /dev/null @@ -1,47 +0,0 @@ -const handler = require('../../../lib/handlers/transactions/delegate-resignation') - -let wallet -let transaction - -beforeEach(() => { - wallet = require('./__fixtures__/wallet') - transaction = require('./__fixtures__/transaction') -}) - -describe('DelegateResignationHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('DelegateResignationHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be truth', () => { - wallet.username = 'dummy' - - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false if wallet has no registered username', () => { - wallet.username = null - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Wallet has not registered a username') - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/delegate-resignation.test.ts b/packages/crypto/__tests__/handlers/transactions/delegate-resignation.test.ts new file mode 100644 index 0000000000..c9ac61c7c2 --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/delegate-resignation.test.ts @@ -0,0 +1,43 @@ +import "jest-extended"; + +import { DelegateResignationHandler } from "../../../src/handlers/transactions/delegate-resignation"; +import { Bignum } from "../../../src/utils/bignum"; +import { transaction as originalTransaction } from "./__fixtures__/transaction"; +import { wallet as originalWallet } from "./__fixtures__/wallet"; + +const handler = new DelegateResignationHandler(); + +let wallet; +let transaction; +let errors; + +beforeEach(() => { + wallet = originalWallet; + transaction = originalTransaction; + + errors = []; +}); + +describe("DelegateResignationHandler", () => { + describe("canApply", () => { + it("should be truth", () => { + wallet.username = "dummy"; + + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it("should be false if wallet has no registered username", () => { + wallet.username = null; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Wallet has not registered a username"); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/handler.test.js b/packages/crypto/__tests__/handlers/transactions/handler.test.js deleted file mode 100644 index fabe3274e1..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/handler.test.js +++ /dev/null @@ -1,229 +0,0 @@ -const BaseHandler = require('../../../lib/handlers/transactions/handler') -const { ARKTOSHI } = require('../../../lib/constants') -const Bignum = require('../../../lib/utils/bignum') - -let handler -let wallet -let transaction - -beforeEach(() => { - handler = new BaseHandler() - - wallet = { - address: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - balance: new Bignum(4527654310), - publicKey: - '034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126', - } - - transaction = { - version: 1, - id: '943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4', - blockid: '11233167632577333611', - type: 0, - timestamp: 36482198, - amount: new Bignum(100000000), - fee: new Bignum(10000000), - senderId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - recipientId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - senderPublicKey: - '034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126', - signature: - '304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b', // eslint-disable-line max-len - asset: {}, - } -}) - -describe('Handler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('Handler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - const errors = [] - expect(handler.canApply(wallet, transaction, errors)).toBeTrue() - expect(errors).toHaveLength(0) - }) - - it('should be false if wallet publicKey does not match tx senderPublicKey', () => { - transaction.senderPublicKey = 'a'.repeat(66) - const errors = [] - const result = handler.canApply(wallet, transaction, errors) - - expect(result).toBeFalse() - expect(errors).toContain( - 'wallet "publicKey" does not match transaction "senderPublicKey"', - ) - }) - - it('should be true even with publicKey case mismatch', () => { - transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase() - wallet.publicKey = wallet.publicKey.toLowerCase() - - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - }) - - describe('applyTransactionToSender', () => { - it('should be a function', () => { - expect(handler.applyTransactionToSender).toBeFunction() - }) - - it('should be ok', () => { - handler.apply = jest.fn() - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.applyTransactionToSender(wallet, transaction) - - expect(wallet.balance).toEqual( - new Bignum(initialBalance) - .minus(transaction.amount) - .minus(transaction.fee), - ) - }) - - it('should not be ok', () => { - handler.apply = jest.fn() - - transaction.senderPublicKey = 'a'.repeat(66) - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.applyTransactionToSender(wallet, transaction) - - expect(wallet.balance).toEqual(new Bignum(initialBalance)) - }) - - it('should not fail due to case mismatch', () => { - handler.apply = jest.fn() - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase() - wallet.publicKey = wallet.publicKey.toLowerCase() - - handler.applyTransactionToSender(wallet, transaction) - - expect(wallet.balance).toEqual( - new Bignum(initialBalance) - .minus(transaction.amount) - .minus(transaction.fee), - ) - }) - }) - - describe('revertTransactionForSender', () => { - it('should be a function', () => { - expect(handler.revertTransactionForSender).toBeFunction() - }) - - it('should be ok', () => { - handler.revert = jest.fn() - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.revertTransactionForSender(wallet, transaction) - - expect(wallet.balance).toEqual( - new Bignum(initialBalance) - .plus(transaction.amount) - .plus(transaction.fee), - ) - }) - - it('should not be ok', () => { - handler.revert = jest.fn() - - transaction.senderPublicKey = 'a'.repeat(66) - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.revertTransactionForSender(wallet, transaction) - - expect(wallet.balance).toEqual(new Bignum(initialBalance)) - }) - - it('should not fail due to case mismatch', () => { - handler.revert = jest.fn() - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase() - wallet.publicKey = wallet.publicKey.toLowerCase() - - handler.revertTransactionForSender(wallet, transaction) - - expect(wallet.balance).toEqual( - new Bignum(initialBalance) - .plus(transaction.amount) - .plus(transaction.fee), - ) - }) - }) - - describe('applyTransactionToRecipient', () => { - it('should be a function', () => { - expect(handler.applyTransactionToRecipient).toBeFunction() - }) - - it('should be ok', () => { - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.applyTransactionToRecipient(wallet, transaction) - - expect(wallet.balance).toEqual( - new Bignum(initialBalance).plus(transaction.amount), - ) - }) - - it('should not be ok', () => { - transaction.recipientId = 'invalid-recipientId' - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.applyTransactionToRecipient(wallet, transaction) - - expect(wallet.balance).toEqual(new Bignum(initialBalance)) - }) - }) - - describe('revertTransactionForRecipient', () => { - it('should be a function', () => { - expect(handler.revertTransactionForRecipient).toBeFunction() - }) - - it('should be ok', () => { - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.revertTransactionForRecipient(wallet, transaction) - - expect(wallet.balance).toEqual( - new Bignum(initialBalance - transaction.amount), - ) - }) - - it('should not be ok', () => { - transaction.recipientId = 'invalid-recipientId' - - const initialBalance = 1000 * ARKTOSHI - wallet.balance = new Bignum(initialBalance) - - handler.revertTransactionForRecipient(wallet, transaction) - - expect(wallet.balance).toEqual(new Bignum(initialBalance)) - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/handler.test.ts b/packages/crypto/__tests__/handlers/transactions/handler.test.ts new file mode 100644 index 0000000000..102d5d43f3 --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/handler.test.ts @@ -0,0 +1,262 @@ +import "jest-extended"; + +import { ARKTOSHI } from "../../../src/constants"; +import { DelegateRegistrationHandler } from "../../../src/handlers/transactions/delegate-registration"; +import { DelegateResignationHandler } from "../../../src/handlers/transactions/delegate-resignation"; +import { Handler } from "../../../src/handlers/transactions/handler"; +import { SecondSignatureHandler } from "../../../src/handlers/transactions/second-signature"; +import { TransferHandler } from "../../../src/handlers/transactions/transfer"; +import { VoteHandler } from "../../../src/handlers/transactions/vote"; +import { configManager } from "../../../src/managers"; +import { Bignum } from "../../../src/utils/bignum"; + +let wallet; +let transaction; +let transactionWithSecondSignature; +let errors; + +class FakeHandler extends Handler { + // tslint:disable-next-line:no-shadowed-variable + public apply(wallet: any, transaction: any) { + throw new Error("Method not implemented."); + } + + // tslint:disable-next-line:no-shadowed-variable + public revert(wallet: any, transaction: any) { + throw new Error("Method not implemented."); + } +} + +beforeEach(() => { + wallet = { + address: "D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F", + balance: new Bignum(4527654310), + publicKey: "02a47a2f594635737d2ce9898680812ff7fa6aaa64ddea1360474c110e9985a087", + }; + + transaction = { + id: "65a4f09a3a19d212a65d27de05d1ae7e0c461e088a35499996667f98d2a3897c", + signature: + "304402206974568da7c363155decbc20ddc17746a2e7e663901c426f5a41411374cc6d18022052f4353ec93227713f9907f2bb2549e6bc42584b736aa5f9ff36e2c239154648", + timestamp: 54836734, + type: 0, + fee: new Bignum(10000000), + senderPublicKey: "02a47a2f594635737d2ce9898680812ff7fa6aaa64ddea1360474c110e9985a087", + amount: new Bignum(10000000), + recipientId: "D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F", + }; + + transactionWithSecondSignature = { + id: "e3b29bba60d5f1f2aad2087dea44644f166b00ae3db1a16a99b622dc4f3900f8", + signature: + "304402206974568da7c363155decbc20ddc17746a2e7e663901c426f5a41411374cc6d18022052f4353ec93227713f9907f2bb2549e6bc42584b736aa5f9ff36e2c239154648", + signSignature: + "304402202d0ae57c6a0afb225443b56c6e049cb08df48b5813362f7e11574b96f225738f0220055b5a941cc70100404a7788c57b37e2e806acf58c4284c567dc53477f546540", + timestamp: 54836734, + type: 0, + fee: new Bignum(10000000), + senderPublicKey: "02a47a2f594635737d2ce9898680812ff7fa6aaa64ddea1360474c110e9985a087", + amount: new Bignum(10000000), + recipientId: "D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F", + }; + + errors = []; +}); + +describe("Specific handler - fake handler tests", () => { + const handler = new FakeHandler(); + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, errors)).toBeTrue(); + expect(errors).toHaveLength(0); + }); + + it("should be true even with publicKey case mismatch", () => { + transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase(); + wallet.publicKey = wallet.publicKey.toLowerCase(); + + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it("should be true if the transaction has a second signature but wallet does not, when ignoreInvalidSecondSignatureField=true", () => { + configManager.getMilestone().ignoreInvalidSecondSignatureField = true; + + expect(handler.canApply(wallet, transactionWithSecondSignature, errors)).toBeTrue(); + }); + }); +}); + +describe.each([ + ["Transfer handler", TransferHandler], + ["Vote handler", VoteHandler], + ["Delegate registration handler", DelegateRegistrationHandler], + ["Delegate resignation handler", DelegateResignationHandler], + ["Second signature handler", SecondSignatureHandler], + ["Fake handler", FakeHandler], +])("Commmon handler tests - %s", (handlerDesc, TransactionHandler) => { + const handler = new TransactionHandler(); + describe("canApply", () => { + it("should be false if wallet publicKey does not match tx senderPublicKey", () => { + transaction.senderPublicKey = "a".repeat(66); + const result = handler.canApply(wallet, transaction, errors); + + expect(result).toBeFalse(); + expect(errors).toContain('wallet "publicKey" does not match transaction "senderPublicKey"'); + }); + + it("should be false if the transaction has a second signature but wallet does not", () => { + delete configManager.getMilestone().ignoreInvalidSecondSignatureField; + + expect(handler.canApply(wallet, transactionWithSecondSignature, errors)).toBeFalse(); + expect(errors).toContain("Invalid second-signature field"); + }); + + it("should be false if the wallet has a second public key but the transaction second signature does not match", () => { + transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase(); + wallet.secondPublicKey = "invalid-public-key"; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain( + handlerDesc === "Second signature handler" + ? "Wallet already has a second signature" + : "Failed to verify second-signature", + ); + }); + + it("should be false if the validation fails", () => { + delete transaction.senderPublicKey; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain('child "senderPublicKey" fails because ["senderPublicKey" is required]'); + }); + + it("should be false if wallet has not enough balance", () => { + wallet.balance = transaction.amount.plus(transaction.fee).minus(1); // 1 arktoshi short + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); + + describe("applyTransactionToSender", () => { + it("should be ok", () => { + handler.apply = jest.fn(); + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance).minus(transaction.amount).minus(transaction.fee)); + }); + + it("should not be ok", () => { + handler.apply = jest.fn(); + + transaction.senderPublicKey = "a".repeat(66); + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance)); + }); + + it("should not fail due to case mismatch", () => { + handler.apply = jest.fn(); + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase(); + wallet.publicKey = wallet.publicKey.toLowerCase(); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance).minus(transaction.amount).minus(transaction.fee)); + }); + }); + + describe("revertTransactionForSender", () => { + it("should be ok", () => { + handler.revert = jest.fn(); + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.revertTransactionForSender(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance).plus(transaction.amount).plus(transaction.fee)); + }); + + it("should not be ok", () => { + handler.revert = jest.fn(); + + transaction.senderPublicKey = "a".repeat(66); + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.revertTransactionForSender(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance)); + }); + + it("should not fail due to case mismatch", () => { + handler.revert = jest.fn(); + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + transaction.senderPublicKey = transaction.senderPublicKey.toUpperCase(); + wallet.publicKey = wallet.publicKey.toLowerCase(); + + handler.revertTransactionForSender(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance).plus(transaction.amount).plus(transaction.fee)); + }); + }); + + describe("applyTransactionToRecipient", () => { + it("should be ok", () => { + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.applyTransactionToRecipient(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance).plus(transaction.amount)); + }); + + it("should not be ok", () => { + transaction.recipientId = "invalid-recipientId"; + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.applyTransactionToRecipient(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance)); + }); + }); + + describe("revertTransactionForRecipient", () => { + it("should be ok", () => { + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.revertTransactionForRecipient(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance - transaction.amount)); + }); + + it("should not be ok", () => { + transaction.recipientId = "invalid-recipientId"; + + const initialBalance = 1000 * ARKTOSHI; + wallet.balance = new Bignum(initialBalance); + + handler.revertTransactionForRecipient(wallet, transaction); + + expect(wallet.balance).toEqual(new Bignum(initialBalance)); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/ipfs.test.js b/packages/crypto/__tests__/handlers/transactions/ipfs.test.js deleted file mode 100644 index 96db5ff75b..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/ipfs.test.js +++ /dev/null @@ -1,43 +0,0 @@ -const handler = require('../../../lib/handlers/transactions/ipfs') - -let wallet -let transaction - -beforeEach(() => { - wallet = require('./__fixtures__/wallet') - transaction = require('./__fixtures__/transaction') -}) - -describe('IpfsHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('IpfsHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false', () => { - transaction.senderPublicKey = 'a'.repeat(66) - - expect(handler.canApply(wallet, transaction, [])).toBeFalse() - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/ipfs.test.ts b/packages/crypto/__tests__/handlers/transactions/ipfs.test.ts new file mode 100644 index 0000000000..66747ef8f2 --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/ipfs.test.ts @@ -0,0 +1,40 @@ +import "jest-extended"; + +import { IpfsHandler } from "../../../src/handlers/transactions/ipfs"; +import { Bignum } from "../../../src/utils/bignum"; +import { transaction as originalTransaction } from "./__fixtures__/transaction"; +import { wallet as originalWallet } from "./__fixtures__/wallet"; + +const handler = new IpfsHandler(); + +let wallet; +let transaction; +let errors; + +beforeEach(() => { + wallet = originalWallet; + transaction = originalTransaction; + + errors = []; +}); + +describe("IpfsHandler", () => { + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it("should be false", () => { + transaction.senderPublicKey = ("a" as any).repeat(66); + + expect(handler.canApply(wallet, transaction, [])).toBeFalse(); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/multi-payment.test.js b/packages/crypto/__tests__/handlers/transactions/multi-payment.test.js deleted file mode 100644 index 9d05491225..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/multi-payment.test.js +++ /dev/null @@ -1,91 +0,0 @@ -/* eslint-disable */ - -const Bignum = require('../../../lib/utils/bignum') -const handler = require('../../../lib/handlers/transactions/multi-payment') - -let wallet -let transaction - -beforeEach(() => { - wallet = require('./__fixtures__/wallet') - - transaction = { - version: 1, - id: '943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4', - blockid: '11233167632577333611', - type: 7, - timestamp: 36482198, - amount: new Bignum(100000000), - fee: new Bignum(10000000), - senderId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - recipientId: 'DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh', - senderPublicKey: - '034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126', - signature: - '304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b', // eslint-disable-line max-len - asset: { - payments: [ - { - amount: new Bignum(10), - }, - { - amount: new Bignum(20), - }, - { - amount: new Bignum(30), - }, - { - amount: new Bignum(40), - }, - { - amount: new Bignum(50), - }, - ], - }, - } -}) - -describe('MultiPaymentHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('MultiPaymentHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - const amount = transaction.asset.payments.reduce( - (a, p) => a.plus(p.amount), - Bignum.ZERO, - ) - - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false if wallet has insufficient balance', () => { - const amount = transaction.asset.payments.reduce( - (a, p) => a.plus(p.amount), - Bignum.ZERO, - ) - wallet.balance = Bignum.ZERO - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Insufficient balance in the wallet') - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/multi-payment.test.ts b/packages/crypto/__tests__/handlers/transactions/multi-payment.test.ts new file mode 100644 index 0000000000..116f0fc31d --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/multi-payment.test.ts @@ -0,0 +1,80 @@ +import "jest-extended"; + +import { MultiPaymentHandler } from "../../../src/handlers/transactions/multi-payment"; +import { Bignum } from "../../../src/utils/bignum"; +import { transaction as originalTransaction } from "./__fixtures__/transaction"; +import { wallet as originalWallet } from "./__fixtures__/wallet"; + +const handler = new MultiPaymentHandler(); + +let wallet; +let transaction; +let errors; + +beforeEach(() => { + wallet = originalWallet; + + transaction = { + version: 1, + id: "943c220691e711c39c79d437ce185748a0018940e1a4144293af9d05627d2eb4", + blockid: "11233167632577333611", + type: 7, + timestamp: 36482198, + amount: new Bignum(0), + fee: new Bignum(10000000), + senderId: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + recipientId: "DTRdbaUW3RQQSL5By4G43JVaeHiqfVp9oh", + senderPublicKey: "034da006f958beba78ec54443df4a3f52237253f7ae8cbdb17dccf3feaa57f3126", + signature: + "304402205881204c6e515965098099b0e20a7bf104cd1bad6cfe8efd1641729fcbfdbf1502203cfa3bd9efb2ad250e2709aaf719ac0db04cb85d27a96bc8149aeaab224de82b", + asset: { + payments: [ + { + amount: new Bignum(10), + recipientId: "a", + }, + { + amount: new Bignum(20), + recipientId: "b", + }, + { + amount: new Bignum(30), + recipientId: "c", + }, + { + amount: new Bignum(40), + recipientId: "d", + }, + { + amount: new Bignum(50), + recipientId: "e", + }, + ], + }, + }; + + errors = []; +}); + +describe.skip("MultiPaymentHandler", () => { + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + expect(errors).toBeEmpty(); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + + it("should be false if wallet has insufficient funds send all payouts", () => { + wallet.balance = new Bignum(10000149); + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet to transfer all payments"); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/multi-signature.test.js b/packages/crypto/__tests__/handlers/transactions/multi-signature.test.js deleted file mode 100644 index d36199198b..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/multi-signature.test.js +++ /dev/null @@ -1,159 +0,0 @@ -const Bignum = require('../../../lib/utils/bignum') -const handler = require('../../../lib/handlers/transactions/multi-signature') -const WalletModel = require('../../../lib/models/wallet') - -let wallet -let transaction -let multisignatureTest - -beforeEach(() => { - wallet = new WalletModel('D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7') - wallet.balance = new Bignum(100390000000) - wallet.publicKey = - '026f717e50bf3dbb9d8593996df5435ba22217410fc7a132f3d2c942a01a00a202' - wallet.secondPublicKey = - '0380728436880a0a11eadf608c4d4e7f793719e044ee5151074a5f2d5d43cb9066' - wallet.multisignature = multisignatureTest - - transaction = { - version: 1, - id: 'e22ddd7385b42c00f79b9c6ecd253333ddef6e0bf955341ace2e63dad1f4bd70', - type: 4, - timestamp: 48059808, - amount: Bignum.ZERO, - fee: new Bignum(8000000000), - recipientId: 'DGN48KSVFx88chiSu7JbqkAXstqtM1uLJQ', - senderPublicKey: - '026f717e50bf3dbb9d8593996df5435ba22217410fc7a132f3d2c942a01a00a202', - signature: - '30450221008baddfae37be66d725e22d9e93c10334d859558f2aef38762803178dbb39354f022025a9bdc7fc4c86d3f67cd1d012dbee3d5691ab3188b5457fdeae82fdd5995767', // eslint-disable-line max-len - signSignature: - '3045022100eb9844a235309309f805235ec40336260cc3dc2c3cbb4cb687dd55b32d8f405402202a98ca5b3b2ad31cec0ed01d9c085a828dd5c07c3893858d4c127fce57d6d410', // eslint-disable-line max-len - signatures: [ - '3045022100f073a3f59ed753f98734462dbe7c9082bb7cb9d46348c671708c93df2fdd2a7602206dc19039d3561f8d1226755dd3b0ca25f359347729eff066eaf3cc3b5c18bc59', - '3045022100c560d6d8504b6761245f7bb3e3b723380b50c380ae30c9544c781f3a9b1359a702206b50506ba6c0a39bed7bec226b55bf9ece979716eb95e2a757f025d3592fde17', - '30440220344345bcb9754ab242dc27bd3d705e5213597914183818005ff1f2e91466f17a0220474c27d05cd5f121c3cad0295e6fc9f8a8cdfa03647e70eb3783e4c1139dde04', - '3045022100998e29255a8f1c140aa41d93ec43271fd8d0e5b9c18df366e3c7b59cc0c293d902205292dd36e9db18f072f00559267361b9426ab26bff2ee613ec0c3627317b4dab', - '3044022007379b5643032d9e9d3395298776be041b2a85a211be2d7b6a5855cf030ae0ca02203c5d3da458034483fdef9f43ee4db4428999cfeb8893795f695e663407238090', - '3044022060461195aeb4386dfab1e3618cdec48f4b988ea394461962379cdbfe8f17b7110220415522adf0239bff7e44e6c0cc8d57211d9d9fe745a6ba2911a81586d5dfc5dc', - '3044022057355ab8ad9502745895a649aede98dfe829c46465eda57438720baeaa6ece5c02200ed3c2eb019579b243380ac066d691f6f27012dc6b93a1403e1a49c992cc0812', - '3044022010cf1079e46cbac198e49f763795095c3a1f33b772cf3e6f335c313f786eb0570220450a110a813cc5453265f0e97850794b0f9d5c6efd6d9ea08009df3d4b9f2299', - '3044022000f35223b23f03413f17538b157e62388c0b150fc046fdcc35792a48d694499402205c2e494ca74565e7841cd6034228cd3d9b57bb832f0da5834991bf92b415c0b8', - '304402206b69cbd52335fca4a510fa1dcb1417617ad1128aa06dcf543d1f11890e46fdef022012d3054adc0a924429d34091910bf82c0abc757f39cfc0887c7e4d9b35f21ad6', - '30440220490bf3e963aa500404e5d559dc06bb1ce176ddbab92f46add87c17c19c3781c90220775f0a3f65d95e3e268eb1f2f2fc86044995e7ebdd1f51f99a973fd00c952d57', - '30450221008795d2e1a454c2cbda92d5fcb7e539372cefbe9f8a181d658abcbd2ba18da8e702207b395488d31f037dc158c12799885edb94f36b1437b2bda79d9074c9a82aa686', - '3045022100cf706e93a9984a958dba6e17287d17febae005d277afc77890e0a3912ee7ed3d02206618718ee68cae209c42a801b7b295fa2564878838712a1b22beaa3637b57c58', - '30440220743aba2fdb663dda73b64ada17812a98adf26f2419c6ab2cfba8f66666527a91022036ade1b37b72079eb43b51a8fe5e31da2e42f7b6d0b437f8a693cc276b9123b8', - '304402206902fc8f519670a7768ad13f1b2d69373aa14c89b70020f83273d6bb0cfd89d102207de30b9ac0c17ff11e364b72c41f1cd8d4e6dccbe28399cb78e96eea32deef12', - ], - asset: { - multisignature: { - min: 15, - lifetime: 72, - keysgroup: [ - '+0217e9e2a1aca300a7011acaadf60af94252875568373546895f227c050d48aac5', - '+02b3b3233c171a122f88c1dbe44539dfefb36530ca3ec04163aef9f448a1823795', - '+03a3013f144160e1964b97e78117571e571a631f0042efcd0de309c7159c7886c8', - '+02fb475ef881b8f56e00407095a87319934c34467db11d3230e54d9328c6cddbe5', - '+03ab9cc2c5364f1676a94b2b5ff3fbc3705e8ce94c6e7e4712890905addf765a3f', - '+024be9e731a63f86b56e5f48dbdfb3443a0628c82ea308ee4c88d3fcbe3183eb9d', - '+0371b8fd17fb1f31095e8a1586bbe29e205904c9100de07c84090a423929a20dcf', - '+02cc09a7c5560db72e312f58a9f5ca4b60b5109efc5ce9dd58a116fa16516bb493', - '+02145fbe9309ebb1547eb332686efb4d8b6e2aaa1fe636663bf6ab1000e5cf72d3', - '+0274966781d4d23f8991530b33bdb051905cde809ae52e58e45cfd1bc8f6f70cc6', - '+0347288f8db9be069415c6c97fd4825867f4bd9b9f78557e8aa1244890beb85001', - '+035359097c405e90516be78104de0ca17001da2826397e0937b8b1e8e613fff352', - '+021aa343234514f8fdaf5e668bdc822a42805382567fa2ca9a5e06e92065f5658a', - '+033a28a0a9592952336918ddded08dd55503b82852fe67df1d358f07a575910844', - '+02747bec17b02cc09345c8c0dbeb09bff2db74d1c355135e10af0001eb1dc00265', - ], - }, - }, - confirmations: 1091040, - } - - multisignatureTest = { - min: 15, - lifetime: 72, - keysgroup: [ - '034a7aca6841cfbdc688f09d55345f21c7ffbd1844693fa68d607fc94f729cbbea', - '02fd6743ddfdc7c5bac24145e449c2e4f2d569b5297dd7bf088c3bc219f582a2f0', - '02f9c51812f4be127b9f1f21cb4e146eca6aecc85739a243db0f1064981deda216', - '0214d60ca95cd87a097ed6e6e42281acb68ae1815c8f494b8ff18d24dc9e072171', - '02a14634e04e80b05acd56bc361af98498d76fbf5233f8d62773ceaa07172ddaa6', - '021a9ba0e72f02b8fa7bad386582ec1d6c05b7664c892bf2a86035a85350f37203', - '02e3ba373c6c352316b748e75358ead36504b0ef5139d215fb6a83a330c4eb60d5', - '0309039bfa18d6fd790edb79438783b27fbcab06040a2fdaa66fb81ad53ca8db5f', - '0393d12aff5962fa9065487959719a81c5d991e7c48a823039acd9254c2b673841', - '03d3265264f06fe1dd752798403a73e537eb461fc729c83a84b579e8434adfe7c4', - '02acfa496a6c12cb9acc3219993b17c62d19f4b570996c12a37d6e89eaa9716859', - '03136f2101f1767b0d63d9545410bcaf3a941b2b6f06851093f3c679e0d31ca1cd', - '02e6ec3941be86177bf0b998589c07da1b73e990466fdaee347c972c10f61b3797', - '037dcd05d921a9f2ddd11960fec2ea9904fc55cad030549a6c5f5a41b2e35e56d2', - '03206f7ae26f14cffb62b8c28b5e632952cdeb84b7c74ac0c2198b08bd84ee4f23', - ], - } -}) - -describe('MultiSignatureHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('MultiSignatureHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - delete wallet.multisignature - - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false if failure to verify signatures', () => { - wallet.multisignature = multisignatureTest - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Failed to verify multi-signatures') - }) - - it('should be false if keyCount is less than minimum', () => { - wallet.multisignature = multisignatureTest - wallet.multisignature.min = 20 - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Failed to verify multi-signatures') - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - - it('should be ok', () => { - wallet.multisignature = null - - expect(wallet.multisignature).toBeNull() - - handler.apply(wallet, transaction) - - expect(wallet.multisignature).toEqual(transaction.asset.multisignature) - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - - it('should be ok', () => { - handler.revert(wallet, transaction) - - expect(wallet.multisignature).toBeNull() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/multi-signature.test.ts b/packages/crypto/__tests__/handlers/transactions/multi-signature.test.ts new file mode 100644 index 0000000000..1453b389d6 --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/multi-signature.test.ts @@ -0,0 +1,191 @@ +import "jest-extended"; + +import { crypto } from "../../../src/crypto"; +import { MultiSignatureHandler } from "../../../src/handlers/transactions/multi-signature"; +import { Wallet } from "../../../src/models/wallet"; +import { Bignum } from "../../../src/utils/bignum"; +import { transactionValidator } from "../../../src/validation"; + +const handler = new MultiSignatureHandler(); + +let wallet; +let transaction; +let multisignatureTest; +let errors; + +beforeEach(() => { + wallet = new Wallet("D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7"); + wallet.balance = new Bignum(100390000000); + wallet.publicKey = "026f717e50bf3dbb9d8593996df5435ba22217410fc7a132f3d2c942a01a00a202"; + wallet.secondPublicKey = "0380728436880a0a11eadf608c4d4e7f793719e044ee5151074a5f2d5d43cb9066"; + wallet.multisignature = multisignatureTest; + + transaction = { + version: 1, + id: "e22ddd7385b42c00f79b9c6ecd253333ddef6e0bf955341ace2e63dad1f4bd70", + type: 4, + timestamp: 48059808, + amount: Bignum.ZERO, + fee: new Bignum(8000000000), + recipientId: "DGN48KSVFx88chiSu7JbqkAXstqtM1uLJQ", + senderPublicKey: "026f717e50bf3dbb9d8593996df5435ba22217410fc7a132f3d2c942a01a00a202", + signature: + "30450221008baddfae37be66d725e22d9e93c10334d859558f2aef38762803178dbb39354f022025a9bdc7fc4c86d3f67cd1d012dbee3d5691ab3188b5457fdeae82fdd5995767", + signSignature: + "3045022100eb9844a235309309f805235ec40336260cc3dc2c3cbb4cb687dd55b32d8f405402202a98ca5b3b2ad31cec0ed01d9c085a828dd5c07c3893858d4c127fce57d6d410", + signatures: [ + "3045022100f073a3f59ed753f98734462dbe7c9082bb7cb9d46348c671708c93df2fdd2a7602206dc19039d3561f8d1226755dd3b0ca25f359347729eff066eaf3cc3b5c18bc59", + "3045022100c560d6d8504b6761245f7bb3e3b723380b50c380ae30c9544c781f3a9b1359a702206b50506ba6c0a39bed7bec226b55bf9ece979716eb95e2a757f025d3592fde17", + "30440220344345bcb9754ab242dc27bd3d705e5213597914183818005ff1f2e91466f17a0220474c27d05cd5f121c3cad0295e6fc9f8a8cdfa03647e70eb3783e4c1139dde04", + "3045022100998e29255a8f1c140aa41d93ec43271fd8d0e5b9c18df366e3c7b59cc0c293d902205292dd36e9db18f072f00559267361b9426ab26bff2ee613ec0c3627317b4dab", + "3044022007379b5643032d9e9d3395298776be041b2a85a211be2d7b6a5855cf030ae0ca02203c5d3da458034483fdef9f43ee4db4428999cfeb8893795f695e663407238090", + "3044022060461195aeb4386dfab1e3618cdec48f4b988ea394461962379cdbfe8f17b7110220415522adf0239bff7e44e6c0cc8d57211d9d9fe745a6ba2911a81586d5dfc5dc", + "3044022057355ab8ad9502745895a649aede98dfe829c46465eda57438720baeaa6ece5c02200ed3c2eb019579b243380ac066d691f6f27012dc6b93a1403e1a49c992cc0812", + "3044022010cf1079e46cbac198e49f763795095c3a1f33b772cf3e6f335c313f786eb0570220450a110a813cc5453265f0e97850794b0f9d5c6efd6d9ea08009df3d4b9f2299", + "3044022000f35223b23f03413f17538b157e62388c0b150fc046fdcc35792a48d694499402205c2e494ca74565e7841cd6034228cd3d9b57bb832f0da5834991bf92b415c0b8", + "304402206b69cbd52335fca4a510fa1dcb1417617ad1128aa06dcf543d1f11890e46fdef022012d3054adc0a924429d34091910bf82c0abc757f39cfc0887c7e4d9b35f21ad6", + "30440220490bf3e963aa500404e5d559dc06bb1ce176ddbab92f46add87c17c19c3781c90220775f0a3f65d95e3e268eb1f2f2fc86044995e7ebdd1f51f99a973fd00c952d57", + "30450221008795d2e1a454c2cbda92d5fcb7e539372cefbe9f8a181d658abcbd2ba18da8e702207b395488d31f037dc158c12799885edb94f36b1437b2bda79d9074c9a82aa686", + "3045022100cf706e93a9984a958dba6e17287d17febae005d277afc77890e0a3912ee7ed3d02206618718ee68cae209c42a801b7b295fa2564878838712a1b22beaa3637b57c58", + "30440220743aba2fdb663dda73b64ada17812a98adf26f2419c6ab2cfba8f66666527a91022036ade1b37b72079eb43b51a8fe5e31da2e42f7b6d0b437f8a693cc276b9123b8", + "304402206902fc8f519670a7768ad13f1b2d69373aa14c89b70020f83273d6bb0cfd89d102207de30b9ac0c17ff11e364b72c41f1cd8d4e6dccbe28399cb78e96eea32deef12", + ], + asset: { + multisignature: { + min: 15, + lifetime: 72, + keysgroup: [ + "+0217e9e2a1aca300a7011acaadf60af94252875568373546895f227c050d48aac5", + "+02b3b3233c171a122f88c1dbe44539dfefb36530ca3ec04163aef9f448a1823795", + "+03a3013f144160e1964b97e78117571e571a631f0042efcd0de309c7159c7886c8", + "+02fb475ef881b8f56e00407095a87319934c34467db11d3230e54d9328c6cddbe5", + "+03ab9cc2c5364f1676a94b2b5ff3fbc3705e8ce94c6e7e4712890905addf765a3f", + "+024be9e731a63f86b56e5f48dbdfb3443a0628c82ea308ee4c88d3fcbe3183eb9d", + "+0371b8fd17fb1f31095e8a1586bbe29e205904c9100de07c84090a423929a20dcf", + "+02cc09a7c5560db72e312f58a9f5ca4b60b5109efc5ce9dd58a116fa16516bb493", + "+02145fbe9309ebb1547eb332686efb4d8b6e2aaa1fe636663bf6ab1000e5cf72d3", + "+0274966781d4d23f8991530b33bdb051905cde809ae52e58e45cfd1bc8f6f70cc6", + "+0347288f8db9be069415c6c97fd4825867f4bd9b9f78557e8aa1244890beb85001", + "+035359097c405e90516be78104de0ca17001da2826397e0937b8b1e8e613fff352", + "+021aa343234514f8fdaf5e668bdc822a42805382567fa2ca9a5e06e92065f5658a", + "+033a28a0a9592952336918ddded08dd55503b82852fe67df1d358f07a575910844", + "+02747bec17b02cc09345c8c0dbeb09bff2db74d1c355135e10af0001eb1dc00265", + ], + }, + }, + confirmations: 1091040, + }; + + multisignatureTest = { + min: 15, + lifetime: 72, + keysgroup: [ + "034a7aca6841cfbdc688f09d55345f21c7ffbd1844693fa68d607fc94f729cbbea", + "02fd6743ddfdc7c5bac24145e449c2e4f2d569b5297dd7bf088c3bc219f582a2f0", + "02f9c51812f4be127b9f1f21cb4e146eca6aecc85739a243db0f1064981deda216", + "0214d60ca95cd87a097ed6e6e42281acb68ae1815c8f494b8ff18d24dc9e072171", + "02a14634e04e80b05acd56bc361af98498d76fbf5233f8d62773ceaa07172ddaa6", + "021a9ba0e72f02b8fa7bad386582ec1d6c05b7664c892bf2a86035a85350f37203", + "02e3ba373c6c352316b748e75358ead36504b0ef5139d215fb6a83a330c4eb60d5", + "0309039bfa18d6fd790edb79438783b27fbcab06040a2fdaa66fb81ad53ca8db5f", + "0393d12aff5962fa9065487959719a81c5d991e7c48a823039acd9254c2b673841", + "03d3265264f06fe1dd752798403a73e537eb461fc729c83a84b579e8434adfe7c4", + "02acfa496a6c12cb9acc3219993b17c62d19f4b570996c12a37d6e89eaa9716859", + "03136f2101f1767b0d63d9545410bcaf3a941b2b6f06851093f3c679e0d31ca1cd", + "02e6ec3941be86177bf0b998589c07da1b73e990466fdaee347c972c10f61b3797", + "037dcd05d921a9f2ddd11960fec2ea9904fc55cad030549a6c5f5a41b2e35e56d2", + "03206f7ae26f14cffb62b8c28b5e632952cdeb84b7c74ac0c2198b08bd84ee4f23", + ], + }; + + errors = []; +}); + +describe("MultiSignatureHandler", () => { + describe("canApply", () => { + it("should be true", () => { + delete wallet.multisignature; + + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it.skip("should be false if the wallet already has multisignatures", () => { + wallet.verifySignatures = jest.fn(() => true); + wallet.multisignature = multisignatureTest; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toEqual(["Wallet is already a multi-signature wallet"]); + }); + + it.skip("should be false if failure to verify signatures", () => { + wallet.verifySignatures = jest.fn(() => false); + wallet.multisignature = multisignatureTest; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toEqual(["Failed to verify multi-signatures"]); + }); + + it("should be false if failure to verify signatures in asset", () => { + wallet.verifySignatures = jest.fn(() => false); + delete wallet.multisignature; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toEqual(["Failed to verify multi-signatures"]); + }); + + it("should be false if the number of keys is less than minimum", () => { + delete wallet.multisignature; + + wallet.verifySignatures = jest.fn(() => true); + crypto.verifySecondSignature = jest.fn(() => true); + transactionValidator.validate = jest.fn(() => ({ fails: false })); + + transaction.asset.multisignature.keysgroup.splice(0, 5); + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toEqual(["Specified key count does not meet minimum key count"]); + }); + + it("should be false if the number of keys does not equal the signature count", () => { + delete wallet.multisignature; + + wallet.verifySignatures = jest.fn(() => true); + crypto.verifySecondSignature = jest.fn(() => true); + transactionValidator.validate = jest.fn(() => ({ fails: false })); + + transaction.signatures.splice(0, 5); + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toEqual(["Specified key count does not equal signature count"]); + }); + + it("should be false if wallet has insufficient funds", () => { + delete wallet.multisignature; + + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); + + describe("apply", () => { + it("should be ok", () => { + wallet.multisignature = null; + + expect(wallet.multisignature).toBeNull(); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.multisignature).toEqual(transaction.asset.multisignature); + }); + }); + + describe("revert", () => { + it("should be ok", () => { + handler.revertTransactionForSender(wallet, transaction); + + expect(wallet.multisignature).toBeNull(); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/second-signature.test.js b/packages/crypto/__tests__/handlers/transactions/second-signature.test.js deleted file mode 100644 index b4fcf91bdf..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/second-signature.test.js +++ /dev/null @@ -1,116 +0,0 @@ -const Bignum = require('../../../lib/utils/bignum') -const handler = require('../../../lib/handlers/transactions/second-signature') - -let wallet -let transaction - -beforeEach(() => { - wallet = { - address: 'DSD9Wi2rfqzDb3REUB5MELQGrsUAjY67gj', - balance: new Bignum('6453530000000'), - publicKey: - '03cba4fd60f856ad034ee0a9146432757ae35956b640c26fb6674061924b05a5c9', - } - - transaction = { - version: 1, - network: 30, - type: 1, - timestamp: 53995738, - senderPublicKey: - '03cba4fd60f856ad034ee0a9146432757ae35956b640c26fb6674061924b05a5c9', - fee: new Bignum(500000000), - asset: { - signature: { - publicKey: - '02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8', - }, - }, - signature: - '3044022064e7abe87c186b201eaeeb9587097432816c94b52b85520a70da1d78b93456aa0220205e263a278c64771d46038f116c37dc16c86e73664e7e829951d7c5544c6d3e', - amount: Bignum.ZERO, - recipientId: 'DSD9Wi2rfqzDb3REUB5MELQGrsUAjY67gj', - id: 'e5a4cf622a24d459987f093e14a14c6b0492834358f86099afe1a2d14457cf31', - } -}) - -describe('SecondSignatureHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('SecondSignatureHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - const errors = [] - expect(handler.canApply(wallet, transaction, errors)).toBeTrue() - - expect(errors).toBeEmpty() - }) - - it('should be false if wallet already has a second signature', () => { - wallet.secondPublicKey = - '02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8' - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Wallet already has a second signature') - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - - it('should apply second signature registration', () => { - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - - handler.apply(wallet, transaction) - - expect(wallet.secondPublicKey).toBe( - '02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8', - ) - }) - - it('should be invalid to apply a second signature registration twice', () => { - const errors = [] - expect(handler.canApply(wallet, transaction, errors)).toBeTrue() - expect(errors).toBeEmpty() - - handler.apply(wallet, transaction) - - expect(wallet.secondPublicKey).toBe( - '02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8', - ) - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Wallet already has a second signature') - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - - it('should be ok', () => { - expect(wallet.secondPublicKey).toBeUndefined() - - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - - handler.apply(wallet, transaction) - - expect(wallet.secondPublicKey).toBe( - '02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8', - ) - - handler.revert(wallet, transaction) - - expect(wallet.secondPublicKey).toBeUndefined() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/second-signature.test.ts b/packages/crypto/__tests__/handlers/transactions/second-signature.test.ts new file mode 100644 index 0000000000..5f0a02a9b2 --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/second-signature.test.ts @@ -0,0 +1,101 @@ +import "jest-extended"; + +import { SecondSignatureHandler } from "../../../src/handlers/transactions/second-signature"; +import { Bignum } from "../../../src/utils/bignum"; + +const handler = new SecondSignatureHandler(); + +let wallet; +let transaction; +let errors; + +beforeEach(() => { + wallet = { + address: "DSD9Wi2rfqzDb3REUB5MELQGrsUAjY67gj", + balance: new Bignum("6453530000000"), + publicKey: "03cba4fd60f856ad034ee0a9146432757ae35956b640c26fb6674061924b05a5c9", + }; + + transaction = { + version: 1, + network: 30, + type: 1, + timestamp: 53995738, + senderPublicKey: "03cba4fd60f856ad034ee0a9146432757ae35956b640c26fb6674061924b05a5c9", + fee: new Bignum(500000000), + asset: { + signature: { + publicKey: "02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8", + }, + }, + signature: + "3044022064e7abe87c186b201eaeeb9587097432816c94b52b85520a70da1d78b93456aa0220205e263a278c64771d46038f116c37dc16c86e73664e7e829951d7c5544c6d3e", + amount: Bignum.ZERO, + recipientId: "DSD9Wi2rfqzDb3REUB5MELQGrsUAjY67gj", + id: "e5a4cf622a24d459987f093e14a14c6b0492834358f86099afe1a2d14457cf31", + }; + + errors = []; +}); + +describe("SecondSignatureHandler", () => { + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, errors)).toBeTrue(); + + expect(errors).toBeEmpty(); + }); + + it("should be false if wallet already has a second signature", () => { + wallet.secondPublicKey = "02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8"; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Wallet already has a second signature"); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); + + describe("apply", () => { + it("should apply second signature registration", () => { + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.secondPublicKey).toBe("02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8"); + }); + + it("should be invalid to apply a second signature registration twice", () => { + expect(handler.canApply(wallet, transaction, errors)).toBeTrue(); + expect(errors).toBeEmpty(); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.secondPublicKey).toBe("02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8"); + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Wallet already has a second signature"); + }); + }); + + describe("revert", () => { + it("should be ok", () => { + expect(wallet.secondPublicKey).toBeUndefined(); + + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + + handler.applyTransactionToSender(wallet, transaction); + + expect(wallet.secondPublicKey).toBe("02d5cfcbc4920d041d2a54b29e1f69173536796fd50f62af0f88ad6adc6df07cb8"); + + handler.revertTransactionForSender(wallet, transaction); + + expect(wallet.secondPublicKey).toBeUndefined(); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/timelock-transfer.test.js b/packages/crypto/__tests__/handlers/transactions/timelock-transfer.test.js deleted file mode 100644 index c4fd67c492..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/timelock-transfer.test.js +++ /dev/null @@ -1,43 +0,0 @@ -const handler = require('../../../lib/handlers/transactions/timelock-transfer') - -let wallet -let transaction - -beforeEach(() => { - wallet = require('./__fixtures__/wallet') - transaction = require('./__fixtures__/transaction') -}) - -describe('TimelockTransferHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('TimelockTransferHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false', () => { - transaction.senderPublicKey = 'a'.repeat(66) - - expect(handler.canApply(wallet, transaction, [])).toBeFalse() - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/timelock-transfer.test.ts b/packages/crypto/__tests__/handlers/transactions/timelock-transfer.test.ts new file mode 100644 index 0000000000..d35242e8bc --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/timelock-transfer.test.ts @@ -0,0 +1,40 @@ +import "jest-extended"; + +import { TimelockTransferHandler } from "../../../src/handlers/transactions/timelock-transfer"; +import { Bignum } from "../../../src/utils/bignum"; +import { transaction as originalTransaction } from "./__fixtures__/transaction"; +import { wallet as originalWallet } from "./__fixtures__/wallet"; + +const handler = new TimelockTransferHandler(); + +let wallet; +let transaction; +let errors; + +beforeEach(() => { + wallet = originalWallet; + transaction = originalTransaction; + + errors = []; +}); + +describe("TimelockTransferHandler", () => { + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it("should be false", () => { + transaction.senderPublicKey = "a".repeat(66); + + expect(handler.canApply(wallet, transaction, [])).toBeFalse(); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, transaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/transfer.test.js b/packages/crypto/__tests__/handlers/transactions/transfer.test.js deleted file mode 100644 index b9a56a1c0b..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/transfer.test.js +++ /dev/null @@ -1,43 +0,0 @@ -const handler = require('../../../lib/handlers/transactions/transfer') - -let wallet -let transaction - -beforeEach(() => { - wallet = require('./__fixtures__/wallet') - transaction = require('./__fixtures__/transaction') -}) - -describe('TransferHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('TransferHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - - it('should be true', () => { - expect(handler.canApply(wallet, transaction, [])).toBeTrue() - }) - - it('should be false', () => { - transaction.senderPublicKey = 'a'.repeat(66) - - expect(handler.canApply(wallet, transaction, [])).toBeFalse() - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/transfer.test.ts b/packages/crypto/__tests__/handlers/transactions/transfer.test.ts new file mode 100644 index 0000000000..7967ae31ba --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/transfer.test.ts @@ -0,0 +1,33 @@ +import "jest-extended"; + +import { TransferHandler } from "../../../src/handlers/transactions/transfer"; +import { transaction as originalTransaction } from "./__fixtures__/transaction"; +import { wallet as originalWallet } from "./__fixtures__/wallet"; + +const handler = new TransferHandler(); + +let wallet; +let transaction; + +beforeEach(() => { + wallet = originalWallet; + transaction = originalTransaction; +}); + +describe("TransferHandler", () => { + it("should be instantiated", () => { + expect(handler.constructor.name).toBe("TransferHandler"); + }); + + describe("canApply", () => { + it("should be true", () => { + expect(handler.canApply(wallet, transaction, [])).toBeTrue(); + }); + + it("should be false", () => { + transaction.senderPublicKey = "a".repeat(66); + + expect(handler.canApply(wallet, transaction, [])).toBeFalse(); + }); + }); +}); diff --git a/packages/crypto/__tests__/handlers/transactions/vote.test.js b/packages/crypto/__tests__/handlers/transactions/vote.test.js deleted file mode 100644 index 47186e04e1..0000000000 --- a/packages/crypto/__tests__/handlers/transactions/vote.test.js +++ /dev/null @@ -1,119 +0,0 @@ -const Bignum = require('../../../lib/utils/bignum') -const handler = require('../../../lib/handlers/transactions/vote') - -let wallet -let transaction - -beforeEach(() => { - wallet = { - address: 'DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh', - balance: new Bignum('6453530000000'), - publicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - vote: null, - } - - transaction = { - version: 1, - id: '44d6f5ac5fbb6104ee250f6b5cb43401961114263499fd067922f3e2a9cb9d24', - blockid: '5273958469976113749', - type: 3, - timestamp: 36345270, - amount: Bignum.ZERO, - fee: new Bignum(100000000), - senderId: 'DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh', - recipientId: 'DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh', - senderPublicKey: - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - signature: - '304402204da11f2677ea67ad3718520020eb2e2d43b5c83f947490d2b454ce3ec0f1dcba022011a00e3c3febdaf531a404d728b111812647c2f0e33df439c7cbae01dcb702ba', // eslint-disable-line max-len - asset: { - votes: [ - '+0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - ], - }, - confirmations: 19771, - } -}) - -describe('VoteHandler', () => { - it('should be instantiated', () => { - expect(handler.constructor.name).toBe('VoteHandler') - }) - - describe('canApply', () => { - it('should be a function', () => { - expect(handler.canApply).toBeFunction() - }) - it('should be false if wallet has already voted', () => { - wallet.vote = - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0' - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Wallet has already voted') - }) - it('should be false if tx vote-choice does not match wallet vote-choice', () => { - wallet.vote = - 'a310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0' - transaction.asset.votes[0] = - '-0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0' - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain( - 'Wallet vote-choice does not match transaction vote-choice', - ) - }) - it('should be false if unvoting a non-voted wallet', () => { - transaction.asset.votes[0] = - '-0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0' - const errors = [] - - expect(handler.canApply(wallet, transaction, errors)).toBeFalse() - expect(errors).toContain('Wallet has not voted yet') - }) - }) - - describe('apply', () => { - it('should be a function', () => { - expect(handler.apply).toBeFunction() - }) - - it('should be ok', () => { - expect(wallet.vote).toBeNull() - - handler.apply(wallet, transaction) - - expect(wallet.vote).not.toBeNull() - }) - - it('should not be ok', () => { - wallet.vote = - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0' - - expect(wallet.vote).not.toBeNull() - - handler.apply(wallet, transaction) - - expect(wallet.vote).not.toBeNull() - }) - }) - - describe('revert', () => { - it('should be a function', () => { - expect(handler.revert).toBeFunction() - }) - - it('should be ok', () => { - wallet.vote = - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0' - - expect(wallet.vote).not.toBeNull() - - handler.revert(wallet, transaction) - - expect(wallet.vote).toBeNull() - }) - }) -}) diff --git a/packages/crypto/__tests__/handlers/transactions/vote.test.ts b/packages/crypto/__tests__/handlers/transactions/vote.test.ts new file mode 100644 index 0000000000..e8278c6daa --- /dev/null +++ b/packages/crypto/__tests__/handlers/transactions/vote.test.ts @@ -0,0 +1,152 @@ +import "jest-extended"; + +import { VoteHandler } from "../../../src/handlers/transactions/vote"; +import { Bignum } from "../../../src/utils/bignum"; + +const handler = new VoteHandler(); + +let wallet; +let voteTransaction; +let unvoteTransaction; +let errors; + +beforeEach(() => { + wallet = { + address: "DQ7VAW7u171hwDW75R1BqfHbA9yiKRCBSh", + balance: new Bignum("6453530000000"), + publicKey: "02a47a2f594635737d2ce9898680812ff7fa6aaa64ddea1360474c110e9985a087", + vote: null, + }; + + voteTransaction = { + id: "73cbce62d69308ff7e69f1a7836106a16dc59907198aea4bb80d340232e53041", + signature: + "3045022100f53da6eb18ca7954bb7c620ceeaf5cb3433685d173401146aea35ee8e5f5c95002204ea57f573745c8f5c57b256e38397d3e1977bdbfac295128320401c6117bb2f3", + timestamp: 54833694, + type: 3, + fee: new Bignum(100000000), + senderPublicKey: "02a47a2f594635737d2ce9898680812ff7fa6aaa64ddea1360474c110e9985a087", + amount: Bignum.ZERO, + recipientId: "DLvBAvLePTJ9DfDzby5AAkqPqwCVDCT647", + asset: { + votes: ["+02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"], + }, + }; + + unvoteTransaction = { + id: "d714bc0443208f9281ad83f9f3d941628b875c84f65a09601148ce87ca879cb9", + signature: + "3045022100957106a924eb40df6ff530cff80fede0195c30284fdb5671e736c7d0b57696f6022072b0fd80af235d79701e9aea74ef48366ef9f5aecedbb5d502e6392569c059c8", + timestamp: 54833718, + type: 3, + fee: new Bignum(100000000), + senderPublicKey: "02a47a2f594635737d2ce9898680812ff7fa6aaa64ddea1360474c110e9985a087", + amount: Bignum.ZERO, + recipientId: "DLvBAvLePTJ9DfDzby5AAkqPqwCVDCT647", + asset: { + votes: ["-02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"], + }, + }; + + errors = []; +}); + +describe("VoteHandler", () => { + describe("canApply", () => { + it("should be true if the vote is valid and the wallet has not voted", () => { + expect(handler.canApply(wallet, voteTransaction, errors)).toBeTrue(); + expect(errors).toBeEmpty(); + }); + + it("should be true if the unvote is valid and the wallet has voted", () => { + wallet.vote = "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"; + + expect(handler.canApply(wallet, unvoteTransaction, errors)).toBeTrue(); + expect(errors).toBeEmpty(); + }); + + it("should be false if wallet has already voted", () => { + wallet.vote = "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"; + + expect(handler.canApply(wallet, voteTransaction, errors)).toBeFalse(); + expect(errors).toContain("Wallet has already voted"); + }); + + it("should be false if the asset public key differs from the currently voted one", () => { + wallet.vote = "a310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0"; + + expect(handler.canApply(wallet, unvoteTransaction, errors)).toBeFalse(); + expect(errors).toContain("The unvote public key does not match the currently voted one"); + }); + + it("should be false if unvoting a non-voted wallet", () => { + expect(handler.canApply(wallet, unvoteTransaction, errors)).toBeFalse(); + expect(errors).toContain("Wallet has not voted yet"); + }); + + it("should be false if wallet has insufficient funds", () => { + wallet.balance = Bignum.ZERO; + + expect(handler.canApply(wallet, voteTransaction, errors)).toBeFalse(); + expect(errors).toContain("Insufficient balance in the wallet"); + }); + }); + + describe("apply", () => { + describe("vote", () => { + it("should be ok", () => { + expect(wallet.vote).toBeNull(); + + handler.applyTransactionToSender(wallet, voteTransaction); + + expect(wallet.vote).not.toBeNull(); + }); + + it("should not be ok", () => { + wallet.vote = "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"; + + expect(wallet.vote).not.toBeNull(); + + handler.applyTransactionToSender(wallet, voteTransaction); + + expect(wallet.vote).not.toBeNull(); + }); + }); + + describe("unvote", () => { + it("should remove the vote from the wallet", () => { + wallet.vote = "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"; + + expect(wallet.vote).not.toBeNull(); + + handler.applyTransactionToSender(wallet, unvoteTransaction); + + expect(wallet.vote).toBeNull(); + }); + }); + }); + + describe("revert", () => { + describe("vote", () => { + it("should remove the vote from the wallet", () => { + wallet.vote = "02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"; + + expect(wallet.vote).not.toBeNull(); + + handler.revertTransactionForSender(wallet, voteTransaction); + + expect(wallet.vote).toBeNull(); + }); + }); + + describe("unvote", () => { + it("should add the vote to the wallet", () => { + expect(wallet.vote).toBeNull(); + + handler.revertTransactionForSender(wallet, unvoteTransaction); + + expect(wallet.vote).toBe("02d0d835266297f15c192be2636eb3fbc30b39b87fc583ff112062ef8ae1a1f2af"); + }); + }); + }); +}); diff --git a/packages/crypto/__tests__/identities/address.test.js b/packages/crypto/__tests__/identities/address.test.js deleted file mode 100644 index e58fe13ea8..0000000000 --- a/packages/crypto/__tests__/identities/address.test.js +++ /dev/null @@ -1,47 +0,0 @@ -const testSubject = require('../../lib/identities/address') -const Keys = require('../../lib/identities/keys') -const { data, passphrase } = require('./fixture') - -describe('Identities - Address', () => { - describe('fromPassphrase', () => { - it('should be a function', () => { - expect(testSubject.fromPassphrase).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromPassphrase(passphrase)).toBe(data.address) - }) - }) - - describe('fromPublicKey', () => { - it('should be a function', () => { - expect(testSubject.fromPublicKey).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromPublicKey(data.publicKey)).toBe(data.address) - }) - }) - - describe('fromPrivateKey', () => { - it('should be a function', () => { - expect(testSubject.fromPrivateKey).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromPrivateKey(Keys.fromPassphrase(passphrase))).toBe( - data.address, - ) - }) - }) - - describe('validate', () => { - it('should be a function', () => { - expect(testSubject.validate).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.validate(data.address)).toBeTrue() - }) - }) -}) diff --git a/packages/crypto/__tests__/identities/address.test.ts b/packages/crypto/__tests__/identities/address.test.ts new file mode 100644 index 0000000000..a614954c2c --- /dev/null +++ b/packages/crypto/__tests__/identities/address.test.ts @@ -0,0 +1,42 @@ +import "jest-extended"; + +import { PublicKeyError } from "../../src/errors"; +import { Address } from "../../src/identities/address"; +import { Keys } from "../../src/identities/keys"; +import { data, passphrase } from "./fixture.json"; + +describe("Identities - Address", () => { + describe("fromPassphrase", () => { + it("should be OK", () => { + expect(Address.fromPassphrase(passphrase)).toBe(data.address); + }); + }); + + describe("fromPublicKey", () => { + it("should pass with a valid public key", () => { + expect(Address.fromPublicKey(data.publicKey)).toBe(data.address); + }); + + it("should fail with an invalid public key", () => { + expect(() => { + Address.fromPublicKey("invalid"); + }).toThrow(PublicKeyError); + }); + }); + + describe("fromPrivateKey", () => { + it("should be OK", () => { + expect(Address.fromPrivateKey(Keys.fromPassphrase(passphrase))).toBe(data.address); + }); + }); + + describe("validate", () => { + it("should pass with a valid address", () => { + expect(Address.validate(data.address)).toBeTrue(); + }); + + it("should fail with an invalid address", () => { + expect(Address.validate("invalid")).toBeFalse(); + }); + }); +}); diff --git a/packages/crypto/__tests__/identities/fixture.json b/packages/crypto/__tests__/identities/fixture.json index ce876dd30b..5b56603923 100644 --- a/packages/crypto/__tests__/identities/fixture.json +++ b/packages/crypto/__tests__/identities/fixture.json @@ -1,9 +1,9 @@ { - "data": { - "privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", - "publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", - "address": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", - "wif": "SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA" - }, - "passphrase": "this is a top secret passphrase" + "data": { + "privateKey": "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712", + "publicKey": "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + "address": "D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib", + "wif": "SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA" + }, + "passphrase": "this is a top secret passphrase" } diff --git a/packages/crypto/__tests__/identities/keys.test.js b/packages/crypto/__tests__/identities/keys.test.js deleted file mode 100644 index 963a0790b2..0000000000 --- a/packages/crypto/__tests__/identities/keys.test.js +++ /dev/null @@ -1,92 +0,0 @@ -const testSubject = require('../../lib/identities/keys') -const Address = require('../../lib/identities/address') - -describe('Identities - Keys', () => { - describe('fromPassphrase', () => { - it('should be a function', () => { - expect(testSubject.fromPassphrase).toBeFunction() - }) - - it('should return two keys in hex', () => { - const keys = testSubject.fromPassphrase('secret') - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - - expect(keys.publicKey).toBeString() - expect(keys.publicKey).toMatch( - Buffer.from(keys.publicKey, 'hex').toString('hex'), - ) - - expect(keys.privateKey).toBeString() - expect(keys.privateKey).toMatch( - Buffer.from(keys.privateKey, 'hex').toString('hex'), - ) - }) - - it('should return address', () => { - const keys = testSubject.fromPassphrase( - 'SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov', - ) - const address = Address.fromPublicKey(keys.publicKey.toString('hex')) - expect(address).toBe('DUMjDrT8mgqGLWZtkCqzvy7yxWr55mBEub') - }) - }) - - describe('fromWIF', () => { - it('should be a function', () => { - expect(testSubject.fromWIF).toBeFunction() - }) - - it('should return two keys in hex', () => { - const keys = testSubject.fromWIF( - 'SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov', - ) - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - - expect(keys.publicKey).toBeString() - expect(keys.publicKey).toMatch( - Buffer.from(keys.publicKey, 'hex').toString('hex'), - ) - - expect(keys.privateKey).toBeString() - expect(keys.privateKey).toMatch( - Buffer.from(keys.privateKey, 'hex').toString('hex'), - ) - }) - - it('should return address', () => { - const keys = testSubject.fromWIF( - 'SDgGxWHHQHnpm5sth7MBUoeSw7V7nbimJ1RBU587xkryTh4qe9ov', - ) - const address = Address.fromPublicKey(keys.publicKey.toString('hex')) - expect(address).toBe('DCAaPzPAhhsMkHfQs7fZvXFW2EskDi92m8') - }) - - it('should get keys from compressed WIF', () => { - const keys = testSubject.fromWIF( - 'SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4', - ) - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - expect(keys).toHaveProperty('compressed', true) - }) - - it('should get keys from uncompressed WIF', () => { - const keys = testSubject.fromWIF( - '6hgnAG19GiMUf75C43XteG2mC8esKTiX9PYbKTh4Gca9MELRWmg', - ) - - expect(keys).toBeObject() - expect(keys).toHaveProperty('publicKey') - expect(keys).toHaveProperty('privateKey') - expect(keys).toHaveProperty('compressed', false) - }) - }) -}) diff --git a/packages/crypto/__tests__/identities/keys.test.ts b/packages/crypto/__tests__/identities/keys.test.ts new file mode 100644 index 0000000000..f5abe4d41d --- /dev/null +++ b/packages/crypto/__tests__/identities/keys.test.ts @@ -0,0 +1,79 @@ +import "jest-extended"; +import wif from "wif"; + +import { NetworkVersionError } from "../../src/errors"; +import { Address } from "../../src/identities/address"; +import { Keys } from "../../src/identities/keys"; +import { data, passphrase } from "./fixture.json"; + +describe("Identities - Keys", () => { + describe("fromPassphrase", () => { + it("should return two keys in hex", () => { + const keys = Keys.fromPassphrase("secret"); + + expect(keys).toBeObject(); + expect(keys.publicKey).toMatch(keys.publicKey); + expect(keys.privateKey).toMatch(keys.privateKey); + }); + + it("should return address", () => { + const keys = Keys.fromPassphrase(passphrase); + // @ts-ignore + const address = Address.fromPublicKey(keys.publicKey.toString("hex")); + expect(address).toBe(data.address); + }); + }); + + describe("fromPrivateKey", () => { + it("should return two keys in hex", () => { + const keys = Keys.fromPrivateKey(data.privateKey); + + expect(keys).toBeObject(); + expect(keys.publicKey).toMatch(data.publicKey); + expect(keys.privateKey).toMatch(data.privateKey); + }); + }); + + describe("fromWIF", () => { + it("should return two keys in hex", () => { + const keys = Keys.fromWIF("SGq4xLgZKCGxs7bjmwnBrWcT4C1ADFEermj846KC97FSv1WFD1dA"); + + expect(keys).toBeObject(); + expect(keys.publicKey).toMatch(data.publicKey); + expect(keys.privateKey).toMatch(data.privateKey); + }); + + it("should return address", () => { + const keys = Keys.fromWIF(data.wif); + // @ts-ignore + const address = Address.fromPublicKey(keys.publicKey.toString("hex")); + expect(address).toBe(data.address); + }); + + it("should get keys from compressed WIF", () => { + const keys = Keys.fromWIF("SAaaKsDdWMXP5BoVnSBLwTLn48n96UvG42WSUUooRv1HrEHmaSd4"); + + expect(keys).toBeObject(); + expect(keys).toHaveProperty("publicKey"); + expect(keys).toHaveProperty("privateKey"); + expect(keys).toHaveProperty("compressed", true); + }); + + it("should get keys from uncompressed WIF", () => { + const keys = Keys.fromWIF("6hgnAG19GiMUf75C43XteG2mC8esKTiX9PYbKTh4Gca9MELRWmg"); + + expect(keys).toBeObject(); + expect(keys).toHaveProperty("publicKey"); + expect(keys).toHaveProperty("privateKey"); + expect(keys).toHaveProperty("compressed", false); + }); + + it("should fail with an invalid network version", () => { + wif.decode = jest.fn(() => ({ version: 1 })); + + expect(() => { + Keys.fromWIF("invalid"); + }).toThrow(NetworkVersionError); + }); + }); +}); diff --git a/packages/crypto/__tests__/identities/private-key.test.js b/packages/crypto/__tests__/identities/private-key.test.js deleted file mode 100644 index cb054b130c..0000000000 --- a/packages/crypto/__tests__/identities/private-key.test.js +++ /dev/null @@ -1,24 +0,0 @@ -const testSubject = require('../../lib/identities/private-key') -const { data, passphrase } = require('./fixture') - -describe('Identities - Private Key', () => { - describe('fromPassphrase', () => { - it('should be a function', () => { - expect(testSubject.fromPassphrase).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromPassphrase(passphrase)).toBe(data.privateKey) - }) - }) - - describe('fromWIF', () => { - it('should be a function', () => { - expect(testSubject.fromWIF).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromWIF(data.wif)).toBe(data.privateKey) - }) - }) -}) diff --git a/packages/crypto/__tests__/identities/private-key.test.ts b/packages/crypto/__tests__/identities/private-key.test.ts new file mode 100644 index 0000000000..249efc9342 --- /dev/null +++ b/packages/crypto/__tests__/identities/private-key.test.ts @@ -0,0 +1,18 @@ +import "jest-extended"; + +import { PrivateKey } from "../../src/identities/private-key"; +import { data, passphrase } from "./fixture.json"; + +describe("Identities - Private Key", () => { + describe("fromPassphrase", () => { + it("should be OK", () => { + expect(PrivateKey.fromPassphrase(passphrase)).toBe(data.privateKey); + }); + }); + + describe("fromWIF", () => { + it("should be OK", () => { + expect(PrivateKey.fromWIF(data.wif)).toBe(data.privateKey); + }); + }); +}); diff --git a/packages/crypto/__tests__/identities/public-key.test.js b/packages/crypto/__tests__/identities/public-key.test.js deleted file mode 100644 index cd301aebbe..0000000000 --- a/packages/crypto/__tests__/identities/public-key.test.js +++ /dev/null @@ -1,34 +0,0 @@ -const testSubject = require('../../lib/identities/public-key') -const { data, passphrase } = require('./fixture') - -describe('Identities - Public Key', () => { - describe('fromPassphrase', () => { - it('should be a function', () => { - expect(testSubject.fromPassphrase).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromPassphrase(passphrase)).toBe(data.publicKey) - }) - }) - - describe('fromWIF', () => { - it('should be a function', () => { - expect(testSubject.fromWIF).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromWIF(data.wif)).toBe(data.publicKey) - }) - }) - - describe('validate', () => { - it('should be a function', () => { - expect(testSubject.validate).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.validate(data.publicKey)).toBeTrue() - }) - }) -}) diff --git a/packages/crypto/__tests__/identities/public-key.test.ts b/packages/crypto/__tests__/identities/public-key.test.ts new file mode 100644 index 0000000000..b63be2f612 --- /dev/null +++ b/packages/crypto/__tests__/identities/public-key.test.ts @@ -0,0 +1,28 @@ +import "jest-extended"; + +import { PublicKey } from "../../src/identities/public-key"; +import { data, passphrase } from "./fixture.json"; + +describe("Identities - Public Key", () => { + describe("fromPassphrase", () => { + it("should be OK", () => { + expect(PublicKey.fromPassphrase(passphrase)).toBe(data.publicKey); + }); + }); + + describe("fromWIF", () => { + it("should be OK", () => { + expect(PublicKey.fromWIF(data.wif)).toBe(data.publicKey); + }); + }); + + describe("validate", () => { + it("should pass with a valid public key", () => { + expect(PublicKey.validate(data.publicKey)).toBeTrue(); + }); + + it("should fail with an invalid public key", () => { + expect(PublicKey.validate("invalid")).toBeFalse(); + }); + }); +}); diff --git a/packages/crypto/__tests__/identities/wif.test.js b/packages/crypto/__tests__/identities/wif.test.js deleted file mode 100644 index e5d7f26e17..0000000000 --- a/packages/crypto/__tests__/identities/wif.test.js +++ /dev/null @@ -1,14 +0,0 @@ -const testSubject = require('../../lib/identities/wif') -const { data, passphrase } = require('./fixture') - -describe('Identities - WIF', () => { - describe('fromPassphrase', () => { - it('should be a function', () => { - expect(testSubject.fromPassphrase).toBeFunction() - }) - - it('should be OK', () => { - expect(testSubject.fromPassphrase(passphrase)).toBe(data.wif) - }) - }) -}) diff --git a/packages/crypto/__tests__/identities/wif.test.ts b/packages/crypto/__tests__/identities/wif.test.ts new file mode 100644 index 0000000000..5e6394809b --- /dev/null +++ b/packages/crypto/__tests__/identities/wif.test.ts @@ -0,0 +1,12 @@ +import "jest-extended"; + +import { WIF } from "../../src/identities/wif"; +import { data, passphrase } from "./fixture.json"; + +describe("Identities - WIF", () => { + describe("fromPassphrase", () => { + it("should be OK", () => { + expect(WIF.fromPassphrase(passphrase)).toBe(data.wif); + }); + }); +}); diff --git a/packages/crypto/__tests__/managers/config.test.js b/packages/crypto/__tests__/managers/config.test.js deleted file mode 100644 index 35c104a911..0000000000 --- a/packages/crypto/__tests__/managers/config.test.js +++ /dev/null @@ -1,104 +0,0 @@ -const configManager = require('../../lib/managers/config') -const feeManager = require('../../lib/managers/fee') -const dynamicFeeManager = require('../../lib/managers/dynamic-fee') -const network = require('../../lib/networks/ark/devnet.json') -const networkMainnet = require('../../lib/networks/ark/mainnet.json') -const { TRANSACTION_TYPES } = require('../../lib/constants') - -beforeEach(() => configManager.setConfig(network)) - -describe('Configuration', () => { - it('should be instantiated', () => { - expect(configManager).toBeObject() - }) - - it('should be set on runtime', () => { - configManager.setConfig(networkMainnet) - - expect(configManager.all()).toEqual(networkMainnet) - }) - - it('key should be "set"', () => { - configManager.set('key', 'value') - - expect(configManager.get('key')).toBe('value') - }) - - it('key should be "get"', () => { - expect(configManager.get('nethash')).toBe( - '2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867', - ) - }) - - it('should build constants', () => { - expect(configManager.constants).toEqual(network.constants) - }) - - it('should build fees', () => { - const fees = network.constants[0].fees.staticFees - - expect(feeManager.get(TRANSACTION_TYPES.TRANSFER)).toEqual(fees.transfer) - expect(feeManager.get(TRANSACTION_TYPES.SECOND_SIGNATURE)).toEqual( - fees.secondSignature, - ) - expect(feeManager.get(TRANSACTION_TYPES.DELEGATE_REGISTRATION)).toEqual( - fees.delegateRegistration, - ) - expect(feeManager.get(TRANSACTION_TYPES.VOTE)).toEqual(fees.vote) - expect(feeManager.get(TRANSACTION_TYPES.MULTI_SIGNATURE)).toEqual( - fees.multiSignature, - ) - expect(feeManager.get(TRANSACTION_TYPES.IPFS)).toEqual(fees.ipfs) - expect(feeManager.get(TRANSACTION_TYPES.TIMELOCK_TRANSFER)).toEqual( - fees.timelockTransfer, - ) - expect(feeManager.get(TRANSACTION_TYPES.MULTI_PAYMENT)).toEqual( - fees.multiPayment, - ) - expect(feeManager.get(TRANSACTION_TYPES.DELEGATE_RESIGNATION)).toEqual( - fees.delegateResignation, - ) - }) - - it('should build dynamic fee offsets', () => { - const addonBytes = network.constants[0].fees.dynamicFees.addonBytes - - expect(dynamicFeeManager.get(TRANSACTION_TYPES.TRANSFER)).toEqual( - addonBytes.transfer, - ) - expect(dynamicFeeManager.get(TRANSACTION_TYPES.SECOND_SIGNATURE)).toEqual( - addonBytes.secondSignature, - ) - expect( - dynamicFeeManager.get(TRANSACTION_TYPES.DELEGATE_REGISTRATION), - ).toEqual(addonBytes.delegateRegistration) - expect(dynamicFeeManager.get(TRANSACTION_TYPES.VOTE)).toEqual( - addonBytes.vote, - ) - expect(dynamicFeeManager.get(TRANSACTION_TYPES.MULTI_SIGNATURE)).toEqual( - addonBytes.multiSignature, - ) - expect(dynamicFeeManager.get(TRANSACTION_TYPES.IPFS)).toEqual( - addonBytes.ipfs, - ) - expect(dynamicFeeManager.get(TRANSACTION_TYPES.TIMELOCK_TRANSFER)).toEqual( - addonBytes.timelockTransfer, - ) - expect(dynamicFeeManager.get(TRANSACTION_TYPES.MULTI_PAYMENT)).toEqual( - addonBytes.multiPayment, - ) - expect( - dynamicFeeManager.get(TRANSACTION_TYPES.DELEGATE_RESIGNATION), - ).toEqual(addonBytes.delegateResignation) - }) - - it('should get constants for height', () => { - expect(configManager.getConstants(21600)).toEqual(network.constants[2]) - }) - - it('should set the height', () => { - configManager.setHeight(21600) - - expect(configManager.getHeight()).toEqual(21600) - }) -}) diff --git a/packages/crypto/__tests__/managers/config.test.ts b/packages/crypto/__tests__/managers/config.test.ts new file mode 100644 index 0000000000..e00cb50f66 --- /dev/null +++ b/packages/crypto/__tests__/managers/config.test.ts @@ -0,0 +1,66 @@ +import "jest-extended"; + +import { TransactionTypes } from "../../src/constants"; +import { configManager } from "../../src/managers/config"; +import { feeManager } from "../../src/managers/fee"; +import { devnet, mainnet } from "../../src/networks"; + +beforeEach(() => configManager.setConfig(devnet)); + +describe("Configuration", () => { + it("should be instantiated", () => { + expect(configManager).toBeObject(); + }); + + it("should be set on runtime", () => { + configManager.setConfig(mainnet); + + expect(configManager.all()).toContainAllKeys([ + ...Object.keys(mainnet.network), + ...["milestones", "exceptions", "genesisBlock"], + ]); + }); + + it('key should be "set"', () => { + configManager.set("key", "value"); + + expect(configManager.get("key")).toBe("value"); + }); + + it('key should be "get"', () => { + expect(configManager.get("nethash")).toBe("2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867"); + }); + + it("should build milestones", () => { + expect(configManager.milestones).toEqual(devnet.milestones); + }); + + it("should build fees", () => { + const feesStatic = devnet.milestones[0].fees.staticFees; + + expect(feeManager.get(TransactionTypes.Transfer)).toEqual(feesStatic.transfer); + expect(feeManager.get(TransactionTypes.SecondSignature)).toEqual(feesStatic.secondSignature); + expect(feeManager.get(TransactionTypes.DelegateRegistration)).toEqual(feesStatic.delegateRegistration); + expect(feeManager.get(TransactionTypes.Vote)).toEqual(feesStatic.vote); + expect(feeManager.get(TransactionTypes.MultiSignature)).toEqual(feesStatic.multiSignature); + expect(feeManager.get(TransactionTypes.Ipfs)).toEqual(feesStatic.ipfs); + expect(feeManager.get(TransactionTypes.TimelockTransfer)).toEqual(feesStatic.timelockTransfer); + expect(feeManager.get(TransactionTypes.MultiPayment)).toEqual(feesStatic.multiPayment); + expect(feeManager.get(TransactionTypes.DelegateResignation)).toEqual(feesStatic.delegateResignation); + }); + + it("should get milestone for height", () => { + expect(configManager.getMilestone(21600)).toEqual(devnet.milestones[2]); + }); + + it("should get milestone for this.height if height is not provided as parameter", () => { + configManager.setHeight(21600); + expect(configManager.getMilestone()).toEqual(devnet.milestones[2]); + }); + + it("should set the height", () => { + configManager.setHeight(21600); + + expect(configManager.getHeight()).toEqual(21600); + }); +}); diff --git a/packages/crypto/__tests__/managers/fee.test.js b/packages/crypto/__tests__/managers/fee.test.js deleted file mode 100644 index 03fad69168..0000000000 --- a/packages/crypto/__tests__/managers/fee.test.js +++ /dev/null @@ -1,29 +0,0 @@ -const feeManager = require('../../lib/managers/fee') -const { TRANSACTION_TYPES } = require('../../lib/constants') - -describe('Fee Manager', () => { - it('should be instantiated', () => { - expect(feeManager).toBeObject() - }) - - it('should set the fee', () => { - feeManager.set(TRANSACTION_TYPES.TRANSFER, 1) - - expect(feeManager.get(TRANSACTION_TYPES.TRANSFER)).toEqual(1) - }) - - it('should get multisignature fee (keysgroup length + 1)', () => { - const transaction = { - type: TRANSACTION_TYPES.MULTI_SIGNATURE, - asset: { - multisignature: { - keysgroup: [1, 2, 3], - }, - }, - } - - feeManager.set(TRANSACTION_TYPES.MULTI_SIGNATURE, 1) - - expect(feeManager.getForTransaction(transaction)).toEqual(4) - }) -}) diff --git a/packages/crypto/__tests__/managers/fee.test.ts b/packages/crypto/__tests__/managers/fee.test.ts new file mode 100644 index 0000000000..2af984f3c4 --- /dev/null +++ b/packages/crypto/__tests__/managers/fee.test.ts @@ -0,0 +1,42 @@ +import "jest-extended"; + +import { TransactionTypes } from "../../src/constants"; +import { feeManager } from "../../src/managers/fee"; +import { ITransactionData } from "../../src/models"; + +describe("Fee Manager", () => { + it("should be instantiated", () => { + expect(feeManager).toBeObject(); + }); + + it("should set the fee", () => { + feeManager.set(TransactionTypes.Transfer, 1); + + expect(feeManager.get(TransactionTypes.Transfer)).toEqual(1); + }); + + it("should get transaction fee", () => { + const transaction = { + type: TransactionTypes.Transfer, + } as ITransactionData; + + feeManager.set(TransactionTypes.Transfer, 111); + + expect(feeManager.getForTransaction(transaction)).toEqual(111); + }); + + it("should get multisignature fee (keysgroup length + 1)", () => { + const transaction = { + type: TransactionTypes.MultiSignature, + asset: { + multisignature: { + keysgroup: ["1", "2", "3"], + }, + }, + } as ITransactionData; + + feeManager.set(TransactionTypes.MultiSignature, 1); + + expect(feeManager.getForTransaction(transaction)).toEqual(4); + }); +}); diff --git a/packages/crypto/__tests__/managers/network.test.js b/packages/crypto/__tests__/managers/network.test.js deleted file mode 100644 index 59f1eb883a..0000000000 --- a/packages/crypto/__tests__/managers/network.test.js +++ /dev/null @@ -1,13 +0,0 @@ -const NetworkManager = require('../../lib/managers/network') -const networkMainnet = require('../../lib/networks/ark/mainnet.json') - -describe('Network Manager', () => { - it('should be instantiated', () => { - expect(NetworkManager).toBeDefined() - }) - - it('should find mainnet by name', () => { - const mainnet = NetworkManager.findByName('mainnet') - expect(mainnet).toMatchObject(networkMainnet) - }) -}) diff --git a/packages/crypto/__tests__/managers/network.test.ts b/packages/crypto/__tests__/managers/network.test.ts new file mode 100644 index 0000000000..6cf99b9f34 --- /dev/null +++ b/packages/crypto/__tests__/managers/network.test.ts @@ -0,0 +1,20 @@ +import "jest-extended"; + +import { NetworkManager } from "../../src/managers/network"; +import * as networks from "../../src/networks"; + +describe("Network Manager", () => { + it("should be instantiated", () => { + expect(NetworkManager).toBeDefined(); + }); + + it("should find mainnet by name", () => { + const actual = NetworkManager.findByName("mainnet"); + expect(actual).toMatchObject(networks.mainnet); + }); + + it("should get all networks", () => { + const all = NetworkManager.getAll(); + expect(all).toEqual(networks); + }); +}); diff --git a/packages/crypto/__tests__/models/block.test.js b/packages/crypto/__tests__/models/block.test.js deleted file mode 100644 index a5b8ef750d..0000000000 --- a/packages/crypto/__tests__/models/block.test.js +++ /dev/null @@ -1,540 +0,0 @@ -const ByteBuffer = require('bytebuffer') -const Block = require('../../lib/models/block') -const Bignum = require('../../lib/utils/bignum') - -describe('Models - Block', () => { - const data = { - id: '187940162505562345', - blockSignature: - '3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8', // eslint-disable-line max-len - generatorPublicKey: - '024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231', - height: 10, - numberOfTransactions: 0, - payloadHash: - '578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23', - payloadLength: 1, - previousBlock: '12123', - reward: 1, - timestamp: 111150, - totalAmount: 10, - totalFee: 1, - transactions: [], - version: 6, - } - - describe('constructor', () => { - it.skip('stores the data', () => {}) - it.skip('verifies the block', () => {}) - }) - - describe('getHeader', () => { - it('returns the block data without the transactions', () => { - // Ignore the verification for testing purposes - jest - .spyOn(Block.prototype, 'verify') - .mockImplementation(() => ({ verified: true })) - - const data2 = { ...data } - const header = new Block(data2).getHeader() - const bignumProperties = ['reward', 'totalAmount', 'totalFee'] - - Object.keys(data).forEach(key => { - if (key !== 'transactions') { - if (bignumProperties.includes(key)) { - expect(header[key]).toEqual(new Bignum(data2[key])) - } else { - expect(header[key]).toEqual(data2[key]) - } - } - }) - - expect(header).not.toHaveProperty('transactions') - }) - }) - - describe('serialize', () => { - const serialize = (object, includeSignature) => { - const serialized = Block.serialize(object, includeSignature) - const buffer = new ByteBuffer(1024, true) - buffer.append(serialized) - buffer.flip() - return buffer - } - - it('version is serialized as a TODO', () => { - expect(serialize(data).readUInt32(0)).toEqual(data.version) - }) - - it('timestamp is serialized as a UInt32', () => { - expect(serialize(data).readUInt32(4)).toEqual(data.timestamp) - }) - - it('height is serialized as a UInt32', () => { - expect(serialize(data).readUInt32(8)).toEqual(data.height) - }) - - describe('if `previousBlock` exists', () => { - it('is serialized as hexadecimal', () => { - const dataWithPreviousBlock = Object.assign({}, data, { - previousBlock: '1234', - }) - expect( - serialize(dataWithPreviousBlock) - .slice(12, 20) - .toString('hex'), - ).toEqual(dataWithPreviousBlock.previousBlockHex) - }) - }) - - describe('if `previousBlock` does not exist', () => { - it('8 bytes are added, as padding', () => { - const dataWithoutPreviousBlock = Object.assign({}, data) - delete dataWithoutPreviousBlock.previousBlock - expect( - serialize(dataWithoutPreviousBlock) - .slice(12, 20) - .toString('hex'), - ).toEqual('0000000000000000') - }) - }) - - it('number of transactions is serialized as a UInt32', () => { - expect(serialize(data).readUInt32(20)).toEqual(data.numberOfTransactions) - }) - - it('`totalAmount` of transactions is serialized as a UInt64', () => { - expect( - serialize(data) - .readUInt64(24) - .toNumber(), - ).toEqual(+data.totalAmount) - }) - - it('`totalFee` of transactions is serialized as a UInt64', () => { - expect( - serialize(data) - .readUInt64(32) - .toNumber(), - ).toEqual(+data.totalFee) - }) - - it('`reward` of transactions is serialized as a UInt64', () => { - expect( - serialize(data) - .readUInt64(40) - .toNumber(), - ).toEqual(+data.reward) - }) - - it('`payloadLength` of transactions is serialized as a UInt32', () => { - expect(serialize(data).readUInt32(48)).toEqual(data.payloadLength) - }) - - it('`payloadHash` of transactions is appended, using 32 bytes, as hexadecimal', () => { - expect( - serialize(data) - .slice(52, 52 + 32) - .toString('hex'), - ).toEqual(data.payloadHash) - }) - - it('`generatorPublicKey` of transactions is appended, using 33 bytes, as hexadecimal', () => { - expect( - serialize(data) - .slice(84, 84 + 33) - .toString('hex'), - ).toEqual(data.generatorPublicKey) - }) - - describe('if the `blockSignature` is not included', () => { - it('is not serialized', () => { - const data2 = { ...data } - delete data2.blockSignature - expect(serialize(data2).limit).toEqual(117) - }) - - it('is not serialized, even when the `includeSignature` parameter is true', () => { - const data2 = { ...data } - delete data2.blockSignature - expect(serialize(data2, true).limit).toEqual(117) - }) - }) - - describe('if the `blockSignature` is included', () => { - it('is serialized', () => { - expect( - serialize(data) - .slice(117, 188) - .toString('hex'), - ).toEqual(data.blockSignature) - }) - - it('is serialized unless the `includeSignature` parameter is false', () => { - expect(serialize(data, false).limit).toEqual(117) - }) - }) - }) - - describe('serializeFull', () => { - describe('genesis block', () => { - describe.each([ - ['mainnet', 468048], - ['devnet', 14492], - ['testnet', 46488], - ])('%s', (network, length) => { - const genesis = require(`@arkecosystem/core/lib/config/${network}/genesisBlock.json`) - const serialized = Block.serializeFull(genesis).toString('hex') - const genesisBlock = new Block(Block.deserialize(serialized)) - expect(serialized).toHaveLength(length) - expect(genesisBlock.verifySignature()).toBeTrue() - }) - }) - - describe('should validate hash', () => { - const b = { - id: '7176646138626297930', - version: 0, - height: 2243161, - timestamp: 24760440, - previousBlock: '3112633353705641986', - numberOfTransactions: 7, - totalAmount: '3890300', - totalFee: '70000000', - reward: '200000000', - payloadLength: 224, - payloadHash: - '3784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282', - generatorPublicKey: - '020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a325', - blockSignature: - '3045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29', - transactions: [ - { - type: 0, - amount: 555760, - fee: 10000000, - recipientId: 'DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY', - timestamp: 24760418, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb978', - signSignature: - '30450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717', - id: - '170543154a3b79459cbaa529f9f62b6f1342682799eb549dbf09fcca2d1f9c11', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - { - type: 0, - amount: 555750, - fee: 10000000, - recipientId: 'DGExsNogZR7JFa2656ZFP9TMWJYJh5djzQ', - timestamp: 24760416, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727', - signSignature: - '304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9', - id: - '1da153f37eceda233ff1b407ac18e47b3cae47c14cdcd5297d929618a916c4a7', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - { - type: 0, - amount: 555770, - fee: 10000000, - recipientId: 'DHGK5np6LuMMErfRfC5CmjpGu3ME85c25n', - timestamp: 24760420, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a', - signSignature: - '3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099f', - id: - '1e255f07dc25ce22d900ea81663c8f00d05a7b7c061e6fc3c731b05d642fa0b9', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - { - type: 0, - amount: 555750, - fee: 10000000, - recipientId: 'D7pcLJNGe197ibmWEmT8mM9KKU1htrcDyW', - timestamp: 24760417, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11', - signSignature: - '304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79', - id: - '66336c61d6ec623f8a1d2fd156a0fac16a4fe93bb3fba337859355c2119923a8', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - { - type: 0, - amount: 555760, - fee: 10000000, - recipientId: 'DD4yhwzryQdNGqKtezmycToQv63g27Tqqq', - timestamp: 24760418, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '30450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e8', - signSignature: - '30440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64', - id: - '78db36f7d79f51c67d7210ee3819dfb8d0d47b16a7484ebf55c5a055b17209a3', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - { - type: 0, - amount: 555760, - fee: 10000000, - recipientId: 'D5LiYGXL5keycWuTF6AFFwSRc6Mt4uEHMu', - timestamp: 24760419, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e', - signSignature: - '3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739', - id: - '83c80bb58777bb43f5037544b44ef69f191d3548fd1b2a00bed368f9f0d694c5', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - { - type: 0, - amount: 555750, - fee: 10000000, - recipientId: 'DPopNLwMvv4zSjdZnqUk8HFH13Mcb7NbEK', - timestamp: 24760416, - asset: {}, - vendorField: 'Goose Voter - True Block Weight', - senderPublicKey: - '0265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c0', - signature: - '3045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4', - signSignature: - '304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb', - id: - 'd2faf992fdd5da96d6d15038b6ddb65230338fa2096e45e44da51daad5e2f3ca', - senderId: 'DB8LnnQqYvHpG4WkGJ9AJWBYEct7G3yRZg', - hop: 2, - broadcast: false, - blockId: '7176646138626297930', - }, - ], - } - const s = Block.serializeFull(b).toString('hex') - const serialized = - '0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29ff000000fe00000000010000ff000000ff000000ff000000ff000000ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e40fad23d21da7a4fd4decb5c49726ea22f5e6bf6304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb97830450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e79c579fb08f448879c22fe965906b4e3b88d02ed304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9ff011e0064d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874fa7a080000000000000000001e84fee45dde2b11525afe192a2e991d014ff93a36304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099fff011e0061d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e1d69583ede5ee82d220e74bffb36bae2ce762dfb3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e56f9a37a859f4f84e93ce7593e809b15a524db2930450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e830440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64ff011e0063d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e0232a083c16aba4362dddec1b3050ffdd6d43f2e3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001eccc4fce0dc95f9951ee40c09a7ae807746cf51403045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb' - const block1 = new Block(b) - const block2 = new Block(Block.deserialize(serialized)) - - expect(s).toEqual(serialized) - expect(block1.verification.verified).toEqual(true) - expect(block2.verification.verified).toEqual(true) - }) - }) - - describe('should reorder correctly transactions in deserialization', () => { - const issue = { - version: 0, - timestamp: 25029544, - height: 3084276, - previousBlockHex: '63b315f3663e4299', - previousBlock: '7184109965722665625', - numberOfTransactions: 2, - totalAmount: 0, - totalFee: 600000000, - reward: 200000000, - payloadLength: 64, - payloadHash: - 'c2fa2d400b4c823873d476f6e0c9e423cf925e9b48f1b5706c7e2771d4095538', - generatorPublicKey: - '02fa6902e91e127d6d3410f6abc271a79ae24029079caa0db5819757e3c1c1c5a4', - blockSignature: - '30440220543f71d6f6445b703459b4f91d2c6f2446cbe6669e9c9008b1c77cc57073af2402206036fee3b434ffd5a31a579dd5b514a1c6384962291fda27b2463de903422834', - id: '11773170219525190460', - transactions: [ - { - type: 3, - network: 0x17, - timestamp: 25028325, - senderPublicKey: - '02aadc3e0993c1d3447db27741745eb9c2c6522cccf02fc8efe3bf2d49708243dd', - fee: 100000000, - amount: 0, - asset: { - votes: [ - '+020431436cf94f3c6a6ba566fe9e42678db8486590c732ca6c3803a10a86f50b92', - ], - }, - signature: - '3045022100be28bdd7dc7117de903eccf97e3afbe87e1a32ee25b0b9bf814b35c6773ed51802202c8d62e708aa7afc08dbfcfd4640d105fe97337fb6145a8d916f2ce11c920255', - recipientId: 'ANYiQJSPSoDT8U9Quh5vU8timD2RM7RS38', - id: - 'bace38ea544678f951cdd4abc269be24b4f5bab925ff6d5b480657952eb5aa65', - }, - { - id: - '7a1a43098cd253db395514220f69e3b99afaabb2bfcf5ecfa3b99727b367344b', - network: 0x17, - type: 1, - timestamp: 25028279, - fee: 500000000, - amount: 0, - senderPublicKey: - '02aadc3e0993c1d3447db27741745eb9c2c6522cccf02fc8efe3bf2d49708243dd', - signature: - '3044022071f4f5281ba7be76e43df4ea9e74f820da761e1f9f3b168b3a6e42c55ccf343a02203629d94845709e31be20943e2cd26637f0d8ccfb4a59764d45c161a942def069', - asset: { - signature: { - publicKey: - '02135e2ebd97d1f1ab5141b4269defc6e5650848062c40baaf869d72571526e6c6', - }, - }, - }, - ], - } - - const block = new Block(issue) - expect(block.data.id).toBe(issue.id) - expect(block.transactions[0].id).toBe(issue.transactions[1].id) - }) - - describe('v1 fix', () => { - const { - outlookTable, - } = require('../../lib/constants').CONFIGURATIONS.ARK.MAINNET - const table = { - '5139199631254983076': '1000099631254983076', - '4683900276587456793': '1000000276587456793', - '4719273207090574361': '1000073207090574361', - '10008425497949974873': '10000425497949974873', - '3011426208694781338': '1000026208694781338', - '122506651077645039': '100006651077645039', - '5720847785115142568': '1000047785115142568', - '7018402152859193732': '1000002152859193732', - '12530635932931954947': '10000635932931954947', - '7061061305098280027': '1000061305098280027', - '3983271186026110297': '1000071186026110297', - '3546732630357730082': '1000032630357730082', - '14024378732446299587': '10000378732446299587', - '5160516564770509401': '1000016564770509401', - '241883250703033792': '100003250703033792', - '18238049267092652511': '10000049267092652511', - '3824223895435898486': '1000023895435898486', - '4888561739037785996': '1000061739037785996', - '1256478353465481084': '1000078353465481084', - '12598210368652133913': '10000210368652133913', - '17559226088420912749': '10000226088420912749', - '13894975866600060289': '10000975866600060289', - '11710672157782824154': '10000672157782824154', - '5509880884401609373': '1000080884401609373', - '11486353335769396593': '10000353335769396593', - '10147280738049458646': '10000280738049458646', - '5684621525438367021': '1000021525438367021', - '719490120693255848': '100000120693255848', - '7154018532147250826': '1000018532147250826', - '38016207884795383': '10000207884795383', - '8324387831264270399': '1000087831264270399', - '10123661368384267251': '10000661368384267251', - '2222163236406460530': '1000063236406460530', - '5059382813585250340': '1000082813585250340', - '7091362542116598855': '1000062542116598855', - '8225244493039935740': '1000044493039935740', - } - - describe('outlook table', () => { - it('should be an object', () => { - expect(typeof outlookTable).toBe('object') - }) - it('should have expected values in the outlook table', () => { - expect(outlookTable).toEqual(table) - }) - }) - describe('apply v1 fix', () => { - it('should not process a common block', () => { - const mock = { - id: '187940162505562345', - blockSignature: - '3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8', // eslint-disable-line max-len - generatorPublicKey: - '024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231', - height: 10, - numberOfTransactions: 0, - payloadHash: - '578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23', - payloadLength: 1, - previousBlock: '12123', - reward: 1, - timestamp: 111150, - totalAmount: 10, - totalFee: 1, - transactions: [], - version: 6, - } - const blk = new Block(mock) - expect(blk.data.id).toBe(mock.id) - }) - it('should process a matching id', () => { - const mock2 = { - id: '8225244493039935740', - blockSignature: - '3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8', // eslint-disable-line max-len - generatorPublicKey: - '024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231', - height: 10, - numberOfTransactions: 0, - payloadHash: - '578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23', - payloadLength: 1, - previousBlock: '12123', - reward: 1, - timestamp: 111150, - totalAmount: 10, - totalFee: 1, - transactions: [], - version: 6, - } - const blk2 = new Block(mock2) - expect(blk2.data.id).not.toBe(mock2.id) - }) - }) - }) -}) diff --git a/packages/crypto/__tests__/models/block.test.ts b/packages/crypto/__tests__/models/block.test.ts new file mode 100644 index 0000000000..b5210f755c --- /dev/null +++ b/packages/crypto/__tests__/models/block.test.ts @@ -0,0 +1,551 @@ +import "jest-extended"; + +import { generators } from "@arkecosystem/core-test-utils"; +const { generateTransfers } = generators; + +import ByteBuffer from "bytebuffer"; +import { configManager } from "../../src"; +import { slots } from "../../src/crypto"; +import { Block, Delegate } from "../../src/models"; +import { testnet } from "../../src/networks"; +import { Bignum } from "../../src/utils/bignum"; +import { dummyBlock, dummyBlock2 } from "../fixtures/block"; + +const { outlookTable } = configManager.getPreset("mainnet").exceptions; + +describe("Models - Block", () => { + const data = { + id: "187940162505562345", + blockSignature: + "3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8", + generatorPublicKey: "024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231", + height: 10, + numberOfTransactions: 0, + payloadHash: "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", + payloadLength: 1, + previousBlock: "12123", + reward: 1, + timestamp: 111150, + totalAmount: 10, + totalFee: 1, + transactions: [], + version: 6, + }; + + describe("constructor", () => { + it("should store the data", () => { + const block = new Block(dummyBlock); + + expect(block.data.blockSignature).toBe(dummyBlock.blockSignature); + expect(block.data.generatorPublicKey).toBe(dummyBlock.generatorPublicKey); + expect(block.data.height).toBe(dummyBlock.height); + expect(block.data.numberOfTransactions).toBe(dummyBlock.numberOfTransactions); + expect(block.data.payloadLength).toBe(dummyBlock.payloadLength); + expect((block.data.reward as Bignum).toFixed()).toBe(dummyBlock.reward); + expect(block.data.timestamp).toBe(dummyBlock.timestamp); + expect((block.data.totalFee as Bignum).toFixed()).toBe(dummyBlock.totalFee); + expect(block.data.version).toBe(dummyBlock.version); + }); + + it("should verify the block", () => { + const block = new Block(dummyBlock); + + expect(block.verification.verified).toBeTrue(); + }); + + it("should fail to verify the block ", () => { + const block = new Block(data); + + expect(block.verification.verified).toBeFalse(); + }); + + it("should fail to verify a block with no previous block", () => { + const deserializeFunction = Block.deserialize; + jest.spyOn(Block, "deserialize").mockImplementation(ser => { + const deser = deserializeFunction(ser); + return Object.assign(deser, { previousBlock: undefined }); + }); + const block = new Block(dummyBlock); + + expect(block.verification.verified).toBeFalse(); + expect(block.verification.errors).toEqual(["Invalid previous block", "Failed to verify block signature"]); + + jest.restoreAllMocks(); + }); + + it("should fail to verify a block with incorrect timestamp", () => { + jest.spyOn(slots, "getSlotNumber").mockImplementation(timestamp => (timestamp ? 2 : 0)); + const block = new Block(dummyBlock); + + expect(block.verification.verified).toBeFalse(); + expect(block.verification.errors).toEqual(["Invalid block timestamp"]); + + jest.restoreAllMocks(); + }); + + it("should fail to verify a block with too much transactions", () => { + const delegate = new Delegate("super cool passphrase", testnet.network); + const optionsDefault = { + timestamp: 12345689, + previousBlock: { + id: "11111111", + idHex: "11111111", + height: 2, + }, + reward: new Bignum(0), + }; + const transactions = generateTransfers( + "testnet", + "super cool passphrase", + "DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY", + 10, + 210, + true, + ); + + const blockForged = delegate.forge(transactions, optionsDefault); + + const block = new Block(blockForged.toJson()); + + expect(block.verification.verified).toBeFalse(); + expect(block.verification.errors).toEqual(["Transactions length is too high"]); + }); + + it("should fail to verify a block with duplicate transactions", () => { + const delegate = new Delegate("super cool passphrase", testnet.network); + const optionsDefault = { + timestamp: 12345689, + previousBlock: { + id: "11111111", + idHex: "11111111", + height: 2, + }, + reward: new Bignum(0), + }; + const transactions = generateTransfers( + "testnet", + "super cool passphrase", + "DB4gFuDztmdGALMb8i1U4Z4R5SktxpNTAY", + 10, + 1, + true, + ); + + const blockForged = delegate.forge([transactions[0], transactions[0]], optionsDefault); + + const block = new Block(blockForged.toJson()); + + expect(block.verification.verified).toBeFalse(); + expect(block.verification.errors).toEqual([`Encountered duplicate transaction: ${transactions[0].id}`]); + }); + + it("should fail to verify a block with too large payload", () => { + jest.spyOn(configManager, "getMilestone").mockImplementation(height => ({ + block: { + version: 0, + maxTransactions: 200, + maxPayload: 0, + }, + reward: 200000000, + })); + const block = new Block(dummyBlock); + + expect(block.verification.verified).toBeFalse(); + expect(block.verification.errors).toEqual(["Payload is too large"]); + + jest.restoreAllMocks(); + }); + + it("should fail to verify a block if error is thrown", () => { + const errorMessage = "Very very, very bad error"; + jest.spyOn(configManager, "getMilestone").mockImplementation(height => { + throw errorMessage; + }); + const block = new Block(dummyBlock); + + expect(block.verification.verified).toBeFalse(); + expect(block.verification.errors).toEqual([errorMessage]); + + jest.restoreAllMocks(); + }); + + it("should construct the block (header only)", () => { + const block = new Block(dummyBlock2.serialized); + const actual = block.toJson(); + + expect(actual.version).toBe(dummyBlock2.data.version); + expect(actual.timestamp).toBe(dummyBlock2.data.timestamp); + expect(actual.height).toBe(dummyBlock2.data.height); + expect(actual.previousBlock).toBe(dummyBlock2.data.previousBlock); + expect(actual.numberOfTransactions).toBe(dummyBlock2.data.numberOfTransactions); + expect(actual.totalAmount).toBe(+dummyBlock2.data.totalAmount); + expect(actual.totalFee).toBe(+dummyBlock2.data.totalFee); + expect(actual.reward).toBe(+dummyBlock2.data.reward); + expect(actual.payloadLength).toBe(dummyBlock2.data.payloadLength); + expect(actual.payloadHash).toBe(dummyBlock2.data.payloadHash); + expect(actual.generatorPublicKey).toBe(dummyBlock2.data.generatorPublicKey); + expect(actual.blockSignature).toBe(dummyBlock2.data.blockSignature); + expect(actual.transactions).toBeEmpty(); + }); + + it("should construct the block (full)", () => { + const block = new Block(dummyBlock2.serializedFull); + const actual = block.toJson(); + + expect(actual.version).toBe(dummyBlock2.data.version); + expect(actual.timestamp).toBe(dummyBlock2.data.timestamp); + expect(actual.height).toBe(dummyBlock2.data.height); + expect(actual.previousBlock).toBe(dummyBlock2.data.previousBlock); + expect(actual.numberOfTransactions).toBe(dummyBlock2.data.numberOfTransactions); + expect(actual.totalAmount).toBe(+dummyBlock2.data.totalAmount); + expect(actual.totalFee).toBe(+dummyBlock2.data.totalFee); + expect(actual.reward).toBe(+dummyBlock2.data.reward); + expect(actual.payloadLength).toBe(dummyBlock2.data.payloadLength); + expect(actual.payloadHash).toBe(dummyBlock2.data.payloadHash); + expect(actual.generatorPublicKey).toBe(dummyBlock2.data.generatorPublicKey); + expect(actual.blockSignature).toBe(dummyBlock2.data.blockSignature); + expect(actual.transactions).toHaveLength(7); + }); + }); + + describe("getHeader", () => { + it("returns the block data without the transactions", () => { + // Ignore the verification for testing purposes + jest.spyOn(Block.prototype as any, "verify").mockImplementation(() => ({ verified: true })); + + const data2 = { ...data }; + const header = new Block(data2).getHeader(); + const bignumProperties = ["reward", "totalAmount", "totalFee"]; + + Object.keys(data).forEach(key => { + if (key !== "transactions") { + if (bignumProperties.includes(key)) { + expect(header[key]).toEqual(new Bignum(data2[key])); + } else { + expect(header[key]).toEqual(data2[key]); + } + } + }); + + expect(header).not.toHaveProperty("transactions"); + + jest.restoreAllMocks(); + }); + }); + + describe("serialize", () => { + const serialize = (object, includeSignature?: any) => { + const serialized = Block.serialize(object, includeSignature); + const buffer = new ByteBuffer(1024, true); + buffer.append(serialized); + buffer.flip(); + return buffer; + }; + + it("version is serialized as a TODO", () => { + expect(serialize(data).readUint32(0)).toEqual(data.version); + }); + + it("timestamp is serialized as a UInt32", () => { + expect(serialize(data).readUint32(4)).toEqual(data.timestamp); + }); + + it("height is serialized as a UInt32", () => { + expect(serialize(data).readUint32(8)).toEqual(data.height); + }); + + describe("if `previousBlock` exists", () => { + it("is serialized as hexadecimal", () => { + const dataWithPreviousBlock: any = Object.assign({}, data, { + previousBlock: "1234", + }); + expect( + serialize(dataWithPreviousBlock) + .slice(12, 20) + .toString("hex"), + ).toEqual(dataWithPreviousBlock.previousBlockHex); + }); + }); + + describe("if `previousBlock` does not exist", () => { + it("8 bytes are added, as padding", () => { + const dataWithoutPreviousBlock = Object.assign({}, data); + delete dataWithoutPreviousBlock.previousBlock; + expect( + serialize(dataWithoutPreviousBlock) + .slice(12, 20) + .toString("hex"), + ).toEqual("0000000000000000"); + }); + }); + + it("number of transactions is serialized as a UInt32", () => { + expect(serialize(data).readUint32(20)).toEqual(data.numberOfTransactions); + }); + + it("`totalAmount` of transactions is serialized as a UInt64", () => { + expect( + serialize(data) + .readUint64(24) + .toNumber(), + ).toEqual(+data.totalAmount); + }); + + it("`totalFee` of transactions is serialized as a UInt64", () => { + expect( + serialize(data) + .readUint64(32) + .toNumber(), + ).toEqual(+data.totalFee); + }); + + it("`reward` of transactions is serialized as a UInt64", () => { + expect( + serialize(data) + .readUint64(40) + .toNumber(), + ).toEqual(+data.reward); + }); + + it("`payloadLength` of transactions is serialized as a UInt32", () => { + expect(serialize(data).readUint32(48)).toEqual(data.payloadLength); + }); + + it("`payloadHash` of transactions is appended, using 32 bytes, as hexadecimal", () => { + expect( + serialize(data) + .slice(52, 52 + 32) + .toString("hex"), + ).toEqual(data.payloadHash); + }); + + it("`generatorPublicKey` of transactions is appended, using 33 bytes, as hexadecimal", () => { + expect( + serialize(data) + .slice(84, 84 + 33) + .toString("hex"), + ).toEqual(data.generatorPublicKey); + }); + + describe("if the `blockSignature` is not included", () => { + it("is not serialized", () => { + const data2 = { ...data }; + delete data2.blockSignature; + expect(serialize(data2).limit).toEqual(117); + }); + + it("is not serialized, even when the `includeSignature` parameter is true", () => { + const data2 = { ...data }; + delete data2.blockSignature; + expect(serialize(data2, true).limit).toEqual(117); + }); + }); + + describe("if the `blockSignature` is included", () => { + it("is serialized", () => { + expect( + serialize(data) + .slice(117, 188) + .toString("hex"), + ).toEqual(data.blockSignature); + }); + + it("is serialized unless the `includeSignature` parameter is false", () => { + expect(serialize(data, false).limit).toEqual(117); + }); + }); + }); + + describe("getIdFromSerialized", () => { + it("should get the id from serialized buffer", () => { + const serialized = Block.serialize(data); + + expect(Block.getIdFromSerialized(serialized)).toBe(data.id); + }); + }); + + describe("serializeFull", () => { + describe("genesis block", () => { + describe.each([["mainnet", 468048], ["devnet", 14492], ["testnet", 46488]])("%s", (network, length) => { + const genesis = require(`@arkecosystem/crypto/src/networks/${network}/genesisBlock.json`); + const serialized = Block.serializeFull(genesis).toString("hex"); + const genesisBlock = new Block(Block.deserialize(serialized)); + expect(serialized).toHaveLength(length); + expect(genesisBlock.verifySignature()).toBeTrue(); + }); + }); + + describe("should validate hash", () => { + // @ts-ignore + const s = Block.serializeFull(dummyBlock).toString("hex"); + const serialized = + "0000000078d07901593a22002b324b8b33a85802070000007c5c3b0000000000801d2c040000000000c2eb0b00000000e00000003784b953afcf936bdffd43fdf005b5732b49c1fc6b11e195c364c20b2eb06282020f5df4d2bc736d12ce43af5b1663885a893fade7ee5e62b3cc59315a63e6a3253045022100eee6c37b5e592e99811d588532726353592923f347c701d52912e6d583443e400220277ffe38ad31e216ba0907c4738fed19b2071246b150c72c0a52bae4477ebe29ff000000fe00000000010000ff000000ff000000ff000000ff000000ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e40fad23d21da7a4fd4decb5c49726ea22f5e6bf6304402204f12469157b19edd06ba25fcad3d4a5ef5b057c23f9e02de4641e6f8eef0553e022010121ab282f83efe1043de9c16bbf2c6845a03684229a0d7c965ffb9abdfb97830450221008327862f0b9178d6665f7d6674978c5caf749649558d814244b1c66cdf945c40022015918134ef01fed3fe2a2efde3327917731344332724522c75c2799a14f78717ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e79c579fb08f448879c22fe965906b4e3b88d02ed304402205f82feb8c5d1d79c565c2ff7badb93e4c9827b132d135dda11cb25427d4ef8ac02205ff136f970533c4ec4c7d0cd1ea7e02d7b62629b66c6c93265f608d7f2389727304402207e912031fcc700d8a55fbc415993302a0d8e6aea128397141b640b6dba52331702201fd1ad3984e42af44f548907add6cb7ad72ca0070c8cc1d8dc9bbda208c56bd9ff011e0064d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874fa7a080000000000000000001e84fee45dde2b11525afe192a2e991d014ff93a36304502210083216e6969e068770e6d2fe5c244881002309df84d20290ddf3f858967ed010202202a479b3da5080ea475d310ff13494654b42db75886a8808bd211b4bdb9146a7a3045022100e1dcab3406bbeb968146a4a391909ce41df9b71592a753b001e7c2ee1d382c5102202a74aeafd4a152ec61854636fbae829c41f1416c1e0637a0809408394973099fff011e0061d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001e1d69583ede5ee82d220e74bffb36bae2ce762dfb3045022100cd4fa9855227be11e17201419dacfbbd5d9946df8d6792a9488160025693821402207fb83969bad6a26959f437b5bb88e255b0a48eb04964d0c0d29f7ee94bd15e11304402205f50c2991a17743d17ffbb09159cadc35a3f848044261842879ccf5be9d81c5e022023bf21c32fb6e94494104f15f8d3a942ab120d0abd6fb4c93790b68e1b307a79ff011e0062d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e56f9a37a859f4f84e93ce7593e809b15a524db2930450221009c792062e13399ac6756b2e9f137194d06e106360ac0f3e24e55c7249cee0b3602205dc1d9c76d0451d1cb5a2396783a13e6d2d790ccfd49291e3d0a78349f7ea0e830440220083ba8a9af49b8be6e93794d71ec43ffc96a158375810e5d9f2478e71655315b0220278402ecaa1d224dab9f0f3b28295bbaea339c85c7400edafdc49df87439fc64ff011e0063d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874f07a080000000000000000001e0232a083c16aba4362dddec1b3050ffdd6d43f2e3044022063c65263e42be02bd9831b375c1d76a88332f00ed0557ecc1e7d2375ca40070902206797b5932c0bad68444beb5a38daa7cadf536ee2144e0d9777b812284d14374e3045022100b04da6692f75d43229ffd8486c1517e8952d38b4c03dfac38b6b360190a5c33e0220776622e5f09f92a1258b4a011f22181c977b622b8d1bbb2f83b42f4126d00739ff011e0060d079010265c1f6b8c1966a90f3fed7bc32fd4f42238ab4938fdb2a4e7ddd01ae8b58b4c080969800000000001f476f6f736520566f746572202d205472756520426c6f636b20576569676874e67a080000000000000000001eccc4fce0dc95f9951ee40c09a7ae807746cf51403045022100d4513c3608c2072e38e7a0e3bb8daf2cd5f7cc6fec9a5570dccd1eda696c591902202ecbbf3c9d0757be7b23c8b1cc6481c51600d158756c47fcb6f4a7f4893e31c4304402201fed4858d0806dd32220960900a871dd2f60e1f623af75feef9b1034a9a0a46402205a29b27c63fcc3e1ee1e77ecbbf4dd6e7db09901e7a09b9fd490cd68d62392cb"; + const block1 = new Block(dummyBlock); + const block2 = new Block(Block.deserialize(serialized)); + + expect(s).toEqual(serialized); + expect(block1.verification.verified).toEqual(true); + expect(block2.verification.verified).toEqual(true); + }); + }); + + describe("should reorder correctly transactions in deserialization", () => { + const issue = { + version: 0, + timestamp: 25029544, + height: 3084276, + previousBlockHex: "63b315f3663e4299", + previousBlock: "7184109965722665625", + numberOfTransactions: 2, + totalAmount: 0, + totalFee: 600000000, + reward: 200000000, + payloadLength: 64, + payloadHash: "c2fa2d400b4c823873d476f6e0c9e423cf925e9b48f1b5706c7e2771d4095538", + generatorPublicKey: "02fa6902e91e127d6d3410f6abc271a79ae24029079caa0db5819757e3c1c1c5a4", + blockSignature: + "30440220543f71d6f6445b703459b4f91d2c6f2446cbe6669e9c9008b1c77cc57073af2402206036fee3b434ffd5a31a579dd5b514a1c6384962291fda27b2463de903422834", + id: "11773170219525190460", + transactions: [ + { + type: 3, + network: 0x17, + timestamp: 25028325, + senderPublicKey: "02aadc3e0993c1d3447db27741745eb9c2c6522cccf02fc8efe3bf2d49708243dd", + fee: 100000000, + amount: 0, + asset: { + votes: ["+020431436cf94f3c6a6ba566fe9e42678db8486590c732ca6c3803a10a86f50b92"], + }, + signature: + "3045022100be28bdd7dc7117de903eccf97e3afbe87e1a32ee25b0b9bf814b35c6773ed51802202c8d62e708aa7afc08dbfcfd4640d105fe97337fb6145a8d916f2ce11c920255", + recipientId: "ANYiQJSPSoDT8U9Quh5vU8timD2RM7RS38", + id: "bace38ea544678f951cdd4abc269be24b4f5bab925ff6d5b480657952eb5aa65", + }, + { + id: "7a1a43098cd253db395514220f69e3b99afaabb2bfcf5ecfa3b99727b367344b", + network: 0x17, + type: 1, + timestamp: 25028279, + fee: 500000000, + amount: 0, + senderPublicKey: "02aadc3e0993c1d3447db27741745eb9c2c6522cccf02fc8efe3bf2d49708243dd", + signature: + "3044022071f4f5281ba7be76e43df4ea9e74f820da761e1f9f3b168b3a6e42c55ccf343a02203629d94845709e31be20943e2cd26637f0d8ccfb4a59764d45c161a942def069", + asset: { + signature: { + publicKey: "02135e2ebd97d1f1ab5141b4269defc6e5650848062c40baaf869d72571526e6c6", + }, + }, + }, + ], + }; + + const block = new Block(issue); + expect(block.data.id).toBe(issue.id); + expect(block.transactions[0].id).toBe(issue.transactions[1].id); + }); + + describe("v1 fix", () => { + const table = { + "5139199631254983076": "1000099631254983076", + "4683900276587456793": "1000000276587456793", + "4719273207090574361": "1000073207090574361", + "10008425497949974873": "10000425497949974873", + "3011426208694781338": "1000026208694781338", + "122506651077645039": "100006651077645039", + "5720847785115142568": "1000047785115142568", + "7018402152859193732": "1000002152859193732", + "12530635932931954947": "10000635932931954947", + "7061061305098280027": "1000061305098280027", + "3983271186026110297": "1000071186026110297", + "3546732630357730082": "1000032630357730082", + "14024378732446299587": "10000378732446299587", + "5160516564770509401": "1000016564770509401", + "241883250703033792": "100003250703033792", + "18238049267092652511": "10000049267092652511", + "3824223895435898486": "1000023895435898486", + "4888561739037785996": "1000061739037785996", + "1256478353465481084": "1000078353465481084", + "12598210368652133913": "10000210368652133913", + "17559226088420912749": "10000226088420912749", + "13894975866600060289": "10000975866600060289", + "11710672157782824154": "10000672157782824154", + "5509880884401609373": "1000080884401609373", + "11486353335769396593": "10000353335769396593", + "10147280738049458646": "10000280738049458646", + "5684621525438367021": "1000021525438367021", + "719490120693255848": "100000120693255848", + "7154018532147250826": "1000018532147250826", + "38016207884795383": "10000207884795383", + "8324387831264270399": "1000087831264270399", + "10123661368384267251": "10000661368384267251", + "2222163236406460530": "1000063236406460530", + "5059382813585250340": "1000082813585250340", + "7091362542116598855": "1000062542116598855", + "8225244493039935740": "1000044493039935740", + }; + + describe("outlook table", () => { + it("should have expected values in the outlook table", () => { + expect(outlookTable).toEqual(table); + }); + }); + describe("apply v1 fix", () => { + it("should not process a common block", () => { + const mock = { + id: "187940162505562345", + blockSignature: + "3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8", + generatorPublicKey: "024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231", + height: 10, + numberOfTransactions: 0, + payloadHash: "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", + payloadLength: 1, + previousBlock: "12123", + reward: 1, + timestamp: 111150, + totalAmount: 10, + totalFee: 1, + transactions: [], + version: 6, + }; + const blk = new Block(mock); + expect(blk.data.id).toBe(mock.id); + }); + it("should process a matching id", () => { + const mock2 = { + id: "8225244493039935740", + blockSignature: + "3045022100a6605198e0f590c88798405bc76748d84e280d179bcefed2c993e70cded2a5dd022008c7f915b89fc4f3250fc4b481abb753c68f30ac351871c50bd6cfaf151370e8", + generatorPublicKey: "024c8247388a02ecd1de2a3e3fd5b7c61ecc2797fa3776599d558333ef1802d231", + height: 10, + numberOfTransactions: 0, + payloadHash: "578e820911f24e039733b45e4882b73e301f813a0d2c31330dafda84534ffa23", + payloadLength: 1, + previousBlock: "12123", + reward: 1, + timestamp: 111150, + totalAmount: 10, + totalFee: 1, + transactions: [], + version: 6, + }; + const blk2 = new Block(mock2); + expect(blk2.data.id).not.toBe(mock2.id); + }); + }); + }); + + describe("toString", () => { + it("should return the block as a string", () => { + const block = new Block(dummyBlock); + + expect(block.toString()).toBe( + `${dummyBlock.id}, height: ${dummyBlock.height.toLocaleString()}, ${ + dummyBlock.numberOfTransactions + } transactions, verified: true, errors: `, + ); + }); + }); +}); diff --git a/packages/crypto/__tests__/models/delegate.test.js b/packages/crypto/__tests__/models/delegate.test.js deleted file mode 100644 index 2b37328658..0000000000 --- a/packages/crypto/__tests__/models/delegate.test.js +++ /dev/null @@ -1,38 +0,0 @@ -const Bignum = require('../../lib/utils/bignum') -const Wallet = require('../../lib/models/wallet') -const { ARKTOSHI } = require('../../lib/constants') -const sortTransactions = require('../../lib/utils/sort-transactions') -const configManager = require('../../lib/managers/config') - -describe('Models - Delegate', () => { - describe('static sortTransactions', () => { - it('returns the transactions ordered by type and id', () => { - const ordered = [ - { type: 1, id: 2 }, - { type: 1, id: 8 }, - { type: 2, id: 5 }, - { type: 2, id: 9 }, - ] - const unordered = [ordered[3], ordered[2], ordered[1], ordered[0]] - - expect(sortTransactions(unordered)).toEqual(ordered) - }) - }) - - describe('forge', () => { - describe('without version option', () => { - it("doesn't sort the transactions", () => { - const address = 'Abcde' - const wallet = new Wallet(address) - wallet.balance = new Bignum(ARKTOSHI) - - expect(wallet.toString()).toBe( - `${address} (1 ${configManager.config.client.symbol})`, - ) - }) - - // TODO probably useful for debugging - it('throws an Error', () => {}) - }) - }) -}) diff --git a/packages/crypto/__tests__/models/delegate.test.ts b/packages/crypto/__tests__/models/delegate.test.ts new file mode 100644 index 0000000000..247f8ac2c0 --- /dev/null +++ b/packages/crypto/__tests__/models/delegate.test.ts @@ -0,0 +1,233 @@ +import "jest-extended"; + +import { generators } from "@arkecosystem/core-test-utils"; +const { generateSecondSignature } = generators; + +import { ARKTOSHI } from "../../src/constants"; +import { configManager } from "../../src/managers/config"; +import { ITransactionData } from "../../src/models"; +import { Delegate } from "../../src/models/delegate"; +import { Wallet } from "../../src/models/wallet"; +import { INetwork, testnet } from "../../src/networks"; +import { Bignum } from "../../src/utils/bignum"; +import { sortTransactions } from "../../src/utils/sort-transactions"; + +const dummy = { + plainPassphrase: "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire", + bip38Passphrase: "6PYTQC4c2vBv6PGvV4HibNni6wNsHsGbR1qpL1DfkCNihsiWwXnjvJMU4B", + publicKey: "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + address: "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", +}; + +describe("Models - Delegate", () => { + describe("constructor", () => { + it("should be ok with a plain text passphrase", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + + expect(delegate.publicKey).toBe(dummy.publicKey); + expect(delegate.address).toBe(dummy.address); + expect(delegate.bip38).toBeFalse(); + }); + + describe("bip38", () => { + it("should pass with a valid passphrase", () => { + const delegate = new Delegate(dummy.bip38Passphrase, testnet.network, "bip38-password"); + + expect(delegate.publicKey).toBe(dummy.publicKey); + expect(delegate.address).toBe(dummy.address); + expect(delegate.bip38).toBeTrue(); + }); + + it("should fail with an invalid passphrase", () => { + const delegate = new Delegate(dummy.bip38Passphrase, testnet.network, "invalid-password"); + + expect(delegate.publicKey).toBeNull(); + expect(delegate.address).toBeNull(); + expect(delegate.bip38).toBeFalse(); + }); + }); + }); + + describe("encryptPassphrase", () => { + it("should pass with valid data", () => { + const passphrase = Delegate.encryptPassphrase(dummy.plainPassphrase, testnet.network, "bip38-password"); + + expect(passphrase).toBe(dummy.bip38Passphrase); + }); + + it("should fail with invalid data", () => { + expect(() => { + Delegate.encryptPassphrase(dummy.plainPassphrase, {} as INetwork, "bip38-password"); + }).toThrow(); + }); + }); + + describe("decryptPassphrase", () => { + it("should pass with a valid password", () => { + const { publicKey } = Delegate.decryptPassphrase(dummy.bip38Passphrase, testnet.network, "bip38-password"); + + expect(publicKey).toBe(dummy.publicKey); + }); + + it("should fail with an invalid password", () => { + expect(() => { + Delegate.decryptPassphrase(dummy.bip38Passphrase, testnet.network, "invalid-password"); + }).toThrow(); + }); + }); + + describe("encryptKeysWithOtp", () => { + it("should pass with a valid OTP secret", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + delegate.otpSecret = "one-time-password"; + + delegate.encryptKeysWithOtp(); + + expect(delegate.otp).toBeString(); + expect(delegate.encryptedKeys).toBeString(); + expect(delegate.keys).toBeNull(); + }); + + it("should fail without an OTP secret", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + delegate.otpSecret = undefined; + + expect(() => { + delegate.encryptKeysWithOtp(); + }).toThrow(); + }); + }); + + describe("decryptKeysWithOtp", () => { + it("should pass with valid data", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + delegate.otpSecret = "one-time-password"; + + delegate.encryptKeysWithOtp(); + + expect(delegate.otp).toBeString(); + expect(delegate.encryptedKeys).toBeString(); + expect(delegate.keys).toBeNull(); + + delegate.decryptKeysWithOtp(); + + expect(delegate.otp).toBeNull(); + expect(delegate.encryptedKeys).toBeNull(); + expect(delegate.keys).toBeObject(); + }); + + it("should fail with missing encrypted data", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + + expect(() => { + delegate.decryptKeysWithOtp(); + }).toThrow(); + }); + + it("should fail with invalid encrypted data", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + delegate.otpSecret = "one-time-password"; + + delegate.encryptKeysWithOtp(); + + expect(delegate.otp).toBeString(); + expect(delegate.encryptedKeys).toBeString(); + expect(delegate.keys).toBeNull(); + + delegate.encryptedKeys = undefined; + + expect(() => { + delegate.decryptKeysWithOtp(); + }).toThrow(); + }); + }); + + describe("sortTransactions", () => { + it("returns the transactions ordered by type and id", () => { + const ordered = [{ type: 1, id: "2" }, { type: 1, id: "8" }, { type: 2, id: "5" }, { type: 2, id: "9" }]; + const unordered = [ordered[3], ordered[2], ordered[1], ordered[0]] as ITransactionData[]; + + expect(sortTransactions(unordered)).toEqual(ordered); + }); + }); + + describe("forge", () => { + const optionsDefault = { + timestamp: 12345689, + previousBlock: { + id: "11111111", + idHex: "11111111", + height: 1, + }, + reward: new Bignum(0), + }; + const transactions = generateSecondSignature("testnet", dummy.plainPassphrase, 1, true); + const expectedBlockData = { + generatorPublicKey: dummy.publicKey, + timestamp: optionsDefault.timestamp, + previousBlock: optionsDefault.previousBlock.id, + height: optionsDefault.previousBlock.height + 1, + numberOfTransactions: 1, + totalAmount: new Bignum(transactions[0].amount), + totalFee: new Bignum(transactions[0].fee), + reward: new Bignum(optionsDefault.reward), + }; + + it("should forge a block", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + + const block = delegate.forge(transactions, optionsDefault); + + Object.keys(expectedBlockData).forEach(key => { + expect(block.data[key]).toEqual(expectedBlockData[key]); + }); + expect(block.verification).toEqual({ + errors: [], + verified: true, + }); + expect(block.transactions).toHaveLength(1); + expect(block.transactions[0].id).toBe(transactions[0].id); + }); + + it("should forge a block - bip38", () => { + const delegate = new Delegate(dummy.bip38Passphrase, testnet.network, "bip38-password"); + + const spyDecryptKeys = jest.spyOn(delegate, "decryptKeysWithOtp"); + const spyEncryptKeys = jest.spyOn(delegate, "encryptKeysWithOtp"); + + const block = delegate.forge(transactions, optionsDefault); + + expect(spyDecryptKeys).toHaveBeenCalledTimes(1); + expect(spyEncryptKeys).toHaveBeenCalledTimes(1); + + Object.keys(expectedBlockData).forEach(key => { + expect(block.data[key]).toEqual(expectedBlockData[key]); + }); + expect(block.verification).toEqual({ + errors: [], + verified: true, + }); + expect(block.transactions).toHaveLength(1); + expect(block.transactions[0].id).toBe(transactions[0].id); + }); + + it("should not forge a block if options.version is defined", () => { + const delegate = new Delegate(dummy.plainPassphrase, testnet.network); + + const options = { + version: "2.0.0", + }; + + const block = delegate.forge(transactions, options); + expect(block).toBeNull(); + }); + + it("should not forge a block if bip38 is on but encryptedKeys is not set", () => { + const delegate = new Delegate(dummy.bip38Passphrase, testnet.network, "bip38-password"); + delegate.encryptedKeys = undefined; + + const block = delegate.forge(transactions, optionsDefault); + expect(block).toBeNull(); + }); + }); +}); diff --git a/packages/crypto/__tests__/models/fixtures/multi-transaction.js b/packages/crypto/__tests__/models/fixtures/multi-transaction.js deleted file mode 100644 index 89be35c1dd..0000000000 --- a/packages/crypto/__tests__/models/fixtures/multi-transaction.js +++ /dev/null @@ -1,60 +0,0 @@ -module.exports = { - version: 1, - network: 30, - type: 4, - timestamp: 25921690, - senderPublicKey: - '02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece', - fee: 9000000000, - vendorFieldHex: '776c386c777473386a65', - asset: { - multisignature: { - min: 15, - lifetime: 72, - keysgroup: [ - '+034a7aca6841cfbdc688f09d55345f21c7ffbd1844693fa68d607fc94f729cbbea', - '+02fd6743ddfdc7c5bac24145e449c2e4f2d569b5297dd7bf088c3bc219f582a2f0', - '+02f9c51812f4be127b9f1f21cb4e146eca6aecc85739a243db0f1064981deda216', - '+0214d60ca95cd87a097ed6e6e42281acb68ae1815c8f494b8ff18d24dc9e072171', - '+02a14634e04e80b05acd56bc361af98498d76fbf5233f8d62773ceaa07172ddaa6', - '+021a9ba0e72f02b8fa7bad386582ec1d6c05b7664c892bf2a86035a85350f37203', - '+02e3ba373c6c352316b748e75358ead36504b0ef5139d215fb6a83a330c4eb60d5', - '+0309039bfa18d6fd790edb79438783b27fbcab06040a2fdaa66fb81ad53ca8db5f', - '+0393d12aff5962fa9065487959719a81c5d991e7c48a823039acd9254c2b673841', - '+03d3265264f06fe1dd752798403a73e537eb461fc729c83a84b579e8434adfe7c4', - '+02acfa496a6c12cb9acc3219993b17c62d19f4b570996c12a37d6e89eaa9716859', - '+03136f2101f1767b0d63d9545410bcaf3a941b2b6f06851093f3c679e0d31ca1cd', - '+02e6ec3941be86177bf0b998589c07da1b73e990466fdaee347c972c10f61b3797', - '+037dcd05d921a9f2ddd11960fec2ea9904fc55cad030549a6c5f5a41b2e35e56d2', - '+03206f7ae26f14cffb62b8c28b5e632952cdeb84b7c74ac0c2198b08bd84ee4f23', - ], - }, - }, - signature: - '3045022100c59d8efdd95010b0368963e492e8f7da1a3f6993b0723724f84aeccf658ea9a30220103d2b4ac07ad808b4ea79829a0081992586c6ef73892cfa7510ab37f0093bcd', // eslint-disable-line max-len - secondSignature: - '3044022059ab88d3fe83d6b52ddd5e863696416168f9cac6de962368587ad7343542f99f02207a95ca473006ab57dee6b06e06f8fa8f1b260548b9226614a0e1f760cc910479', // eslint-disable-line max-len - signatures: [ - '3045022100e9d0015e6e50e4852ad057ae6f83d0bf3e28fd722d1acf58f1a069a8f5043cf002201ee5b35b7f61af853802020fecf629ab59309c09d7e002b20636bdbb26ea92e3', // eslint-disable-line max-len - '3044022042c796e7f447462e60dfeeb8233d5fde3026e9c0aeda4544ae47995694d815a802204a183ae7d885fd875f6795d047364a73802c0d967d32cd797ee765ac10715f2d', // eslint-disable-line max-len - '30450221008a000e62a63298f401c616ae7222df9c187c92906736c0f879ba1162647e25c00220612cf87641075334b37f6e19590fcb45f9941aa50092fd429eda375caf5b8144', // eslint-disable-line max-len - '3045022100bd5ee0064df149406c7b4a64116c913181d7527a078972591280305b14686232022039e7437629b935ac39e2f547cbf191ffad4ab83805a360f50b49176dd5d604cc', // eslint-disable-line max-len - '30440220133a8dde6d87bcc8f627a57e7a1ad8a1f441d77246dee9d9d999851b4aeecbf102205c93de5881e6fd8ce808552b2ee91c6e144a734a9ef8640e0ca055904d5e5bd1', // eslint-disable-line max-len - '3045022100bb9de3351337532b42598a4bccba2f07a5a1dbbb23859094dd90eafa409c7bf3022071ff0989dc6f9c95b95784d3aa3386940a46372d3fb50b7f4e9d6372b0d3b73d', // eslint-disable-line max-len - '3044022075e2e0137f1567b7efeac40faf5b8d55ef32540c7d3931c27ae45748241a118e02203cbf5c251eec1e2c033b556315069bcc4f04960bfc705db6288530de28211fc9', // eslint-disable-line max-len - '30440220259d5ed227ec1cd6275f6e7c6dd299f73500f8b4aa21025296c87419e91e5900022045a70031c442c336f2b280a94e7058976c7f8ae6617fd4fbf41b913ac608c54a', // eslint-disable-line max-len - '3045022100f8537736fb7e25e9ccb4e5d6f3cf7f053d45dc7ff7d41914e4ab9f7b48b9347502204a46de5fd0cc03e2e1a559bc5ffc25a1fce5dc05d5f6f1794a0f3b9f38ebe406', // eslint-disable-line max-len - '3045022100d361e24290fddf2ad194c9a29200d88ccae83afc0f930e5fd5b2ecefcd2cebf3022065049135ced0fe238270511a748ba82be72f6298e2895d8b20680698bbc03ef5', // eslint-disable-line max-len - '30440220556b517965101ef1e0cf59eead99b802b053ac4a631111348d961df29952f2150220630ca079a5647d7d1f22b6f253d58b1f7fb128be9aeb33c9c1fdf53948e52db4', // eslint-disable-line max-len - '3044022047838187fcd1c79059ab53769678940e634400cf7431337a7e27e30bc17ac39702200d2cb974f44239b0edbb7c10096d5d7b682d0788dc3f8d0f5061373f4eefdff8', // eslint-disable-line max-len - '3044022057bbd8432154bf72244f987c5b4c49ba094a0e4abffc3644ba91c5ce1b79712a0220708a2743d4b4ab1d9379bcd60acf70b63e16cd24b95cd142a0b3ced0d43e6cb8', // eslint-disable-line max-len - '304502210097e1a36e47422455f030fa5f79153a4c49d25a2fb487837462b7f9d0fa3bd436022008a212ce0dfa18543987a1381b1261652d76e49f923e0b2ac79073b22367b3a5', // eslint-disable-line max-len - '304402201cbe68b0383c243cf61a9af9142746688b85f6ee106815906d9d8c4d717837aa022038c1eb4e06b90e24b44903de18c3f260e8be945b84126c9a048794f830b9f272', // eslint-disable-line max-len - ], - amount: 0, - signSignature: - '3044022059ab88d3fe83d6b52ddd5e863696416168f9cac6de962368587ad7343542f99f02207a95ca473006ab57dee6b06e06f8fa8f1b260548b9226614a0e1f760cc910479', // eslint-disable-line max-len - vendorField: 'wl8lwts8je', - recipientId: 'D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7', - id: '95199581d640eda97ea810fc3248e34f6e5ab1d8c9802e64a50b930f4ff044ab', -} diff --git a/packages/crypto/__tests__/models/fixtures/transaction.js b/packages/crypto/__tests__/models/fixtures/transaction.js deleted file mode 100644 index 6137dd8bf9..0000000000 --- a/packages/crypto/__tests__/models/fixtures/transaction.js +++ /dev/null @@ -1,30 +0,0 @@ -module.exports = { - version: 1, - network: 30, - type: 4, - timestamp: 48069935, - senderPublicKey: - '023b31dd57d9025a538ed6433c8e0d339abba3084612e8bbbe09a240e0208b33c9', - fee: 2000000000, - asset: { - multisignature: { - keysgroup: [ - '+02db1d199f20038e569500895b3521a453b2924e4a07c75aa9f7bf2aa4ad71392d', - '+02a7442df1f6cbef57d84c9c0eff248f9af48370384987de90bdcebd000feccdb6', - '+037a9458c87080768f79c4320941fdc64c9fe580673f17358125b93e80bd0b1d27', - ], - min: 3, - lifetime: 72, - }, - }, - signature: - '3045022100874eed54c9b4e8e41e47cfa340b38b87d6a16469d67462b066aea51fb3a8918d02205d812e19f382cfee5f9ce3bc98abc050f5aaa8262d9a2bf24d5cb117840d2948', - signatures: [ - '3044022022fb3b1d48d9e4905ab566949d637f0832dd0ab6f2cb67a620496e23e83a86d902203182ad967d22db258f97f9fab6d3856c29738ae745eb2f40eb5d472722b794b9', - '3045022100aef482ecaea6ecaf8e6f86bd7ac474458e657614b3eb9e440789549d1ea85f6002205c75763411e0febb7d11a7ccf7cb826fc11ddbe3722b73f77e22e9f0919e179d', - '3045022100e1dff5c0a4289ffee8caa79fd25fe86f0ded4daaeb9f25e123ea327b01fdb9710220476da4d177652fe4a375e414089ce8c86800bcc4ca6ce0b6d974ef98d8c9d4cf', - ], - amount: 0, - recipientId: 'DJLxkgm7JMortrGVh1ZrvDH39XALWLa83e', - id: '115d68cd8a8a9e589ddd51b721085831ce9472273b64b14e195e31dfbab4bf4e', -} diff --git a/packages/crypto/__tests__/models/transaction.test.js b/packages/crypto/__tests__/models/transaction.test.js deleted file mode 100644 index ecb9be6d09..0000000000 --- a/packages/crypto/__tests__/models/transaction.test.js +++ /dev/null @@ -1,244 +0,0 @@ -const Transaction = require('../../lib/models/transaction') -const builder = require('../../lib/builder') -const crypto = require('../../lib/crypto/crypto') -const transactionData = require('./fixtures/transaction') - -const configManager = require('../../lib/managers/config') -const network = require('../../lib/networks/ark/devnet.json') - -const createRandomTx = type => { - let transaction - - switch (type) { - case 0: { - // transfer - transaction = builder - .transfer() - .recipientId('AMw3TiLrmVmwmFVwRzn96kkUsUpFTqsAEX') - .amount(1000 * 1e10) - .vendorField(Math.random().toString(36)) - .sign(Math.random().toString(36)) - .secondSign(Math.random().toString(36)) - .build() - break - } - - case 1: { - // second signature - transaction = builder - .secondSignature() - .signatureAsset(Math.random().toString(36)) - .sign(Math.random().toString(36)) - .build() - break - } - - case 2: { - // delegate registration - transaction = builder - .delegateRegistration() - .usernameAsset('dummy-delegate') - .sign(Math.random().toString(36)) - .build() - break - } - - case 3: { - // vote registration - transaction = builder - .vote() - .votesAsset([ - '+036928c98ee53a1f52ed01dd87db10ffe1980eb47cd7c0a7d688321f47b5d7d760', - ]) - .sign(Math.random().toString(36)) - .build() - break - } - - case 4: { - // multisignature registration - const passphrases = [1, 2, 3].map(() => Math.random().toString(36)) - const publicKeys = passphrases.map( - passphrase => `+${crypto.getKeys(passphrase).publicKey}`, - ) - const min = Math.min(1, publicKeys.length) - const max = Math.max(1, publicKeys.length) - const minSignatures = Math.floor(Math.random() * (max - min)) + min - - const transactionBuilder = builder - .multiSignature() - .multiSignatureAsset({ - keysgroup: publicKeys, - min: minSignatures, - lifetime: Math.floor(Math.random() * (72 - 1)) + 1, - }) - .sign(Math.random().toString(36)) - - for (let i = 0; i < minSignatures; i++) { - transactionBuilder.multiSignatureSign(passphrases[i]) - } - - transaction = transactionBuilder.build() - break - } - default: { - throw new Error('Invalid transaction type') - } - } - - return transaction -} - -describe('Models - Transaction', () => { - beforeEach(() => configManager.setConfig(network)) - - describe('static fromBytes', () => { - it('should verify all transactions', () => { - ;[0, 1, 2, 3, 4] - .map(type => createRandomTx(type)) - .forEach(transaction => { - const ser = Transaction.serialize(transaction.data).toString('hex') - const newTransaction = Transaction.fromBytes(ser) - expect(newTransaction.data).toEqual(transaction.data) - expect(newTransaction.verified).toBeTrue() - }) - }) - - it('should create a transaction', () => { - const hex = Transaction.serialize(transactionData).toString('hex') - const transaction = Transaction.fromBytes(hex) - expect(transaction).toBeInstanceOf(Transaction) - - // We can't compare the data directly, since the created instance uses Bignums. - // ... call toJson() which casts the Bignums to numbers beforehand. - expect(transaction.toJson()).toEqual(transactionData) - }) - }) - - describe('static deserialize', () => { - it('should match transaction id', () => { - ;[0, 1, 2, 3, 4] - .map(type => createRandomTx(type)) - .forEach(transaction => { - const originalId = transaction.data.id - const newTransaction = new Transaction(transaction.data) - expect(newTransaction.id).toEqual(originalId) - }) - }) - }) - - describe('should deserialize correctly some tests transactions', () => { - const txs = [ - { - id: '80d75c7b90288246199e4a97ba726bad6639595ef92ad7c2bd14fd31563241ab', - network: 0x17, - height: 918991, - type: 1, - timestamp: 7410965, - amount: 0, - fee: 500000000, - recipientId: 'AP4UQ6j9hAHsxudpXh47RNQi7oF1AEfkAG', - senderPublicKey: - '03ca269b2942104b2ad601ccfbe7bd30b14b99cb55210ef7c1a5e25b6669646b99', - signature: - '3045022100d01e0cf0813a722ab5ad92aece2d4d1c3a537422e2ea769182f9172417224e890220437e407db51c4c47393db2e5b1258b2e3ecb707738a5ffdc6e96f08aee7e9c74', - asset: { - signature: { - publicKey: - '03c0e7e86dadd316275a31d84a1fdccd00cd26cc059982f95a1b24382c6ec2ceb0', - }, - }, - }, - { - id: '89f354918b36197269b0e5514f8da66f19829a024f664ccc124bfaabe0266e10', - version: 1, - timestamp: 48068690, - senderPublicKey: - '03b7d1da5c1b9f8efd0737d47123eb9cf7eb6d58198ef31bb6f01aa04bc4dda19d', - recipientId: 'DHPNjqCaTR9KYtC8nHh7Zt1G86Xj4YiU2V', - type: 1, - amount: '0', - fee: '500000000', - signature: - '3045022100e8e03bdac70e18f220feacba25c1575aa89d1ab61673e54eb2aff38439666d2702207e2d84290d7ef2571f5b2fab7e22a77dec96b1c4187cf9def15be74db98e2700', - asset: { - signature: { - publicKey: - '03b7d1da5c1b9f8efd0737d47123eb9cf7eb6d58198ef31bb6f01aa04bc4dda19d', - }, - }, - }, - { - id: 'a50af2bb1f043d128480346d0b49f5b3165716d5c630c6b0978dc7aa168e77a8', - version: 1, - timestamp: 48068923, - senderPublicKey: - '03173fd793c4bac0d64e9bd74ec5c2055716a7c0266feec9d6d94cb75be097b75d', - recipientId: 'DQrj9eh9otRgz2jWdu1K1ASBQqZA6dTkra', - type: 1, - amount: '0', - fee: '500000000', - signature: - '3045022100b263d28a5da58b17c874a5666afab0657f8492266554ad8ff722b00d41e1493d02200c2156dd9b9c1739f1c2099e98b763952bc7ef0423ad9786dcd32f7ffaf4aafc', - asset: { - signature: { - publicKey: - '03173fd793c4bac0d64e9bd74ec5c2055716a7c0266feec9d6d94cb75be097b75d', - }, - }, - }, - { - id: '68e34dc1c417cbfb47e5deea142974bc24c8d03df206f168c8b23d6a4decff73', - version: 1, - timestamp: 48068956, - senderPublicKey: - '02813ade967f05384e0567841d175294b4102c06c428011646e5ef989212925fcf', - recipientId: 'D8PGSYLUC3CxYaXoKjMA2gjV4RaeBpwghZ', - type: 1, - amount: '0', - fee: '500000000', - signature: - '3045022100e593eb501e89941461e247606d088b6e226cc5b5224f89cede532d35f9b16250022034bbdd098493639221e808301e0a99c3790ef9c6d357ac10266c518a2a66066f', - asset: { - signature: { - publicKey: - '02813ade967f05384e0567841d175294b4102c06c428011646e5ef989212925fcf', - }, - }, - }, - { - id: 'b4b3433be888b4b95b68b83a84a08e40d748b0ad92acf8487072ef01c1de251a', - version: 1, - timestamp: 48069792, - senderPublicKey: - '03f9f9dafc06faf4a54be2e45cd7a5523e41f38bb439f6f93cf00a0990e7afc116', - recipientId: 'DNuwcwYGTHDdhTPWMTYekhuGM1fFUpW9Jj', - type: 1, - amount: '0', - fee: '500000000', - signature: - '3044022052d1e5be426a79f827a67597fd460237de65e035593144e4e3afb0e82ab40f3802201d6e31892d000e73532bf8659851a3d221205d65ed1c0b8d08ce46b72c7f00ae', - asset: { - signature: { - publicKey: - '03f9f9dafc06faf4a54be2e45cd7a5523e41f38bb439f6f93cf00a0990e7afc116', - }, - }, - }, - ] - txs.forEach(tx => - it(`txid: ${tx.id}`, () => { - const newtx = new Transaction(tx) - expect(newtx.id).toEqual(tx.id) - }), - ) - }) - - describe('static serialize', () => {}) - - it('Signatures are verified', () => { - ;[0, 1, 2, 3, 4] - .map(type => createRandomTx(type)) - .forEach(transaction => expect(crypto.verify(transaction)).toBeTrue()) - }) -}) diff --git a/packages/crypto/__tests__/models/transaction.test.ts b/packages/crypto/__tests__/models/transaction.test.ts new file mode 100644 index 0000000000..53375e69f6 --- /dev/null +++ b/packages/crypto/__tests__/models/transaction.test.ts @@ -0,0 +1,231 @@ +import "jest-extended"; + +import { transactionBuilder as builder } from "../../src/builder"; +import { crypto } from "../../src/crypto/crypto"; +import { configManager } from "../../src/managers/config"; +import { Transaction } from "../../src/models/transaction"; +import { transaction as transactionData } from "../fixtures/transaction"; + +import { TransactionTypeError } from "../../src/errors"; +import { devnet } from "../../src/networks"; + +const createRandomTx = type => { + let transaction; + + switch (type) { + case 0: { + // transfer + transaction = builder + .transfer() + .recipientId("AMw3TiLrmVmwmFVwRzn96kkUsUpFTqsAEX") + .amount(1000 * 1e10) + .vendorField(Math.random().toString(36)) + .sign(Math.random().toString(36)) + .secondSign(Math.random().toString(36)) + .build(); + break; + } + + case 1: { + // second signature + transaction = builder + .secondSignature() + .signatureAsset(Math.random().toString(36)) + .sign(Math.random().toString(36)) + .build(); + break; + } + + case 2: { + // delegate registration + transaction = builder + .delegateRegistration() + .usernameAsset("dummy-delegate") + .sign(Math.random().toString(36)) + .build(); + break; + } + + case 3: { + // vote registration + transaction = builder + .vote() + .votesAsset(["+036928c98ee53a1f52ed01dd87db10ffe1980eb47cd7c0a7d688321f47b5d7d760"]) + .sign(Math.random().toString(36)) + .build(); + break; + } + + case 4: { + // multisignature registration + const passphrases = [1, 2, 3].map(() => Math.random().toString(36)); + const publicKeys = passphrases.map(passphrase => `+${crypto.getKeys(passphrase).publicKey}`); + const min = Math.min(1, publicKeys.length); + const max = Math.max(1, publicKeys.length); + const minSignatures = Math.floor(Math.random() * (max - min)) + min; + + const transactionBuilder = builder + .multiSignature() + .multiSignatureAsset({ + keysgroup: publicKeys, + min: minSignatures, + lifetime: Math.floor(Math.random() * (72 - 1)) + 1, + }) + .sign(Math.random().toString(36)); + + for (let i = 0; i < minSignatures; i++) { + transactionBuilder.multiSignatureSign(passphrases[i]); + } + + transaction = transactionBuilder.build(); + break; + } + default: { + throw new TransactionTypeError(type); + } + } + + return transaction; +}; + +describe("Models - Transaction", () => { + beforeEach(() => configManager.setConfig(devnet)); + + describe("static fromBytes", () => { + it("should verify all transactions", () => { + [0, 1, 2, 3] + .map(type => createRandomTx(type)) + .forEach(transaction => { + const ser = Transaction.serialize(transaction.data).toString("hex"); + const newTransaction = new Transaction(ser); + expect(newTransaction.data).toEqual(transaction.data); + expect(newTransaction.verified).toBeTrue(); + }); + }); + + it("should create a transaction", () => { + const hex = Transaction.serialize(transactionData).toString("hex"); + const transaction = new Transaction(hex); + expect(transaction).toBeInstanceOf(Transaction); + + // We can't compare the data directly, since the created instance uses Bignums. + // ... call toJson() which casts the Bignums to numbers beforehand. + expect(transaction.toJson()).toEqual(transactionData); + }); + }); + + describe("static deserialize", () => { + it("should match transaction id", () => { + [0, 1, 2, 3] + .map(type => createRandomTx(type)) + .forEach(transaction => { + const originalId = transaction.data.id; + const newTransaction = new Transaction(transaction.data); + expect(newTransaction.id).toEqual(originalId); + }); + }); + }); + + describe("should deserialize correctly some tests transactions", () => { + const txs = [ + { + id: "80d75c7b90288246199e4a97ba726bad6639595ef92ad7c2bd14fd31563241ab", + network: 0x17, + height: 918991, + type: 1, + timestamp: 7410965, + amount: 0, + fee: 500000000, + recipientId: "AP4UQ6j9hAHsxudpXh47RNQi7oF1AEfkAG", + senderPublicKey: "03ca269b2942104b2ad601ccfbe7bd30b14b99cb55210ef7c1a5e25b6669646b99", + signature: + "3045022100d01e0cf0813a722ab5ad92aece2d4d1c3a537422e2ea769182f9172417224e890220437e407db51c4c47393db2e5b1258b2e3ecb707738a5ffdc6e96f08aee7e9c74", + asset: { + signature: { + publicKey: "03c0e7e86dadd316275a31d84a1fdccd00cd26cc059982f95a1b24382c6ec2ceb0", + }, + }, + }, + { + id: "89f354918b36197269b0e5514f8da66f19829a024f664ccc124bfaabe0266e10", + version: 1, + timestamp: 48068690, + senderPublicKey: "03b7d1da5c1b9f8efd0737d47123eb9cf7eb6d58198ef31bb6f01aa04bc4dda19d", + recipientId: "DHPNjqCaTR9KYtC8nHh7Zt1G86Xj4YiU2V", + type: 1, + amount: "0", + fee: "500000000", + signature: + "3045022100e8e03bdac70e18f220feacba25c1575aa89d1ab61673e54eb2aff38439666d2702207e2d84290d7ef2571f5b2fab7e22a77dec96b1c4187cf9def15be74db98e2700", + asset: { + signature: { + publicKey: "03b7d1da5c1b9f8efd0737d47123eb9cf7eb6d58198ef31bb6f01aa04bc4dda19d", + }, + }, + }, + { + id: "a50af2bb1f043d128480346d0b49f5b3165716d5c630c6b0978dc7aa168e77a8", + version: 1, + timestamp: 48068923, + senderPublicKey: "03173fd793c4bac0d64e9bd74ec5c2055716a7c0266feec9d6d94cb75be097b75d", + recipientId: "DQrj9eh9otRgz2jWdu1K1ASBQqZA6dTkra", + type: 1, + amount: "0", + fee: "500000000", + signature: + "3045022100b263d28a5da58b17c874a5666afab0657f8492266554ad8ff722b00d41e1493d02200c2156dd9b9c1739f1c2099e98b763952bc7ef0423ad9786dcd32f7ffaf4aafc", + asset: { + signature: { + publicKey: "03173fd793c4bac0d64e9bd74ec5c2055716a7c0266feec9d6d94cb75be097b75d", + }, + }, + }, + { + id: "68e34dc1c417cbfb47e5deea142974bc24c8d03df206f168c8b23d6a4decff73", + version: 1, + timestamp: 48068956, + senderPublicKey: "02813ade967f05384e0567841d175294b4102c06c428011646e5ef989212925fcf", + recipientId: "D8PGSYLUC3CxYaXoKjMA2gjV4RaeBpwghZ", + type: 1, + amount: "0", + fee: "500000000", + signature: + "3045022100e593eb501e89941461e247606d088b6e226cc5b5224f89cede532d35f9b16250022034bbdd098493639221e808301e0a99c3790ef9c6d357ac10266c518a2a66066f", + asset: { + signature: { + publicKey: "02813ade967f05384e0567841d175294b4102c06c428011646e5ef989212925fcf", + }, + }, + }, + { + id: "b4b3433be888b4b95b68b83a84a08e40d748b0ad92acf8487072ef01c1de251a", + version: 1, + timestamp: 48069792, + senderPublicKey: "03f9f9dafc06faf4a54be2e45cd7a5523e41f38bb439f6f93cf00a0990e7afc116", + recipientId: "DNuwcwYGTHDdhTPWMTYekhuGM1fFUpW9Jj", + type: 1, + amount: "0", + fee: "500000000", + signature: + "3044022052d1e5be426a79f827a67597fd460237de65e035593144e4e3afb0e82ab40f3802201d6e31892d000e73532bf8659851a3d221205d65ed1c0b8d08ce46b72c7f00ae", + asset: { + signature: { + publicKey: "03f9f9dafc06faf4a54be2e45cd7a5523e41f38bb439f6f93cf00a0990e7afc116", + }, + }, + }, + ]; + txs.forEach(tx => + it(`txid: ${tx.id}`, () => { + const newtx = new Transaction(tx); + expect(newtx.id).toEqual(tx.id); + }), + ); + }); + + it("Signatures are verified", () => { + [0, 1, 2, 3] + .map(type => createRandomTx(type)) + .forEach(transaction => expect(crypto.verify(transaction)).toBeTrue()); + }); +}); diff --git a/packages/crypto/__tests__/models/wallet.test.js b/packages/crypto/__tests__/models/wallet.test.js deleted file mode 100644 index e55e4f4751..0000000000 --- a/packages/crypto/__tests__/models/wallet.test.js +++ /dev/null @@ -1,93 +0,0 @@ -const Bignum = require('../../lib/utils/bignum') -const Wallet = require('../../lib/models/wallet') -const multiTx = require('./fixtures/multi-transaction') -const { ARKTOSHI } = require('../../lib/constants') -const configManager = require('../../lib/managers/config') -const network = require('../../lib/networks/ark/devnet.json') - -describe('Models - Wallet', () => { - beforeEach(() => configManager.setConfig(network)) - - describe('toString', () => { - // TODO implementation is right? - it('returns the address and the balance', () => { - const address = 'Abcde' - const wallet = new Wallet(address) - const balance = parseInt((Math.random() * 1000).toFixed(8)) - wallet.balance = new Bignum(balance * ARKTOSHI) - expect(wallet.toString()).toBe( - `${address} (${balance} ${configManager.config.client.symbol})`, - ) - }) - }) - - describe('apply transaction', () => { - const testWallet = new Wallet('D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7') - const data = { - publicKey: - '02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece', - secondPublicKey: - '020d3c837d0a47ee7de1082cd48885003c5e92964e58bb34af3b58c6e42208ae03', - balance: new Bignum(109390000000), - vote: null, - username: null, - voteBalance: Bignum.ZERO, - multisignature: null, - dirty: false, - producedBlocks: 0, - missedBlocks: 0, - } - - it.skip('should be ok for a multi-transaction', () => { - Object.keys(data).forEach(k => { - testWallet[k] = data[k] - }) - expect(testWallet.canApply(multiTx, [])).toBeTrue() - }) - }) - - describe('apply block', () => { - let testWallet - let block - - beforeEach(() => { - testWallet = new Wallet('D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7') - testWallet.publicKey = - '02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece' - testWallet.balance = Bignum.ZERO - testWallet.producedBlocks = 0 - testWallet.forgedFees = Bignum.ZERO - testWallet.forgedRewards = Bignum.ZERO - testWallet.lastBlock = null - - block = { - id: 1, - generatorPublicKey: testWallet.publicKey, - reward: new Bignum(1000000000), - totalFee: new Bignum(1000000000), - } - }) - - it('should apply correct block', () => { - testWallet.applyBlock(block) - expect(testWallet.balance).toEqual(block.reward.plus(block.totalFee)) - expect(testWallet.producedBlocks).toBe(1) - expect(testWallet.forgedFees).toEqual(block.totalFee) - expect(testWallet.forgedRewards).toEqual(block.totalFee) - expect(testWallet.lastBlock).toBeObject(block) - expect(testWallet.dirty).toBeTrue() - }) - - it('should not apply incorrect block', () => { - block.generatorPublicKey = 'a'.repeat(66) - const originalWallet = Object.assign({}, testWallet) - testWallet.applyBlock(block) - expect(testWallet.balance).toEqual(originalWallet.balance) - expect(testWallet.producedBlocks).toBe(0) - expect(testWallet.forgedFees).toEqual(originalWallet.forgedFees) - expect(testWallet.forgedRewards).toEqual(originalWallet.forgedRewards) - expect(testWallet.lastBlock).toBe(originalWallet.lastBlock) - expect(testWallet.dirty).toBeTrue() - }) - }) -}) diff --git a/packages/crypto/__tests__/models/wallet.test.ts b/packages/crypto/__tests__/models/wallet.test.ts new file mode 100644 index 0000000000..c8c5b921e0 --- /dev/null +++ b/packages/crypto/__tests__/models/wallet.test.ts @@ -0,0 +1,424 @@ +import "jest-extended"; + +import { ARKTOSHI, TransactionTypes } from "../../src/constants"; +import { transactionHandler } from "../../src/handlers/transactions"; +import { configManager } from "../../src/managers/config"; +import { Wallet } from "../../src/models/wallet"; +import { Bignum } from "../../src/utils/bignum"; + +import { generators } from "@arkecosystem/core-test-utils"; +const { generateTransfers, generateDelegateRegistration, generateSecondSignature, generateVote } = generators; +import { devnet } from "../../src/networks"; +import { multiTransaction } from "../fixtures/multi-transaction"; + +describe("Models - Wallet", () => { + beforeEach(() => configManager.setConfig(devnet)); + + describe("toString", () => { + // TODO implementation is right? + it("returns the address and the balance", () => { + const address = "Abcde"; + const wallet = new Wallet(address); + const balance = +(Math.random() * 1000).toFixed(8); + wallet.balance = new Bignum(balance * ARKTOSHI); + expect(wallet.toString()).toBe(`${address} (${balance} ${configManager.config.client.symbol})`); + }); + }); + + describe("apply transaction", () => { + const testWallet = new Wallet("D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7"); + const data = { + publicKey: "02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece", + secondPublicKey: "020d3c837d0a47ee7de1082cd48885003c5e92964e58bb34af3b58c6e42208ae03", + balance: new Bignum(109390000000), + vote: null, + username: null, + voteBalance: Bignum.ZERO, + multisignature: null, + dirty: false, + producedBlocks: 0, + missedBlocks: 0, + }; + + it.skip("should be ok for a multi-transaction", () => { + Object.keys(data).forEach(k => { + testWallet[k] = data[k]; + }); + expect(testWallet.canApply(multiTransaction, [])).toBeTrue(); + }); + }); + + describe("apply block", () => { + let testWallet; + let block; + + beforeEach(() => { + testWallet = new Wallet("D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7"); + testWallet.publicKey = "02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece"; + testWallet.balance = Bignum.ZERO; + testWallet.producedBlocks = 0; + testWallet.forgedFees = Bignum.ZERO; + testWallet.forgedRewards = Bignum.ZERO; + testWallet.lastBlock = null; + + block = { + id: 1, + generatorPublicKey: testWallet.publicKey, + reward: new Bignum(1000000000), + totalFee: new Bignum(1000000000), + }; + }); + + it("should apply correct block", () => { + testWallet.applyBlock(block); + expect(testWallet.balance).toEqual(block.reward.plus(block.totalFee)); + expect(testWallet.producedBlocks).toBe(1); + expect(testWallet.forgedFees).toEqual(block.totalFee); + expect(testWallet.forgedRewards).toEqual(block.totalFee); + expect(testWallet.lastBlock).toBeObject(); + expect(testWallet.dirty).toBeTrue(); + }); + + it("should not apply incorrect block", () => { + block.generatorPublicKey = ("a" as any).repeat(66); + const originalWallet = Object.assign({}, testWallet); + testWallet.applyBlock(block); + expect(testWallet.balance).toEqual(originalWallet.balance); + expect(testWallet.producedBlocks).toBe(0); + expect(testWallet.forgedFees).toEqual(originalWallet.forgedFees); + expect(testWallet.forgedRewards).toEqual(originalWallet.forgedRewards); + expect(testWallet.lastBlock).toBe(originalWallet.lastBlock); + expect(testWallet.dirty).toBeTrue(); + }); + }); + + describe("revert block", () => { + const walletInit = { + balance: new Bignum(1000 * ARKTOSHI), + forgedFees: new Bignum(10 * ARKTOSHI), + forgedRewards: new Bignum(50 * ARKTOSHI), + producedBlocks: 1, + dirty: false, + lastBlock: { id: 1234856 }, + publicKey: "02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece", + address: "D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7", + }; + const block = { + id: 1, + generatorPublicKey: walletInit.publicKey, + reward: new Bignum(2 * ARKTOSHI), + totalFee: new Bignum(1 * ARKTOSHI), + }; + let testWallet; + + beforeEach(() => { + testWallet = new Wallet(walletInit.address); + testWallet = Object.assign(testWallet, walletInit); + }); + + it("should revert block if generator public key matches the wallet public key", () => { + const success = testWallet.revertBlock(block); + + expect(success).toBeTrue(); + expect(testWallet.balance).toEqual(walletInit.balance.minus(block.reward).minus(block.totalFee)); + expect(testWallet.producedBlocks).toBe(walletInit.producedBlocks - 1); + expect(testWallet.forgedFees).toEqual(walletInit.forgedFees.minus(block.totalFee)); + expect(testWallet.forgedRewards).toEqual(walletInit.forgedRewards.minus(block.reward)); + expect(testWallet.lastBlock).toBeNull(); + expect(testWallet.dirty).toBeTrue(); + }); + + it("should revert block if generator public key matches the wallet address", () => { + testWallet.publicKey = undefined; + const success = testWallet.revertBlock(block); + + expect(success).toBeTrue(); + expect(testWallet.balance).toEqual(walletInit.balance.minus(block.reward).minus(block.totalFee)); + expect(testWallet.producedBlocks).toBe(walletInit.producedBlocks - 1); + expect(testWallet.forgedFees).toEqual(walletInit.forgedFees.minus(block.totalFee)); + expect(testWallet.forgedRewards).toEqual(walletInit.forgedRewards.minus(block.reward)); + expect(testWallet.lastBlock).toBeNull(); + expect(testWallet.dirty).toBeTrue(); + }); + + it("should not revert block if generator public key doesn't match the wallet address / publicKey", () => { + const invalidWallet = Object.assign({}, walletInit, { publicKey: undefined, address: undefined }); + testWallet = Object.assign(testWallet, invalidWallet); + const success = testWallet.revertBlock(block); + + expect(success).toBeFalse(); + for (const key of Object.keys(invalidWallet)) { + expect(testWallet[key]).toBe(invalidWallet[key]); + } + }); + }); + + describe("calling transactionHandler canApply, applyTransaction, revertTransaction", () => { + const mockBackup = { + canApply: transactionHandler.canApply, + applyTransactionToSender: transactionHandler.applyTransactionToSender, + revertTransactionForSender: transactionHandler.revertTransactionForSender, + applyTransactionToRecipient: transactionHandler.applyTransactionToRecipient, + revertTransactionForRecipient: transactionHandler.revertTransactionForRecipient, + }; + let testWallet; + + beforeEach(() => { + Object.assign(transactionHandler, mockBackup); + testWallet = new Wallet("D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7"); + }); + + afterAll(() => { + Object.assign(transactionHandler, mockBackup); + }); + + it.each(Object.keys(mockBackup))("should call transactionHandler %s with correct parameters", fn => { + transactionHandler[fn] = jest.fn(); + const transaction = { id: 123456 }; + const params = fn === "canApply" ? [transaction, []] : [transaction]; + testWallet[fn](...params); + expect(transactionHandler[fn]).toHaveBeenCalledWith(testWallet, ...params); + }); + }); + + describe("audit transaction - auditApply", () => { + const walletInit = { + balance: new Bignum(1000 * ARKTOSHI), + forgedFees: new Bignum(10 * ARKTOSHI), + forgedRewards: new Bignum(50 * ARKTOSHI), + producedBlocks: 1, + dirty: false, + lastBlock: { id: 1234856 }, + publicKey: "02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece", + address: "D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7", + }; + let testWallet; + + const generateTransactionType = (type, asset = {}) => { + // use 2nd signature as a base + const transaction = generateSecondSignature("devnet", "super secret passphrase", 1, true)[0]; + return Object.assign(transaction, { type, asset }); + }; + + beforeEach(() => { + testWallet = new Wallet(walletInit.address); + testWallet = Object.assign(testWallet, walletInit); + }); + + it("should return correct audit data for Transfer type", () => { + const transaction = generateTransfers( + "devnet", + "super secret passphrase", + "D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7", + ARKTOSHI, + 1, + true, + )[0]; + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": true }, + { Transfer: true }, + ]); + }); + + it("should return correct audit data for 2nd signature type", () => { + const transaction = generateSecondSignature("devnet", "super secret passphrase", 1, true)[0]; + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": true }, + { "Second public key": null }, + ]); + }); + + it("should return correct audit data for delegate registration type", () => { + const transaction = generateDelegateRegistration("devnet", "super secret passphrase", 1, true)[0]; + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": true }, + { "Current username": null }, + { "New username": transaction.asset.delegate.username }, + ]); + }); + + it("should return correct audit data for vote type", () => { + const transaction = generateVote( + "devnet", + "super secret passphrase", + "02337316a26d8d49ec27059bd0589c49ba474029c3627715380f4df83fb431aece", + 1, + true, + )[0]; + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": true }, + { "Current vote": null }, + { "New vote": transaction.asset.votes[0] }, + ]); + }); + + it("should return correct audit data for multisignature type", () => { + const asset = { + multisignature: { + keysgroup: ["first", "second", "third"], + min: 2, + lifetime: 1000, + }, + }; + const transaction = generateTransactionType(TransactionTypes.MultiSignature, asset); + transaction.signatures = []; + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": false }, + { "Multisignature not yet registered": true }, + { "Multisignature enough keys": true }, + { "Multisignature all keys signed": false }, + { "Multisignature verification": false }, + ]); + }); + + it("should return correct audit data for ipfs type", () => { + const transaction = generateTransactionType(TransactionTypes.Ipfs); + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": false }, + { IPFS: true }, + ]); + }); + + it("should return correct audit data for timelock type", () => { + const transaction = generateTransactionType(TransactionTypes.TimelockTransfer); + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": false }, + { Timelock: true }, + ]); + }); + + it("should return correct audit data for multipayment type", () => { + const asset = { + payments: [{ amount: new Bignum(10) }, { amount: new Bignum(20) }], + }; + const transaction = generateTransactionType(TransactionTypes.MultiPayment, asset); + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": false }, + { "Multipayment remaining amount": new Bignum(30) }, + ]); + }); + + it("should return correct audit data for delegate resignation type", () => { + const transaction = generateTransactionType(TransactionTypes.DelegateResignation); + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": false }, + { "Resignate Delegate": null }, + ]); + }); + + it("should return correct audit data for unknown type", () => { + const transaction = generateTransactionType(99); + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": false }, + { "Unknown Type": true }, + ]); + }); + + describe("when wallet has multisignature", () => { + it("should return correct audit data for Transfer type", () => { + const transaction = generateTransfers( + "devnet", + "super secret passphrase", + "D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7", + ARKTOSHI, + 1, + true, + )[0]; + testWallet.multisignature = { + keysgroup: [ + "+02db1d199f20038e569500895b3521a453b2924e4a07c75aa9f7bf2aa4ad71392d", + "+02a7442df1f6cbef57d84c9c0eff248f9af48370384987de90bdcebd000feccdb6", + "+037a9458c87080768f79c4320941fdc64c9fe580673f17358125b93e80bd0b1d27", + ], + min: 2, + }; + transaction.signatures = [ + "3044022022fb3b1d48d9e4905ab566949d637f0832dd0ab6f2cb67a620496e23e83a86d902203182ad967d22db258f97f9fab6d3856c29738ae745eb2f40eb5d472722b794b9", + "3045022100aef482ecaea6ecaf8e6f86bd7ac474458e657614b3eb9e440789549d1ea85f6002205c75763411e0febb7d11a7ccf7cb826fc11ddbe3722b73f77e22e9f0919e179d", + "3045022100e1dff5c0a4289ffee8caa79fd25fe86f0ded4daaeb9f25e123ea327b01fdb9710220476da4d177652fe4a375e414089ce8c86800bcc4ca6ce0b6d974ef98d8c9d4cf", + ]; + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([{ Mutisignature: false }, { Transfer: true }]); + }); + }); + + describe("when wallet has 2nd public key", () => { + it("should return correct audit data for Transfer type", () => { + const transaction = generateTransfers( + "devnet", + { + passphrase: "super secret passphrase", + secondPassphrase: "super secret secondpassphrase", + }, + "D61xc3yoBQDitwjqUspMPx1ooET6r1XLt7", + ARKTOSHI, + 1, + true, + )[0]; + testWallet.secondPublicKey = "02db1d199f20038e569500895b3521a453b2924e4a07c75aa9f7bf2aa4ad71392d"; + + const audit = testWallet.auditApply(transaction); + + expect(audit).toEqual([ + { + "Remaining amount": +walletInit.balance.minus(transaction.amount).minus(transaction.fee), + }, + { "Signature validation": true }, + { "Second Signature Verification": false }, + { Transfer: true }, + ]); + }); + }); + }); +}); diff --git a/packages/crypto/__tests__/utils/format-arktoshi.test.js b/packages/crypto/__tests__/utils/format-arktoshi.test.js deleted file mode 100644 index 89d96d7e9a..0000000000 --- a/packages/crypto/__tests__/utils/format-arktoshi.test.js +++ /dev/null @@ -1,12 +0,0 @@ -const { Bignum, formatArktoshi } = require('../../lib/utils') -const { ARKTOSHI } = require('../../lib/constants') - -describe('Format Arktoshi', () => { - it('should format arktoshis', () => { - expect(formatArktoshi(ARKTOSHI)).toBe('1 DѦ') - expect(formatArktoshi(0.1 * ARKTOSHI)).toBe('0.1 DѦ') - expect(formatArktoshi((0.1 * ARKTOSHI).toString())).toBe('0.1 DѦ') - expect(formatArktoshi(new Bignum(10))).toBe('0.0000001 DѦ') - expect(formatArktoshi(new Bignum(ARKTOSHI + 10012))).toBe('1.00010012 DѦ') - }) -}) diff --git a/packages/crypto/__tests__/utils/format-arktoshi.test.ts b/packages/crypto/__tests__/utils/format-arktoshi.test.ts new file mode 100644 index 0000000000..6ccdfe4ca8 --- /dev/null +++ b/packages/crypto/__tests__/utils/format-arktoshi.test.ts @@ -0,0 +1,14 @@ +import "jest-extended"; + +import { ARKTOSHI } from "../../src/constants"; +import { Bignum, formatArktoshi } from "../../src/utils"; + +describe("Format Arktoshi", () => { + it("should format arktoshis", () => { + expect(formatArktoshi(ARKTOSHI)).toBe("1 DѦ"); + expect(formatArktoshi(0.1 * ARKTOSHI)).toBe("0.1 DѦ"); + expect(formatArktoshi((0.1 * ARKTOSHI).toString())).toBe("0.1 DѦ"); + expect(formatArktoshi(new Bignum(10))).toBe("0.0000001 DѦ"); + expect(formatArktoshi(new Bignum(ARKTOSHI + 10012))).toBe("1.00010012 DѦ"); + }); +}); diff --git a/packages/crypto/__tests__/utils/is-exception.test.ts b/packages/crypto/__tests__/utils/is-exception.test.ts new file mode 100644 index 0000000000..f367d6a761 --- /dev/null +++ b/packages/crypto/__tests__/utils/is-exception.test.ts @@ -0,0 +1,23 @@ +import "jest-extended"; + +import { configManager } from "../../src/managers"; +import { IBlockData } from "../../src/models"; +import { isException } from "../../src/utils"; + +describe("IsException", () => { + it("should return true", () => { + configManager.get = jest.fn(() => ["1"]); + expect(isException({ id: "1" } as IBlockData)).toBeTrue(); + }); + + it("should return false", () => { + configManager.get = jest.fn(() => ["1"]); + expect(isException({ id: "2" } as IBlockData)).toBeFalse(); + + configManager.get = jest.fn(() => undefined); + expect(isException({ id: "2" } as IBlockData)).toBeFalse(); + + configManager.get = jest.fn(() => undefined); + expect(isException({ id: undefined } as IBlockData)).toBeFalse(); + }); +}); diff --git a/packages/crypto/__tests__/utils/message.test.js b/packages/crypto/__tests__/utils/message.test.js deleted file mode 100644 index 175d504e36..0000000000 --- a/packages/crypto/__tests__/utils/message.test.js +++ /dev/null @@ -1,39 +0,0 @@ -const { Message } = require('../../lib/crypto') - -const fixture = { - data: { - publicKey: - '034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192', - signature: - '304402200fb4adddd1f1d652b544ea6ab62828a0a65b712ed447e2538db0caebfa68929e02205ecb2e1c63b29879c2ecf1255db506d671c8b3fa6017f67cfd1bf07e6edd1cc8', - message: 'Hello World', - }, - passphrase: 'this is a top secret passphrase', -} - -describe('Message', () => { - describe('sign', () => { - it('should be a function', () => { - expect(Message.sign).toBeFunction() - }) - - it('should be ok', () => { - const actual = Message.sign(fixture.data.message, fixture.passphrase) - - expect(actual).toHaveProperty('publicKey') - expect(actual).toHaveProperty('signature') - expect(actual).toHaveProperty('message') - expect(Message.verify(actual)).toBeTrue() - }) - }) - - describe('verify', () => { - it('should be a function', () => { - expect(Message.verify).toBeFunction() - }) - - it('should be ok', () => { - expect(Message.verify(fixture.data)).toBeTrue() - }) - }) -}) diff --git a/packages/crypto/__tests__/utils/message.test.ts b/packages/crypto/__tests__/utils/message.test.ts new file mode 100644 index 0000000000..328dfec9fb --- /dev/null +++ b/packages/crypto/__tests__/utils/message.test.ts @@ -0,0 +1,32 @@ +import "jest-extended"; + +import { Message } from "../../src/crypto"; + +const fixture = { + data: { + publicKey: "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192", + signature: + "304402200fb4adddd1f1d652b544ea6ab62828a0a65b712ed447e2538db0caebfa68929e02205ecb2e1c63b29879c2ecf1255db506d671c8b3fa6017f67cfd1bf07e6edd1cc8", + message: "Hello World", + }, + passphrase: "this is a top secret passphrase", +}; + +describe("Message", () => { + describe("sign", () => { + it("should be ok", () => { + const actual = Message.sign(fixture.data.message, fixture.passphrase); + + expect(actual).toHaveProperty("publicKey"); + expect(actual).toHaveProperty("signature"); + expect(actual).toHaveProperty("message"); + expect(Message.verify(actual)).toBeTrue(); + }); + }); + + describe("verify", () => { + it("should be ok", () => { + expect(Message.verify(fixture.data)).toBeTrue(); + }); + }); +}); diff --git a/packages/crypto/__tests__/utils/network-list.js b/packages/crypto/__tests__/utils/network-list.js deleted file mode 100644 index e598ed993e..0000000000 --- a/packages/crypto/__tests__/utils/network-list.js +++ /dev/null @@ -1,14 +0,0 @@ -const tg = require('tiny-glob/sync') -const path = require('path') - -const entries = tg('../../lib/networks/**/*.json', { cwd: __dirname }) - -const NETWORKS = {} -entries.forEach(file => { - NETWORKS[path.parse(file).name] = require(file) -}) - -const NETWORKS_LIST = [] -entries.forEach(file => NETWORKS_LIST.push(require(file))) - -module.exports = { NETWORKS, NETWORKS_LIST } diff --git a/packages/crypto/__tests__/utils/network-list.ts b/packages/crypto/__tests__/utils/network-list.ts new file mode 100644 index 0000000000..5eac96e291 --- /dev/null +++ b/packages/crypto/__tests__/utils/network-list.ts @@ -0,0 +1,16 @@ +import "jest-extended"; + +import { parse } from "path"; +import tg from "tiny-glob/sync"; + +const entries = tg("../../src/networks/**/*.json", { cwd: __dirname }); + +const NETWORKS = {}; +entries.forEach(file => { + NETWORKS[parse(file).name] = require(file); +}); + +const NETWORKS_LIST = []; +entries.forEach(file => NETWORKS_LIST.push(require(file))); + +module.exports = { NETWORKS, NETWORKS_LIST }; diff --git a/packages/crypto/__tests__/utils/sort-transactions.test.ts b/packages/crypto/__tests__/utils/sort-transactions.test.ts new file mode 100644 index 0000000000..0f04a2fe84 --- /dev/null +++ b/packages/crypto/__tests__/utils/sort-transactions.test.ts @@ -0,0 +1,47 @@ +import { ITransactionData } from "../../src/models"; +import { sortTransactions } from "../../src/utils"; + +describe("sortTransactions", () => { + it("should sort by type", () => { + const transactions = [{ type: 1, id: "10" }, { type: 2, id: "11" }, { type: 0, id: "12" }]; + + sortTransactions(transactions as ITransactionData[]); + expect(transactions[0].id).toBe("12"); + expect(transactions[1].id).toBe("10"); + expect(transactions[2].id).toBe("11"); + }); + + it("should sort by id", () => { + const transactions = [{ type: 0, id: "57" }, { type: 0, id: "21" }, { type: 0, id: "35" }]; + + sortTransactions(transactions as ITransactionData[]); + expect(transactions[0].id).toBe("21"); + expect(transactions[1].id).toBe("35"); + expect(transactions[2].id).toBe("57"); + }); + + it("should sort by type and id", () => { + const transactions = [ + { type: 2, id: "12" }, + { type: 0, id: "87" }, + { type: 3, id: "45" }, + { type: 1, id: "36" }, + { type: 1, id: "47" }, + { type: 3, id: "14" }, + { type: 2, id: "16" }, + { type: 3, id: "98" }, + { type: 1, id: "39" }, + ]; + + sortTransactions(transactions as ITransactionData[]); + expect(transactions[0].id).toBe("87"); + expect(transactions[1].id).toBe("36"); + expect(transactions[2].id).toBe("39"); + expect(transactions[3].id).toBe("47"); + expect(transactions[4].id).toBe("12"); + expect(transactions[5].id).toBe("16"); + expect(transactions[6].id).toBe("14"); + expect(transactions[7].id).toBe("45"); + expect(transactions[8].id).toBe("98"); + }); +}); diff --git a/packages/crypto/__tests__/validation/extensions/bignumber.test.ts b/packages/crypto/__tests__/validation/extensions/bignumber.test.ts new file mode 100644 index 0000000000..a2980b5774 --- /dev/null +++ b/packages/crypto/__tests__/validation/extensions/bignumber.test.ts @@ -0,0 +1,86 @@ +import BigNumber from "bignumber.js"; +import Joi from "joi"; +import { extensions } from "../../../src/validation/extensions"; + +const shouldPass = value => expect(value.error).toBeNull(); +const shouldFail = (value, message) => expect(value.error.details[0].message).toBe(`"value" ${message}`); + +const validator = Joi.extend(extensions); + +let bigNumber; + +beforeEach(() => { + bigNumber = new BigNumber(100); +}); + +describe("BigNumber validation extension", () => { + it("passes when validating if only the same number", () => { + shouldPass(validator.validate(bigNumber, validator.bignumber().only(100))); + }); + + it("fails when validating if only a different number", () => { + shouldFail(validator.validate(bigNumber, validator.bignumber().only(2)), "is different from allowed value"); + }); + + it("passes when validating if minimum a smaller or equal number", () => { + shouldPass(validator.validate(bigNumber, validator.bignumber().min(20))); + + shouldPass(validator.validate(bigNumber, validator.bignumber().min(100))); + }); + + describe("min", () => { + it("should pass", () => { + shouldPass(validator.validate(new BigNumber(1), validator.bignumber().min(1))); + }); + + it("should fail", () => { + shouldFail(validator.validate(new BigNumber(1), validator.bignumber().min(2)), "is less than minimum"); + }); + }); + + describe("max", () => { + it("should pass", () => { + shouldPass(validator.validate(new BigNumber(1), validator.bignumber().max(2))); + }); + + it("should fail", () => { + shouldFail(validator.validate(new BigNumber(2), validator.bignumber().max(1)), "is greater than maximum"); + }); + }); + + describe("only", () => { + it("should pass", () => { + shouldPass(validator.validate(new BigNumber(0), validator.bignumber().only(0))); + }); + + it("should fail", () => { + shouldFail( + validator.validate(new BigNumber(0), validator.bignumber().only(1)), + "is different from allowed value", + ); + }); + }); + + describe("integer", () => { + it("should pass", () => { + shouldPass(validator.validate(new BigNumber(1), validator.bignumber().integer())); + }); + + it("should fail", () => { + shouldFail( + validator.validate(new BigNumber(123.456), validator.bignumber().integer()), + "is not an integer", + ); + }); + }); + + describe("positive", () => { + it("should pass", () => { + shouldPass(validator.validate(new BigNumber(1), validator.bignumber().positive())); + }); + + it("should fail", () => { + shouldFail(validator.validate(new BigNumber(-1), validator.bignumber().positive()), "is not positive"); + }); + }); +}); diff --git a/packages/crypto/__tests__/validation/extensions/transactions/delegate-registration.test.js b/packages/crypto/__tests__/validation/extensions/transactions/delegate-registration.test.js deleted file mode 100644 index 32f504830d..0000000000 --- a/packages/crypto/__tests__/validation/extensions/transactions/delegate-registration.test.js +++ /dev/null @@ -1,109 +0,0 @@ -/* eslint no-empty: "off" */ - -const Joi = require('joi').extend( - require('../../../../lib/validation/extensions'), -) - -const { constants, transactionBuilder } = require('../../../../lib') - -let transaction -beforeEach(() => { - transaction = transactionBuilder.delegateRegistration() -}) - -describe('Delegate Registration Transaction', () => { - it('should be valid', () => { - transaction.usernameAsset('delegate1').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).toBeNull() - }) - - it('should be invalid due to no transaction as object', () => { - expect( - Joi.validate('test', Joi.arkDelegateRegistration()).error, - ).not.toBeNull() - }) - - it('should be invalid due to non-zero amount', () => { - transaction - .usernameAsset('delegate1') - .amount(10 * constants.ARKTOSHI) - .sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) - - it('should be invalid due to space in username', () => { - transaction.usernameAsset('test 123').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) - - it('should be invalid due to non-alphanumeric in username', () => { - transaction.usernameAsset('£££').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) - - it('should be invalid due to username too long', () => { - transaction.usernameAsset('1234567890123456789012345').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) - - it('should be invalid due to undefined username', () => { - try { - transaction.usernameAsset(undefined).sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - } catch (error) {} - }) - - it('should be invalid due to no username', () => { - transaction.usernameAsset('').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) - - it('should be invalid due to capitals in username', () => { - transaction.usernameAsset('I_AM_INVALID').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) - - it('should be invalid due to wrong transaction type', () => { - transaction = transactionBuilder.transfer() - transaction - .recipientId(null) - .amount(10 * constants.ARKTOSHI) - .sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkDelegateRegistration()) - .error, - ).not.toBeNull() - }) -}) diff --git a/packages/crypto/__tests__/validation/extensions/transactions/delegate-registration.test.ts b/packages/crypto/__tests__/validation/extensions/transactions/delegate-registration.test.ts new file mode 100644 index 0000000000..892481fa3f --- /dev/null +++ b/packages/crypto/__tests__/validation/extensions/transactions/delegate-registration.test.ts @@ -0,0 +1,80 @@ +// tslint:disable:no-empty + +import Joi from "joi"; +import { constants, transactionBuilder } from "../../../../src"; +import { extensions } from "../../../../src/validation/extensions"; + +const validator = Joi.extend(extensions); + +let transaction; +beforeEach(() => { + transaction = transactionBuilder.delegateRegistration(); +}); + +describe("Delegate Registration Transaction", () => { + it("should be valid", () => { + transaction.usernameAsset("delegate1").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).toBeNull(); + }); + + it("should be invalid due to no transaction as object", () => { + expect(validator.validate("test", validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to non-zero amount", () => { + transaction + .usernameAsset("delegate1") + .amount(10 * constants.ARKTOSHI) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to space in username", () => { + transaction.usernameAsset("test 123").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to non-alphanumeric in username", () => { + transaction.usernameAsset("£££").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to username too long", () => { + transaction.usernameAsset("1234567890123456789012345").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to undefined username", () => { + try { + transaction.usernameAsset(undefined).sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + } catch (error) {} + }); + + it("should be invalid due to no username", () => { + transaction.usernameAsset("").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to capitals in username", () => { + transaction.usernameAsset("I_AM_INVALID").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); + + it("should be invalid due to wrong transaction type", () => { + transaction = transactionBuilder.transfer(); + transaction + .recipientId(null) + .amount(10 * constants.ARKTOSHI) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.delegateRegistration()).error).not.toBeNull(); + }); +}); diff --git a/packages/crypto/__tests__/validation/extensions/transactions/multi-signature.test.js b/packages/crypto/__tests__/validation/extensions/transactions/multi-signature.test.js deleted file mode 100644 index 31dafdca5e..0000000000 --- a/packages/crypto/__tests__/validation/extensions/transactions/multi-signature.test.js +++ /dev/null @@ -1,215 +0,0 @@ -/* eslint no-empty: "off" */ - -const Joi = require('joi').extend( - require('../../../../lib/validation/extensions'), -) - -const { constants, crypto, transactionBuilder } = require('../../../../lib') - -const passphrase = 'passphrase 1' -const publicKey = - '+03e8021105a6c202097e97e6c6d650942d913099bf6c9f14a6815df1023dde3b87' -const passphrases = [passphrase, 'passphrase 2', 'passphrase 3'] -const keysGroup = [ - publicKey, - '+03dfdaaa7fd28bc9359874b7e33138f4d0afe9937e152c59b83a99fae7eeb94899', - '+03de72ef9d3ebf1b374f1214f5b8dde823690ab2aa32b4b8b3226cc568aaed1562', -] - -const signTransaction = (transaction, values) => { - values.map(value => transaction.multiSignatureSign(value)) -} - -let transaction -let multiSignatureAsset -beforeEach(() => { - transaction = transactionBuilder.multiSignature() - multiSignatureAsset = { - min: 1, - keysgroup: keysGroup, - lifetime: 72, - } -}) - -describe('Multi Signature Transaction', () => { - it('should be valid with min of 3', () => { - multiSignatureAsset.min = 3 - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).toBeNull() - }) - - it('should be valid with 3 public keys', () => { - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).toBeNull() - }) - - it('should be valid with lifetime of 10', () => { - multiSignatureAsset.lifetime = 10 - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).toBeNull() - }) - - it('should be invalid due to no transaction as object', () => { - expect(Joi.validate('test', Joi.arkMultiSignature()).error).not.toBeNull() - }) - - it('should be invalid due to non-zero amount', () => { - transaction - .multiSignatureAsset(multiSignatureAsset) - .amount(10 * constants.ARKTOSHI) - .sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to zero fee', () => { - transaction - .multiSignatureAsset(multiSignatureAsset) - .fee(0) - .sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to min too low', () => { - multiSignatureAsset.min = 0 - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to min too high', () => { - multiSignatureAsset.min = multiSignatureAsset.keysgroup.length + 1 - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to lifetime too low', () => { - multiSignatureAsset.lifetime = 0 - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to lifetime too high', () => { - multiSignatureAsset.lifetime = 100 - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to no public keys', () => { - multiSignatureAsset.keysgroup = [] - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to too many public keys', () => { - const values = [] - multiSignatureAsset.keysgroup = [] - for (let i = 0; i < 20; i++) { - const value = `passphrase ${i}` - values.push(value) - multiSignatureAsset.keysgroup.push(crypto.getKeys(value).publicKey) - } - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, values) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to duplicate public keys', () => { - multiSignatureAsset.keysgroup = [publicKey, publicKey] - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to no signatures', () => { - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to not enough signatures', () => { - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases.slice(1)) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to too many signatures', () => { - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, ['wrong passphrase', ...passphrases]) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to no "+" for publicKeys', () => { - multiSignatureAsset.keysgroup = keysGroup.map(value => value.slice(1)) - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to having "-" for publicKeys', () => { - multiSignatureAsset.keysgroup = keysGroup.map(value => `-${value.slice(1)}`) - transaction.multiSignatureAsset(multiSignatureAsset).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to wrong keysgroup type', () => { - try { - multiSignatureAsset.keysgroup = publicKey - transaction.multiSignatureAsset(publicKey).sign('passphrase') - signTransaction(transaction, passphrases) - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).error, - ).not.toBeNull() - } catch (error) {} - }) - - it('should be invalid due to wrong transaction type', () => { - transaction = transactionBuilder.delegateRegistration() - transaction.usernameAsset('delegate_name').sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkMultiSignature()).errors, - ).not.toBeNull() - }) -}) diff --git a/packages/crypto/__tests__/validation/extensions/transactions/multi-signature.test.ts b/packages/crypto/__tests__/validation/extensions/transactions/multi-signature.test.ts new file mode 100644 index 0000000000..8dbf7533c9 --- /dev/null +++ b/packages/crypto/__tests__/validation/extensions/transactions/multi-signature.test.ts @@ -0,0 +1,176 @@ +// tslint:disable:no-empty + +import Joi from "joi"; +import { constants, crypto, transactionBuilder } from "../../../../src"; +import { extensions } from "../../../../src/validation/extensions"; + +const validator = Joi.extend(extensions); + +const passphrase = "passphrase 1"; +const publicKey = "+03e8021105a6c202097e97e6c6d650942d913099bf6c9f14a6815df1023dde3b87"; +const passphrases = [passphrase, "passphrase 2", "passphrase 3"]; +const keysGroup = [ + publicKey, + "+03dfdaaa7fd28bc9359874b7e33138f4d0afe9937e152c59b83a99fae7eeb94899", + "+03de72ef9d3ebf1b374f1214f5b8dde823690ab2aa32b4b8b3226cc568aaed1562", +]; + +const signTransaction = (tx, values) => { + values.map(value => tx.multiSignatureSign(value)); +}; + +let transaction; +let multiSignatureAsset; +beforeEach(() => { + transaction = transactionBuilder.multiSignature(); + multiSignatureAsset = { + min: 1, + keysgroup: keysGroup, + lifetime: 72, + }; +}); + +describe("Multi Signature Transaction", () => { + it("should be valid with min of 3", () => { + multiSignatureAsset.min = 3; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).toBeNull(); + }); + + it("should be valid with 3 public keys", () => { + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).toBeNull(); + }); + + it("should be valid with lifetime of 10", () => { + multiSignatureAsset.lifetime = 10; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).toBeNull(); + }); + + it("should be invalid due to no transaction as object", () => { + expect(validator.validate("test", validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to non-zero amount", () => { + transaction + .multiSignatureAsset(multiSignatureAsset) + .amount(10 * constants.ARKTOSHI) + .sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to zero fee", () => { + transaction + .multiSignatureAsset(multiSignatureAsset) + .fee(0) + .sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to min too low", () => { + multiSignatureAsset.min = 0; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to min too high", () => { + multiSignatureAsset.min = multiSignatureAsset.keysgroup.length + 1; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to lifetime too low", () => { + multiSignatureAsset.lifetime = 0; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to lifetime too high", () => { + multiSignatureAsset.lifetime = 100; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to no public keys", () => { + multiSignatureAsset.keysgroup = []; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to too many public keys", () => { + const values = []; + multiSignatureAsset.keysgroup = []; + for (let i = 0; i < 20; i++) { + const value = `passphrase ${i}`; + values.push(value); + multiSignatureAsset.keysgroup.push(crypto.getKeys(value).publicKey); + } + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, values); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to duplicate public keys", () => { + multiSignatureAsset.keysgroup = [publicKey, publicKey]; + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to no signatures", () => { + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to not enough signatures", () => { + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases.slice(1)); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to too many signatures", () => { + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, ["wrong passphrase", ...passphrases]); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it('should be invalid due to no "+" for publicKeys', () => { + multiSignatureAsset.keysgroup = keysGroup.map(value => value.slice(1)); + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it('should be invalid due to having "-" for publicKeys', () => { + multiSignatureAsset.keysgroup = keysGroup.map(value => `-${value.slice(1)}`); + transaction.multiSignatureAsset(multiSignatureAsset).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to wrong keysgroup type", () => { + try { + multiSignatureAsset.keysgroup = publicKey; + transaction.multiSignatureAsset(publicKey).sign("passphrase"); + signTransaction(transaction, passphrases); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).error).not.toBeNull(); + } catch (error) {} + }); + + it("should be invalid due to wrong transaction type", () => { + transaction = transactionBuilder.delegateRegistration(); + transaction.usernameAsset("delegate_name").sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.multiSignature()).errors).not.toBeNull(); + }); +}); diff --git a/packages/crypto/__tests__/validation/extensions/transactions/second-signature.test.js b/packages/crypto/__tests__/validation/extensions/transactions/second-signature.test.js deleted file mode 100644 index 81a6cb7962..0000000000 --- a/packages/crypto/__tests__/validation/extensions/transactions/second-signature.test.js +++ /dev/null @@ -1,74 +0,0 @@ -const Joi = require('joi').extend( - require('../../../../lib/validation/extensions'), -) - -const { constants, transactionBuilder } = require('../../../../lib') - -let transaction -beforeEach(() => { - transaction = transactionBuilder.secondSignature() -}) - -// NOTE: some tests aren't strictly about the second signature - -describe('Second Signature Transaction', () => { - it('should be valid', () => { - transaction.signatureAsset('second passphrase').sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkSecondSignature()).error, - ).toBeNull() - }) - - it('should be valid with correct data', () => { - transaction - .signatureAsset('second passphrase') - .fee(1 * constants.ARKTOSHI) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkSecondSignature()).error, - ).toBeNull() - }) - - it('should be invalid due to no transaction as object', () => { - expect(Joi.validate('test', Joi.arkSecondSignature()).error).not.toBeNull() - }) - - it('should be invalid due to non-zero amount', () => { - transaction - .signatureAsset('second passphrase') - .amount(10 * constants.ARKTOSHI) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkSecondSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to zero fee', () => { - transaction - .signatureAsset('second passphrase') - .fee(0) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkSecondSignature()).error, - ).not.toBeNull() - }) - - it('should be invalid due to second signature', () => { - transaction - .signatureAsset('second passphrase') - .fee(1) - .sign('passphrase') - .secondSign('second passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkSecondSignature()), - ).not.toBeNull() - }) - - it('should be invalid due to wrong transaction type', () => { - transaction = transactionBuilder.delegateRegistration() - transaction.usernameAsset('delegate_name').sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkSecondSignature()).error, - ).not.toBeNull() - }) -}) diff --git a/packages/crypto/__tests__/validation/extensions/transactions/second-signature.test.ts b/packages/crypto/__tests__/validation/extensions/transactions/second-signature.test.ts new file mode 100644 index 0000000000..d5e4d79fd2 --- /dev/null +++ b/packages/crypto/__tests__/validation/extensions/transactions/second-signature.test.ts @@ -0,0 +1,62 @@ +import Joi from "joi"; +import { constants, transactionBuilder } from "../../../../src"; +import { extensions } from "../../../../src/validation/extensions"; + +const validator = Joi.extend(extensions); + +let transaction; +beforeEach(() => { + transaction = transactionBuilder.secondSignature(); +}); + +// NOTE: some tests aren't strictly about the second signature + +describe("Second Signature Transaction", () => { + it("should be valid", () => { + transaction.signatureAsset("second passphrase").sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.secondSignature()).error).toBeNull(); + }); + + it("should be valid with correct data", () => { + transaction + .signatureAsset("second passphrase") + .fee(1 * constants.ARKTOSHI) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.secondSignature()).error).toBeNull(); + }); + + it("should be invalid due to no transaction as object", () => { + expect(validator.validate("test", validator.secondSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to non-zero amount", () => { + transaction + .signatureAsset("second passphrase") + .amount(10 * constants.ARKTOSHI) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.secondSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to zero fee", () => { + transaction + .signatureAsset("second passphrase") + .fee(0) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.secondSignature()).error).not.toBeNull(); + }); + + it("should be invalid due to second signature", () => { + transaction + .signatureAsset("second passphrase") + .fee(1) + .sign("passphrase") + .secondSign("second passphrase"); + expect(validator.validate(transaction.getStruct(), validator.secondSignature())).not.toBeNull(); + }); + + it("should be invalid due to wrong transaction type", () => { + transaction = transactionBuilder.delegateRegistration(); + transaction.usernameAsset("delegate_name").sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.secondSignature()).error).not.toBeNull(); + }); +}); diff --git a/packages/crypto/__tests__/validation/extensions/transactions/transfer.test.js b/packages/crypto/__tests__/validation/extensions/transactions/transfer.test.js deleted file mode 100644 index 1e0883aaeb..0000000000 --- a/packages/crypto/__tests__/validation/extensions/transactions/transfer.test.js +++ /dev/null @@ -1,135 +0,0 @@ -const Joi = require('joi').extend( - require('../../../../lib/validation/extensions'), -) - -const { constants, transactionBuilder } = require('../../../../lib') - -const address = 'APnDzjtDb1FthuqcLMeL5XMWb1uD1KeMGi' -const fee = 1 * constants.ARKTOSHI -const amount = 10 * constants.ARKTOSHI - -let transaction -beforeEach(() => { - transaction = transactionBuilder.transfer() -}) - -describe('Transfer Transaction', () => { - it('should be valid', () => { - transaction - .recipientId(address) - .amount(amount) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).toBeNull() - }) - - it('should be valid with correct data', () => { - transaction - .recipientId(address) - .amount(amount) - .fee(fee) - .vendorField('Ahoy') - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).toBeNull() - }) - - it('should be valid with up to 64 bytes in vendor field', () => { - transaction - .recipientId(address) - .amount(amount) - .fee(fee) - .vendorField('a'.repeat(64)) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).toBeNull() - - transaction - .recipientId(address) - .amount(amount) - .fee(fee) - .vendorField('⊁'.repeat(21)) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).toBeNull() - }) - - it('should be invalid with more than 64 bytes in vendor field', () => { - transaction - .recipientId(address) - .amount(amount) - .fee(fee) - .vendorField('a'.repeat(65)) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).not.toBeNull() - - transaction - .recipientId(address) - .amount(amount) - .fee(fee) - .vendorField('⊁'.repeat(22)) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).not.toBeNull() - }) - - it('should be invalid due to no transaction as object', () => { - expect(Joi.validate('test', Joi.arkTransfer()).error).not.toBeNull() - }) - - it('should be invalid due to no address', () => { - transaction - .recipientId(null) - .amount(amount) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).not.toBeNull() - }) - - it('should be invalid due to invalid address', () => { - transaction - .recipientId(address) - .amount(amount) - .sign('passphrase') - const struct = transaction.getStruct() - struct.recipientId = 'woop' - expect(Joi.validate(struct, Joi.arkTransfer()).error).not.toBeNull() - }) - - it('should be invalid due to zero amount', () => { - transaction - .recipientId(address) - .amount(0) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).not.toBeNull() - }) - - it('should be invalid due to zero fee', () => { - transaction - .recipientId(address) - .amount(0) - .fee(0) - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).not.toBeNull() - }) - - it('should be invalid due to wrong transaction type', () => { - transaction = transactionBuilder.delegateRegistration() - transaction.usernameAsset('delegate_name').sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkTransfer()).error, - ).not.toBeNull() - }) -}) diff --git a/packages/crypto/__tests__/validation/extensions/transactions/transfer.test.ts b/packages/crypto/__tests__/validation/extensions/transactions/transfer.test.ts new file mode 100644 index 0000000000..e974467c7a --- /dev/null +++ b/packages/crypto/__tests__/validation/extensions/transactions/transfer.test.ts @@ -0,0 +1,181 @@ +import Joi from "joi"; +import { configManager, constants, transactionBuilder } from "../../../../src"; +import { extensions } from "../../../../src/validation/extensions"; + +const validator = Joi.extend(extensions); + +const address = "APnDzjtDb1FthuqcLMeL5XMWb1uD1KeMGi"; +const fee = 1 * constants.ARKTOSHI; +const amount = 10 * constants.ARKTOSHI; + +let transaction; +beforeEach(() => { + transaction = transactionBuilder.transfer(); +}); + +describe("Transfer Transaction", () => { + it("should be valid", () => { + transaction + .recipientId(address) + .amount(amount) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).toBeNull(); + }); + + it("should be valid with correct data", () => { + transaction + .recipientId(address) + .amount(amount) + .fee(fee) + .vendorField("Ahoy") + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).toBeNull(); + }); + + it("should be valid with up to 64 bytes in vendor field", () => { + transaction + .recipientId(address) + .amount(amount) + .fee(fee) + .vendorField("a".repeat(64)) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).toBeNull(); + + transaction + .recipientId(address) + .amount(amount) + .fee(fee) + .vendorField("⊁".repeat(21)) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).toBeNull(); + }); + + it("should be invalid with more than 64 bytes in vendor field", () => { + transaction + .recipientId(address) + .amount(amount) + .fee(fee); + + // Bypass vendorfield check by manually assigning a vendorfield > 64 bytes + transaction.data.vendorField = "a".repeat(65); + transaction.sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + + transaction + .recipientId(address) + .amount(amount) + .fee(fee); + + // Bypass vendorfield check by manually assigning a vendorfield > 64 bytes + transaction.vendorField("⊁".repeat(22)); + transaction.sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + }); + + it("should be invalid due to no transaction as object", () => { + expect(validator.validate("test", validator.transfer()).error).not.toBeNull(); + }); + + it("should be invalid due to no address", () => { + transaction + .recipientId(null) + .amount(amount) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + }); + + it("should be invalid due to invalid address", () => { + transaction + .recipientId(address) + .amount(amount) + .sign("passphrase"); + const struct = transaction.getStruct(); + struct.recipientId = "woop"; + expect(validator.validate(struct, validator.transfer()).error).not.toBeNull(); + }); + + it("should be invalid due to zero amount", () => { + transaction + .recipientId(address) + .amount(0) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + }); + + it("should be invalid due to zero fee", () => { + transaction + .recipientId(address) + .amount(1) + .fee(0) + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + }); + + it("should be invalid due to wrong transaction type", () => { + transaction = transactionBuilder.delegateRegistration(); + transaction.usernameAsset("delegate_name").sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + }); + + it("should be valid due to missing network byte", () => { + transaction + .recipientId(address) + .amount(1) + .fee(1) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).toBeNull(); + }); + + it("should be valid due to correct network byte", () => { + transaction + .recipientId(address) + .amount(1) + .fee(1) + .network(configManager.get("pubKeyHash")) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).toBeNull(); + }); + + it("should be invalid due to wrong network byte", () => { + transaction + .recipientId(address) + .amount(1) + .fee(1) + .network(1) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.transfer()).error).not.toBeNull(); + }); + + it("should be valid after a network change", () => { + configManager.setFromPreset("devnet"); + + let transfer = transaction + .recipientId(address) + .amount(1) + .fee(1) + .network(configManager.get("pubKeyHash")) + .sign("passphrase") + .build(); + + expect(transfer.data.network).toBe(30); + expect(validator.validate(transfer.data, validator.transfer()).error).toBeNull(); + + configManager.setFromPreset("mainnet"); + + transfer = transaction + .recipientId(address) + .amount(1) + .fee(1) + .network(configManager.get("pubKeyHash")) + .sign("passphrase") + .build(); + + expect(transfer.data.network).toBe(23); + expect(validator.validate(transfer.data, validator.transfer()).error).toBeNull(); + }); +}); diff --git a/packages/crypto/__tests__/validation/extensions/transactions/vote.test.js b/packages/crypto/__tests__/validation/extensions/transactions/vote.test.js deleted file mode 100644 index a677916dc7..0000000000 --- a/packages/crypto/__tests__/validation/extensions/transactions/vote.test.js +++ /dev/null @@ -1,115 +0,0 @@ -/* eslint no-empty: "off" */ - -const Joi = require('joi').extend( - require('../../../../lib/validation/extensions'), -) - -const { constants, transactionBuilder } = require('../../../../lib') - -const vote = - '+02bcfa0951a92e7876db1fb71996a853b57f996972ed059a950d910f7d541706c9' -const unvote = - '-0326580718fc86ba609799ac95fcd2721af259beb5afa81bfce0ab7d9fe95de991' -const votes = [ - vote, - '+0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - unvote, -] -const invalidVotes = [ - '02bcfa0951a92e7876db1fb71996a853b57f996972ed059a950d910f7d541706c9', - '0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0', - '0326580718fc86ba609799ac95fcd2721af259beb5afa81bfce0ab7d9fe95de991', -] - -let transaction -beforeEach(() => { - transaction = transactionBuilder.vote() -}) - -describe('Vote Transaction', () => { - it('should be valid with 1 vote', () => { - transaction - .votesAsset([vote]) - - .sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).toBeNull() - }) - - it('should be valid with 1 unvote', () => { - transaction.votesAsset([unvote]).sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).toBeNull() - }) - - it('should be invalid due to no transaction as object', () => { - expect(Joi.validate('test', Joi.arkVote()).error).not.toBeNull() - }) - - it('should be invalid due to non-zero amount', () => { - transaction - .votesAsset([vote]) - .amount(10 * constants.ARKTOSHI) - .sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - }) - - it('should be invalid due to zero fee', () => { - transaction - .votesAsset(votes) - .fee(0) - .sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - }) - - it('should be invalid due to no votes', () => { - transaction.votesAsset([]).sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - }) - - it('should be invalid due to more than 1 vote', () => { - transaction.votesAsset(votes).sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - }) - - it('should be invalid due to invalid votes', () => { - transaction.votesAsset(invalidVotes).sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - }) - - it('should be invalid due to wrong vote type', () => { - try { - transaction.votesAsset(vote).sign('passphrase') - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - } catch (error) {} - }) - - it('should be invalid due to wrong transaction type', () => { - transaction = transactionBuilder.delegateRegistration() - transaction.usernameAsset('delegate_name').sign('passphrase') - - expect( - Joi.validate(transaction.getStruct(), Joi.arkVote()).error, - ).not.toBeNull() - }) -}) diff --git a/packages/crypto/__tests__/validation/extensions/transactions/vote.test.ts b/packages/crypto/__tests__/validation/extensions/transactions/vote.test.ts new file mode 100644 index 0000000000..4128774b3a --- /dev/null +++ b/packages/crypto/__tests__/validation/extensions/transactions/vote.test.ts @@ -0,0 +1,91 @@ +// tslint:disable:no-empty + +import Joi from "joi"; +import { constants, transactionBuilder } from "../../../../src"; +import { extensions } from "../../../../src/validation/extensions"; + +const validator = Joi.extend(extensions); + +const vote = "+02bcfa0951a92e7876db1fb71996a853b57f996972ed059a950d910f7d541706c9"; +const unvote = "-0326580718fc86ba609799ac95fcd2721af259beb5afa81bfce0ab7d9fe95de991"; +const votes = [vote, "+0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", unvote]; +const invalidVotes = [ + "02bcfa0951a92e7876db1fb71996a853b57f996972ed059a950d910f7d541706c9", + "0310ad026647eed112d1a46145eed58b8c19c67c505a67f1199361a511ce7860c0", + "0326580718fc86ba609799ac95fcd2721af259beb5afa81bfce0ab7d9fe95de991", +]; + +let transaction; +beforeEach(() => { + transaction = transactionBuilder.vote(); +}); + +describe("Vote Transaction", () => { + it("should be valid with 1 vote", () => { + transaction + .votesAsset([vote]) + + .sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.vote()).error).toBeNull(); + }); + + it("should be valid with 1 unvote", () => { + transaction.votesAsset([unvote]).sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).toBeNull(); + }); + + it("should be invalid due to no transaction as object", () => { + expect(validator.validate("test", validator.vote()).error).not.toBeNull(); + }); + + it("should be invalid due to non-zero amount", () => { + transaction + .votesAsset([vote]) + .amount(10 * constants.ARKTOSHI) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + }); + + it("should be invalid due to zero fee", () => { + transaction + .votesAsset(votes) + .fee(0) + .sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + }); + + it("should be invalid due to no votes", () => { + transaction.votesAsset([]).sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + }); + + it("should be invalid due to more than 1 vote", () => { + transaction.votesAsset(votes).sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + }); + + it("should be invalid due to invalid votes", () => { + transaction.votesAsset(invalidVotes).sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + }); + + it("should be invalid due to wrong vote type", () => { + try { + transaction.votesAsset(vote).sign("passphrase"); + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + } catch (error) {} + }); + + it("should be invalid due to wrong transaction type", () => { + transaction = transactionBuilder.delegateRegistration(); + transaction.usernameAsset("delegate_name").sign("passphrase"); + + expect(validator.validate(transaction.getStruct(), validator.vote()).error).not.toBeNull(); + }); +}); diff --git a/packages/crypto/__tests__/validation/rules/address.test.js b/packages/crypto/__tests__/validation/rules/address.test.js deleted file mode 100644 index f66a674e25..0000000000 --- a/packages/crypto/__tests__/validation/rules/address.test.js +++ /dev/null @@ -1,15 +0,0 @@ -const rule = require('../../../lib/validation/rules/address') - -describe('Address Rule', () => { - it('should be a function', () => { - expect(rule).toBeFunction() - }) - - it('should be true', () => { - expect(rule('DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN').passes).toBeTrue() - }) - - it('should be false', () => { - expect(rule('_DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN_').passes).toBeFalse() - }) -}) diff --git a/packages/crypto/__tests__/validation/rules/public-key.test.js b/packages/crypto/__tests__/validation/rules/public-key.test.js deleted file mode 100644 index a6345a539a..0000000000 --- a/packages/crypto/__tests__/validation/rules/public-key.test.js +++ /dev/null @@ -1,22 +0,0 @@ -const rule = require('../../../lib/validation/rules/public-key') - -describe('Public Key Rule', () => { - it('should be a function', () => { - expect(rule).toBeFunction() - }) - - it('should be true', () => { - expect( - rule('022cca9529ec97a772156c152a00aad155ee6708243e65c9d211a589cb5d43234d') - .passes, - ).toBeTrue() - }) - - it('should be false', () => { - expect( - rule( - '_022cca9529ec97a772156c152a00aad155ee6708243e65c9d211a589cb5d43234d_', - ).passes, - ).toBeFalse() - }) -}) diff --git a/packages/crypto/__tests__/validation/rules/username.test.js b/packages/crypto/__tests__/validation/rules/username.test.js deleted file mode 100644 index 6ce8279e72..0000000000 --- a/packages/crypto/__tests__/validation/rules/username.test.js +++ /dev/null @@ -1,15 +0,0 @@ -const rule = require('../../../lib/validation/rules/username') - -describe('Username Rule', () => { - it('should be a function', () => { - expect(rule).toBeFunction() - }) - - it('should be true', () => { - expect(rule('boldninja').passes).toBeTrue() - }) - - it('should be false', () => { - expect(rule('bold ninja').passes).toBeFalse() - }) -}) diff --git a/packages/crypto/__tests__/validation/transaction-validator.test.js b/packages/crypto/__tests__/validation/transaction-validator.test.js deleted file mode 100644 index d3cf9d2e14..0000000000 --- a/packages/crypto/__tests__/validation/transaction-validator.test.js +++ /dev/null @@ -1,11 +0,0 @@ -const { transactionValidator } = require('../../lib/validation') - -describe('Validators - Transaction', () => { - it('should be instantiated', () => { - expect(transactionValidator.constructor.name).toBe('TransactionValidator') - }) - - it('should have validate function', () => { - expect(transactionValidator.validate).toBeFunction() - }) -}) diff --git a/packages/crypto/__tests__/validation/validator.test.js b/packages/crypto/__tests__/validation/validator.test.js deleted file mode 100755 index 644c7af9ea..0000000000 --- a/packages/crypto/__tests__/validation/validator.test.js +++ /dev/null @@ -1,203 +0,0 @@ -const Joi = require('joi') - -let validator - -beforeEach(() => { - validator = require('../../lib/validation').validator -}) - -describe('Validator', () => { - describe('validate', () => { - it('should be a function', () => { - expect(validator.validate).toBeFunction() - }) - }) - - describe('passes', () => { - it('should be a function', () => { - expect(validator.passes).toBeFunction() - }) - - it('should be true', () => { - validator.results = { - passes: true, - } - - expect(validator.passes()).toBeTrue() - }) - - it('should be false', () => { - validator.results = { - passes: false, - } - - expect(validator.passes()).toBeFalse() - }) - }) - - describe('fails', () => { - it('should be a function', () => { - expect(validator.fails).toBeFunction() - }) - - it('should be true', () => { - validator.results = { - fails: true, - } - - expect(validator.fails()).toBeTrue() - }) - - it('should be false', () => { - validator.results = { - fails: false, - } - - expect(validator.fails()).toBeFalse() - }) - }) - - describe('validated', () => { - it('should be a function', () => { - expect(validator.validated).toBeFunction() - }) - - it('should be true', () => { - validator.results = { - data: { - key: 'value', - }, - } - - expect(validator.validated()).toHaveProperty('key', 'value') - }) - - it('should be false', () => { - validator.results = { - data: { - invalidKey: 'value', - }, - } - - expect(validator.validated()).not.toHaveProperty('key', 'value') - }) - }) - - describe('extend', () => { - it('should be a function', () => { - expect(validator.extend).toBeFunction() - }) - - it('should add the given method', () => { - expect(validator.rules).not.toHaveProperty('fake') - - validator.extend('fake', 'news') - - expect(validator.rules).toHaveProperty('fake') - }) - }) - - describe('__validateWithRule', () => { - it('should be a function', () => { - expect(validator.__validateWithRule).toBeFunction() - }) - - it('should be true', () => { - validator.__validateWithRule( - 'DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN', - 'address', - ) - - expect(validator.passes()).toBeTrue() - }) - - it('should be false', () => { - validator.__validateWithRule( - '_DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN_', - 'address', - ) - - expect(validator.passes()).toBeFalse() - }) - }) - - describe('__validateWithFunction', () => { - it('should be a function', () => { - expect(validator.__validateWithFunction).toBeFunction() - }) - - it('should be true', () => { - validator.__validateWithFunction( - 'DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN', - value => ({ - data: value, - passes: value.length === 34, - fails: value.length !== 34, - }), - ) - - expect(validator.passes()).toBeTrue() - }) - - it('should be false', () => { - validator.__validateWithFunction( - '_DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN_', - value => ({ - data: value, - passes: value.length === 34, - fails: value.length !== 34, - }), - ) - - expect(validator.passes()).toBeFalse() - }) - }) - - describe('__validateWithJoi', () => { - it('should be a function', () => { - expect(validator.__validateWithJoi).toBeFunction() - }) - - it('should be true', () => { - validator.__validateWithJoi( - 'DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN', - Joi.string() - .alphanum() - .length(34) - .required(), - ) - - expect(validator.passes()).toBeTrue() - }) - - it('should be false', () => { - validator.__validateWithJoi( - '_DARiJqhogp2Lu6bxufUFQQMuMyZbxjCydN_', - Joi.string() - .alphanum() - .length(34) - .required(), - ) - - expect(validator.passes()).toBeFalse() - }) - }) - - describe('__reset', () => { - it('should be a function', () => { - expect(validator.__reset).toBeFunction() - }) - - it('should be empty', () => { - validator.results = { - key: 'value', - } - - expect(validator.results).not.toBeNull() - - validator.__reset() - - expect(validator.results).toBeNull() - }) - }) -}) diff --git a/packages/crypto/__tests__/validation/validator.test.ts b/packages/crypto/__tests__/validation/validator.test.ts new file mode 100644 index 0000000000..6b068d146d --- /dev/null +++ b/packages/crypto/__tests__/validation/validator.test.ts @@ -0,0 +1,48 @@ +import "jest-extended"; +import Joi from "joi"; +import { Bignum } from "../../dist"; +import { Validator } from "../../src/validation/validator"; + +describe("Validator", () => { + describe("validate", () => { + it("should validate a simple number", async () => { + Validator.init(); + + const schema = { + a: Joi.number(), + }; + + const value = { + a: 123, + }; + + const result = await Validator.validate(value, schema); + expect(result).toEqual(value); + }); + + it("should validate using extended schemas", async () => { + Validator.init(); + + const schema = { + a: Validator.joi.bignumber(), + }; + + const value = { + a: new Bignum(12), + }; + + const result = await Validator.validate(value, schema); + expect(result).toEqual(value); + }); + + it("should return an error if an error was thrown", () => { + Validator.joi = { + validate: () => { + throw new Error("erreur"); + }, + }; + const result = Validator.validate("", ""); + expect(result.error).toBeDefined(); + }); + }); +}); diff --git a/packages/crypto/build/webpack.base.js b/packages/crypto/build/webpack.base.js index aa00bfb9ee..a503f358d5 100644 --- a/packages/crypto/build/webpack.base.js +++ b/packages/crypto/build/webpack.base.js @@ -1,24 +1,25 @@ +const path = require("path"); + module.exports = (babelOptions = {}) => ({ - mode: 'production', + mode: "production", + + entry: path.resolve(__dirname, "../src/index.ts"), + + devtool: "inline-source-map", - context: __dirname, + context: __dirname, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader', - options: { - presets: [['@babel/preset-env', babelOptions]], - }, - }, - }, - ], - }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, - resolve: { - extensions: ['.js', '.json'], - }, -}) + resolve: { + extensions: [".tsx", ".ts", ".js", ".json"], + }, +}); diff --git a/packages/crypto/build/webpack.config.js b/packages/crypto/build/webpack.config.js index 9b856b70c1..12f79fc59d 100644 --- a/packages/crypto/build/webpack.config.js +++ b/packages/crypto/build/webpack.config.js @@ -1,75 +1,69 @@ -/* eslint max-len: "off" */ +const path = require("path"); +const merge = require("webpack-merge"); +const nodeExternals = require("webpack-node-externals"); +const pkg = require("../package.json"); +const base = require("./webpack.base"); -const path = require('path') -const merge = require('webpack-merge') -const nodeExternals = require('webpack-node-externals') -const pkg = require('../package.json') -const base = require('./webpack.base') - -const resolve = dir => path.resolve(__dirname, '..', dir) +const resolve = dir => path.resolve(__dirname, "..", dir); const format = dist => ({ - path: resolve(path.dirname(dist)), - filename: path.basename(dist), -}) + path: resolve(path.dirname(dist)), + filename: path.basename(dist), +}); const browserConfig = { - entry: resolve(pkg.main), - target: 'web', - babel: { - modules: 'umd', - useBuiltIns: 'usage', - targets: { - browsers: 'defaults', + target: "web", + babel: { + modules: "umd", + useBuiltIns: "usage", + targets: { + browsers: "defaults", + }, + }, + resolve: { + alias: { + deepmerge$: "deepmerge/dist/umd.js", + }, + }, + node: { + net: "empty", }, - }, - resolve: { - alias: { - deepmerge$: 'deepmerge/dist/umd.js', + output: { + ...format(pkg.browser), + library: "ArkEcosystemCrypto", + libraryTarget: "umd", + umdNamedDefine: true, + globalObject: "this", }, - }, - node: { - net: 'empty', - }, - output: { - ...format(pkg.browser), - library: 'ArkEcosystemCrypto', - libraryTarget: 'umd', - umdNamedDefine: true, - globalObject: 'this', - }, -} +}; const moduleConfig = { - target: 'node', - babel: { - modules: 'commonjs', - useBuiltIns: 'usage', - targets: { - node: 'current', + target: "node", + babel: { + modules: "commonjs", + useBuiltIns: "usage", + targets: { + node: "current", + }, + }, + resolve: { + alias: { + deepmerge$: "deepmerge/dist/cjs.js", + }, + }, + externals: [ + nodeExternals({ + modulesFromFile: true, + modulesDir: resolve("node_modules"), + }), + ], + output: { + ...format(pkg.module), + libraryTarget: "commonjs2", }, - }, - resolve: { - alias: { - deepmerge$: 'deepmerge/dist/cjs.js', + optimization: { + minimize: false, }, - }, - externals: [ - nodeExternals({ - modulesFromFile: true, - modulesDir: resolve('node_modules'), - }), - ], - entry: resolve(pkg.main), - output: { - ...format(pkg.module), - libraryTarget: 'commonjs2', - }, - optimization: { - minimize: false, - }, -} +}; -module.exports = [browserConfig, moduleConfig].map(({ babel, ...entry }) => - merge(base(babel), entry), -) +module.exports = [browserConfig, moduleConfig].map(({ babel, ...entry }) => merge(base(babel), entry)); diff --git a/packages/crypto/jest.config.js b/packages/crypto/jest.config.js deleted file mode 100644 index 4bb674ebeb..0000000000 --- a/packages/crypto/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - testEnvironment: 'node', - bail: false, - verbose: true, - testMatch: ['**/__tests__/**/*.test.js'], - moduleFileExtensions: ['js', 'json'], - coverageDirectory: '/.coverage', - collectCoverageFrom: ['lib/**/*.js', '!**/node_modules/**'], - watchman: false, - setupFiles: ['/../../node_modules/regenerator-runtime/runtime'], - setupTestFrameworkScriptFile: 'jest-extended', -} diff --git a/packages/crypto/lib/builder/index.js b/packages/crypto/lib/builder/index.js deleted file mode 100644 index 54dafea4f8..0000000000 --- a/packages/crypto/lib/builder/index.js +++ /dev/null @@ -1,84 +0,0 @@ -class TransactionBuilderDirector { - /** - * Create new delegate resignation transaction type. - * @return {DelegateResignationBuilder} - */ - delegateResignation() { - return this.__getTransaction('delegate-resignation') - } - - /** - * Create new delegate transaction type. - * @return {DelegateRegistrationBuilder} - */ - delegateRegistration() { - return this.__getTransaction('delegate-registration') - } - - /** - * Create new IPFS transaction type. - * @return {IPFSBuilder} - */ - ipfs() { - return this.__getTransaction('ipfs') - } - - /** - * Create new multi-payment transaction type. - * @return {MultiPaymentBuilder} - */ - multiPayment() { - return this.__getTransaction('multi-payment') - } - - /** - * Create new multi-signature transaction type. - * @return {MultiSignatureBuilder} - */ - multiSignature() { - return this.__getTransaction('multi-signature') - } - - /** - * Create new second signature transaction type. - * @return {SecondSignatureBuilder} - */ - secondSignature() { - return this.__getTransaction('second-signature') - } - - /** - * Create new timelock transfer transaction type. - * @return {TimelockTransferBuilder} - */ - timelockTransfer() { - return this.__getTransaction('timelock-transfer') - } - - /** - * Create new transfer transaction type. - * @return {TransferBuilder} - */ - transfer() { - return this.__getTransaction('transfer') - } - - /** - * Create new vote transaction type. - * @return {VoteBuilder} - */ - vote() { - return this.__getTransaction('vote') - } - - /** - * Create new instance of specified transaction type. - * @param {String} transactionType - * @return {TransactionBuilder} - */ - __getTransaction(transactionType) { - return new (require(`./transactions/${transactionType}`))() // eslint-disable-line new-cap - } -} - -module.exports = new TransactionBuilderDirector() diff --git a/packages/crypto/lib/builder/transactions/delegate-registration.js b/packages/crypto/lib/builder/transactions/delegate-registration.js deleted file mode 100644 index 451b14f31a..0000000000 --- a/packages/crypto/lib/builder/transactions/delegate-registration.js +++ /dev/null @@ -1,54 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const { crypto } = require('../../crypto') - -module.exports = class DelegateRegistrationBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.DELEGATE_REGISTRATION - this.data.fee = feeManager.get(TRANSACTION_TYPES.DELEGATE_REGISTRATION) - this.data.amount = 0 - this.data.recipientId = null - this.data.senderPublicKey = null - this.data.asset = { delegate: {} } - } - - /** - * Establish the delegate username on the asset. - * @param {String} username - * @return {DelegateRegistrationBuilder} - */ - usernameAsset(username) { - this.data.asset.delegate.username = username - return this - } - - /** - * Overrides the inherited `sign` method to include the public key of the new delegate. - * @param {String} passphrase - * @return {DelegateRegistrationBuilder} - * TODO rename to `assetDelegate` and merge with username ? - */ - sign(passphrase) { - this.data.asset.delegate.publicKey = crypto.getKeys(passphrase).publicKey - super.sign(passphrase) - return this - } - - /** - * Overrides the inherited method to return the additional required by this type of transaction. - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.recipientId = this.data.recipientId - struct.asset = this.data.asset - return struct - } -} diff --git a/packages/crypto/lib/builder/transactions/delegate-resignation.js b/packages/crypto/lib/builder/transactions/delegate-resignation.js deleted file mode 100644 index eb6072edc4..0000000000 --- a/packages/crypto/lib/builder/transactions/delegate-resignation.js +++ /dev/null @@ -1,15 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') - -module.exports = class DelegateResignationBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.DELEGATE_RESIGNATION - this.data.fee = feeManager.get(TRANSACTION_TYPES.DELEGATE_RESIGNATION) - } -} diff --git a/packages/crypto/lib/builder/transactions/ipfs.js b/packages/crypto/lib/builder/transactions/ipfs.js deleted file mode 100644 index d86ead86b1..0000000000 --- a/packages/crypto/lib/builder/transactions/ipfs.js +++ /dev/null @@ -1,63 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') - -module.exports = class IPFSBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.IPFS - this.data.fee = feeManager.get(TRANSACTION_TYPES.IPFS) - this.data.amount = 0 - this.data.vendorFieldHex = null - this.data.senderPublicKey = null - this.data.asset = {} - } - - /** - * Set the IPFS hash. - * @param {String} ipfsHash - * @return {IPFSBuilder} - */ - ipfsHash(ipfsHash) { - this.data.ipfsHash = ipfsHash - return this - } - - /** - * Set vendor field from hash. - * @param {String} type TODO is it necessary? - * @return {IPFSBuilder} - */ - vendorField(type) { - this.data.vendorFieldHex = Buffer.from(this.data.ipfsHash, type).toString( - 'hex', - ) - - while (this.data.vendorFieldHex.length < 128) { - this.data.vendorFieldHex = `00${this.data.vendorFieldHex}` - } - - // TODO is this right? when is vendorFieldHex.length is odd, - // it will add 1 more "0" than previous way - // const vendorFieldHex = Buffer.from(this.data.ipfsHash, type).toString('hex') - // this.data.vendorFieldHex = vendorFieldHex.padStart(128, '0') - - return this - } - - /** - * Overrides the inherited method to return the additional required by this. - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.vendorFieldHex = this.data.vendorFieldHex - struct.asset = this.data.asset - return struct - } -} diff --git a/packages/crypto/lib/builder/transactions/mixins/sign.js b/packages/crypto/lib/builder/transactions/mixins/sign.js deleted file mode 100644 index 04455a23ad..0000000000 --- a/packages/crypto/lib/builder/transactions/mixins/sign.js +++ /dev/null @@ -1,44 +0,0 @@ -const { crypto } = require('../../../crypto') -const configManager = require('../../../managers/config') - -module.exports = { - mixin(Base) { - return class extends Base { - /** - * Overrides the inherited `sign` method to set the sender as the recipient too - * @param {String} passphrase - * @return {TransactionBuilder} - */ - sign(passphrase) { - const pubKeyHash = this.data.network - ? this.data.network.pubKeyHash - : null - this.data.recipientId = crypto.getAddress( - crypto.getKeys(passphrase).publicKey, - pubKeyHash, - ) - super.sign(passphrase) - return this - } - - /** - * Overrides the inherited `signWithWif` method to set the sender as the recipient too - * @param {String} wif - * @param {String} networkWif - value associated with network - * @return {TransactionBuilder} - */ - signWithWif(wif, networkWif) { - const pubKeyHash = this.data.network - ? this.data.network.pubKeyHash - : null - const keys = crypto.getKeysFromWIF(wif, { - wif: networkWif || configManager.get('wif'), - }) - this.data.recipientId = crypto.getAddress(keys.publicKey, pubKeyHash) - super.signWithWif(wif, networkWif) - - return this - } - } - }, -} diff --git a/packages/crypto/lib/builder/transactions/mixins/vendor-field.js b/packages/crypto/lib/builder/transactions/mixins/vendor-field.js deleted file mode 100644 index 87e15d50d9..0000000000 --- a/packages/crypto/lib/builder/transactions/mixins/vendor-field.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = { - mixin(Base) { - return class extends Base { - /** - * Set vendor field from data. - * @param {(String|undefined)} value - * @return {TransactionBuilder} - */ - vendorField(value) { - this.data.vendorField = value - // V2 - // this.data.vendorFieldHex = Buffer.from(value, type).toString('hex') - - return this - } - } - }, -} diff --git a/packages/crypto/lib/builder/transactions/multi-payment.js b/packages/crypto/lib/builder/transactions/multi-payment.js deleted file mode 100644 index d481741d7b..0000000000 --- a/packages/crypto/lib/builder/transactions/multi-payment.js +++ /dev/null @@ -1,52 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const vendorField = require('./mixins/vendor-field') - -class MultiPaymentBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.MULTI_PAYMENT - this.data.fee = feeManager.get(TRANSACTION_TYPES.MULTI_PAYMENT) - this.data.payments = {} - this.data.vendorFieldHex = null - } - - /** - * Add payment to the multipayment collection. - * @param {String} address - * @param {Number} amount - * @return {MultiPaymentBuilder} - */ - addPayment(address, amount) { - const paymentsCount = Object.keys(this.data.payments).length / 2 - - if (paymentsCount >= 2258) { - throw new Error('A maximum of 2259 outputs is allowed') - } - - const key = paymentsCount + 1 - this.data.payments[`address${key}`] = address - this.data.payments[`amount${key}`] = amount - - return this - } - - /** - * Overrides the inherited method to return the additional required by this. - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.senderPublicKey = this.data.senderPublicKey - struct.vendorFieldHex = this.data.vendorFieldHex - - return Object.assign(struct, this.data.payments) - } -} - -module.exports = vendorField.mixin(MultiPaymentBuilder) diff --git a/packages/crypto/lib/builder/transactions/multi-signature.js b/packages/crypto/lib/builder/transactions/multi-signature.js deleted file mode 100644 index b2d020d9d6..0000000000 --- a/packages/crypto/lib/builder/transactions/multi-signature.js +++ /dev/null @@ -1,48 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const sign = require('./mixins/sign') - -class MultiSignatureBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.MULTI_SIGNATURE - this.data.fee = 0 - this.data.amount = 0 - this.data.recipientId = null - this.data.senderPublicKey = null - this.data.asset = { multisignature: {} } - } - - /** - * Establish the multi-signature on the asset and updates the fee. - * @param {Object} multiSignature { keysgroup, lifetime, min } - * @return {MultiSignatureBuilder} - */ - multiSignatureAsset(multiSignature) { - this.data.asset.multisignature = multiSignature - this.data.fee = (multiSignature.keysgroup.length + 1) - * feeManager.get(TRANSACTION_TYPES.MULTI_SIGNATURE) - - return this - } - - /** - * Overrides the inherited method to return the additional required by this. - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.recipientId = this.data.recipientId - struct.asset = this.data.asset - - return struct - } -} - -module.exports = sign.mixin(MultiSignatureBuilder) diff --git a/packages/crypto/lib/builder/transactions/second-signature.js b/packages/crypto/lib/builder/transactions/second-signature.js deleted file mode 100644 index 04e3d29ece..0000000000 --- a/packages/crypto/lib/builder/transactions/second-signature.js +++ /dev/null @@ -1,45 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const { crypto } = require('../../crypto') - -module.exports = class SecondSignatureBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.SECOND_SIGNATURE - this.data.fee = feeManager.get(TRANSACTION_TYPES.SECOND_SIGNATURE) - this.data.amount = 0 - this.data.recipientId = null - this.data.senderPublicKey = null - this.data.asset = { signature: {} } - } - - /** - * Establish the signature on the asset, which is the one that would be that - * would be register on the blockchain, when creating a second passphrase. - * @param {String} secondPassphrase - * @return {SecondSignatureBuilder} - */ - signatureAsset(secondPassphrase) { - this.data.asset.signature.publicKey = crypto.getKeys( - secondPassphrase, - ).publicKey - return this - } - - /** - * Overrides the inherited method to return the additional required by this. - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.recipientId = this.data.recipientId - struct.asset = this.data.asset - return struct - } -} diff --git a/packages/crypto/lib/builder/transactions/timelock-transfer.js b/packages/crypto/lib/builder/transactions/timelock-transfer.js deleted file mode 100644 index 862da0ed44..0000000000 --- a/packages/crypto/lib/builder/transactions/timelock-transfer.js +++ /dev/null @@ -1,50 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const vendorField = require('./mixins/vendor-field') - -class TimelockTransferBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.TIMELOCK_TRANSFER - this.data.fee = feeManager.get(TRANSACTION_TYPES.TIMELOCK_TRANSFER) - this.data.amount = 0 - this.data.recipientId = null - this.data.senderPublicKey = null - this.data.timelockType = 0x00 - this.data.timelock = null - } - - /** - * Set the timelock and the timelock type - * @param {Number} timelock - * @param {Number} timelockType - * @return {TimelockTransferBuilder} - */ - timelock(timelock, timelockType) { - this.data.timelock = timelock - this.data.timelockType = timelockType - return this - } - - /** - * Overrides the inherited method to return the additional required by this - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.recipientId = this.data.recipientId - struct.vendorFieldHex = this.data.vendorFieldHex - struct.asset = this.data.asset - struct.timelock = this.data.timelock - struct.timelockType = this.data.timelockType - return struct - } -} - -module.exports = vendorField.mixin(TimelockTransferBuilder) diff --git a/packages/crypto/lib/builder/transactions/transaction.js b/packages/crypto/lib/builder/transactions/transaction.js deleted file mode 100644 index f39c4a83c1..0000000000 --- a/packages/crypto/lib/builder/transactions/transaction.js +++ /dev/null @@ -1,245 +0,0 @@ -const Transaction = require('../../models/transaction') -const { crypto, slots } = require('../../crypto') -const configManager = require('../../managers/config') - -module.exports = class TransactionBuilder { - /** - * @constructor - */ - constructor() { - this.data = { - id: null, - timestamp: slots.getTime(), - version: 0x01, - network: configManager.get('pubKeyHash'), - } - } - - /** - * Build a new Transaction instance. - * @return {Transaction} - */ - build(data) { - return new Transaction({ ...this.data, ...data }) - } - - /** - * Set transaction version. - * @param {Number} version - * @return {TransactionBuilder} - */ - version(version) { - this.data.version = version - return this - } - - /** - * Set transaction network. - * @param {Number} network - * @return {TransactionBuilder} - */ - network(network) { - this.data.network = network - return this - } - - /** - * Set transaction fee. - * @param {Number} fee - * @return {TransactionBuilder} - */ - fee(fee) { - if (fee !== null) { - this.data.fee = fee - } - - return this - } - - /** - * Set amount to transfer. - * @param {Number} amount - * @return {TransactionBuilder} - */ - amount(amount) { - this.data.amount = amount - return this - } - - /** - * Set recipient id. - * @param {String} recipientId - * @return {TransactionBuilder} - */ - recipientId(recipientId) { - this.data.recipientId = recipientId - return this - } - - /** - * Set sender public key. - * @param {String} publicKey - * @return {TransactionBuilder} - */ - senderPublicKey(publicKey) { - this.data.senderPublicKey = publicKey - return this - } - - /** - * Set vendor field. - * @param {String} vendorField - * @return {TransactionBuilder} - */ - vendorField(vendorField) { - if (vendorField && Buffer.from(vendorField).length <= 64) { - this.data.vendorField = vendorField - } - - return this - } - - /** - * Verify the transaction. - * @return {Boolean} - */ - verify() { - return crypto.verify(this.data) - } - - /** - * Serialize the transaction. - * TODO @deprecated when a Transaction model is returned - * @return {Buffer} - */ - serialize() { - return this.model.serialize(this.getStruct()) - } - - /** - * Sign transaction using passphrase. - * @param {String} passphrase - * @return {TransactionBuilder} - */ - sign(passphrase) { - const keys = crypto.getKeys(passphrase) - this.data.senderPublicKey = keys.publicKey - this.data.signature = crypto.sign(this.__getSigningObject(), keys) - - return this - } - - /** - * Sign transaction using wif. - * @param {String} wif - * @param {String} networkWif - value associated with network - * @return {TransactionBuilder} - */ - signWithWif(wif, networkWif) { - const keys = crypto.getKeysFromWIF(wif, { - wif: networkWif || configManager.get('wif'), - }) - this.data.senderPublicKey = keys.publicKey - this.data.signature = crypto.sign(this.__getSigningObject(), keys) - - return this - } - - /** - * Sign transaction with second passphrase. - * @param {String} secondPassphrase - * @return {TransactionBuilder} - */ - secondSign(secondPassphrase) { - if (secondPassphrase) { - const keys = crypto.getKeys(secondPassphrase) - // TODO sign or second? - this.data.signSignature = crypto.secondSign( - this.__getSigningObject(), - keys, - ) - } - - return this - } - - /** - * Sign transaction with wif. - * @param {String} wif - * @param {String} networkWif - value associated with network - * @return {TransactionBuilder} - */ - secondSignWithWif(wif, networkWif) { - if (wif) { - const keys = crypto.getKeysFromWIF(wif, { - wif: networkWif || configManager.get('wif'), - }) - // TODO sign or second? - this.data.signSignature = crypto.secondSign( - this.__getSigningObject(), - keys, - ) - } - - return this - } - - /** - * Sign transaction for multi-signature wallets. - * @param {String} passphrase - * @return {TransactionBuilder} - */ - multiSignatureSign(passphrase) { - const keys = crypto.getKeys(passphrase) - if (!this.data.signatures) { - this.data.signatures = [] - } - this.data.signatures.push(crypto.sign(this.__getSigningObject(), keys)) - - return this - } - - /** - * Get structure of transaction - * @return {Object} - */ - getStruct() { - if (!this.data.senderPublicKey || !this.data.signature) { - throw new Error('The transaction is not signed yet') - } - - const struct = { - // hex: crypto.getBytes(this).toString('hex'), // v2 - id: crypto.getId(this.data).toString('hex'), - signature: this.data.signature, - signSignature: this.data.signSignature, - timestamp: this.data.timestamp, - - type: this.data.type, - fee: this.data.fee, - senderPublicKey: this.data.senderPublicKey, - } - - if (Array.isArray(this.data.signatures)) { - struct.signatures = this.data.signatures - } - - return struct - } - - /** - * Get a valid object used to sign a transaction. - * @return {Object} - */ - __getSigningObject() { - const { data } = this - - Object.keys(data).forEach(key => { - if (['model', 'network', 'id'].includes(key)) { - delete data[key] - } - }) - - return data - } -} diff --git a/packages/crypto/lib/builder/transactions/transfer.js b/packages/crypto/lib/builder/transactions/transfer.js deleted file mode 100644 index 53b62359d1..0000000000 --- a/packages/crypto/lib/builder/transactions/transfer.js +++ /dev/null @@ -1,36 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const vendorField = require('./mixins/vendor-field') - -class TransferBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.TRANSFER - this.data.fee = feeManager.get(TRANSACTION_TYPES.TRANSFER) - this.data.amount = 0 - this.data.recipientId = null - this.data.senderPublicKey = null - this.data.expiration = 0 - } - - /** - * Overrides the inherited method to return the additional required by this - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.recipientId = this.data.recipientId - struct.asset = this.data.asset - struct.vendorField = this.data.vendorField - // struct.vendorFieldHex = this.vendorFieldHex // v2 - return struct - } -} - -module.exports = vendorField.mixin(TransferBuilder) diff --git a/packages/crypto/lib/builder/transactions/vote.js b/packages/crypto/lib/builder/transactions/vote.js deleted file mode 100644 index 010684bb07..0000000000 --- a/packages/crypto/lib/builder/transactions/vote.js +++ /dev/null @@ -1,44 +0,0 @@ -const feeManager = require('../../managers/fee') -const { TRANSACTION_TYPES } = require('../../constants') -const TransactionBuilder = require('./transaction') -const sign = require('./mixins/sign') - -class VoteBuilder extends TransactionBuilder { - /** - * @constructor - */ - constructor() { - super() - - this.data.type = TRANSACTION_TYPES.VOTE - this.data.fee = feeManager.get(TRANSACTION_TYPES.VOTE) - this.data.amount = 0 - this.data.recipientId = null - this.data.senderPublicKey = null - this.data.asset = { votes: [] } - } - - /** - * Establish the votes on the asset. - * @param {Array} votes - * @return {VoteBuilder} - */ - votesAsset(votes) { - this.data.asset.votes = votes - return this - } - - /** - * Overrides the inherited method to return the additional required by this - * @return {Object} - */ - getStruct() { - const struct = super.getStruct() - struct.amount = this.data.amount - struct.recipientId = this.data.recipientId - struct.asset = this.data.asset - return struct - } -} - -module.exports = sign.mixin(VoteBuilder) diff --git a/packages/crypto/lib/client.js b/packages/crypto/lib/client.js deleted file mode 100644 index 46e84afcf1..0000000000 --- a/packages/crypto/lib/client.js +++ /dev/null @@ -1,48 +0,0 @@ -const NetworkManager = require('./managers/network') -const transactionBuilder = require('./builder') -const configManager = require('./managers/config') -const feeManager = require('./managers/fee') - -class Client { - /** - * @constructor - * @param {Object} config - */ - constructor(config) { - this.setConfig(config || NetworkManager.findByName('devnet')) - } - - /** - * Set config for client. - * @param {Object} config - */ - setConfig(config) { - configManager.setConfig(config) - } - - /** - * Get fee manager. - * @return {FeeManager} - */ - getFeeManager() { - return feeManager - } - - /** - * Get config manager. - * @return {ConfigManager} - */ - getConfigManager() { - return configManager - } - - /** - * Get transaction builder. - * @return {TransactionBuilder} - */ - getBuilder() { - return transactionBuilder - } -} - -module.exports = new Client() diff --git a/packages/crypto/lib/constants.js b/packages/crypto/lib/constants.js deleted file mode 100644 index 367e5c921f..0000000000 --- a/packages/crypto/lib/constants.js +++ /dev/null @@ -1,61 +0,0 @@ -const configMainnet = require('./networks/ark/mainnet.json') -const configDevnet = require('./networks/ark/devnet.json') -const configTestnet = require('./networks/ark/testnet.json') - -/** - * The Arktoshi base. - * @type {Number} - */ -exports.ARKTOSHI = 1e8 - -/** - * Available transaction types. - * @type {Object} - */ -exports.TRANSACTION_TYPES = Object.freeze({ - TRANSFER: 0, - SECOND_SIGNATURE: 1, - DELEGATE_REGISTRATION: 2, - VOTE: 3, - MULTI_SIGNATURE: 4, - IPFS: 5, - TIMELOCK_TRANSFER: 6, - MULTI_PAYMENT: 7, - DELEGATE_RESIGNATION: 8, - toString(type) { - switch (type) { - case this.TRANSFER: - return 'transfer' - case this.SECOND_SIGNATURE: - return 'second signature' - case this.DELEGATE_REGISTRATION: - return 'delegate registration' - case this.VOTE: - return 'vote' - case this.MULTI_SIGNATURE: - return 'multi signature' - case this.IPFS: - return 'ipfs' - case this.TIMELOCK_TRANSFER: - return 'timelock transfer' - case this.MULTI_PAYMENT: - return 'multi payment' - case this.DELEGATE_RESIGNATION: - return 'delegate resignation' - default: - throw new Error('Invalid transaction type') - } - }, -}) - -/** - * Available network configurations. - * @type {Object} - */ -exports.CONFIGURATIONS = Object.freeze({ - ARK: { - MAINNET: configMainnet, - DEVNET: configDevnet, - TESTNET: configTestnet, - }, -}) diff --git a/packages/crypto/lib/crypto/crypto.js b/packages/crypto/lib/crypto/crypto.js deleted file mode 100644 index 555ffb7ab3..0000000000 --- a/packages/crypto/lib/crypto/crypto.js +++ /dev/null @@ -1,490 +0,0 @@ -/* eslint default-case: "off" */ - -const bs58check = require('bs58check') -const crypto = require('crypto') -const ByteBuffer = require('bytebuffer') -const secp256k1 = require('secp256k1') -const wif = require('wif') - -const configManager = require('../managers/config') -const utils = require('./utils') -const { Bignum } = require('../utils') -const feeManager = require('../managers/fee') -const { - transactionIdFixTable, -} = require('../constants').CONFIGURATIONS.ARK.MAINNET - -class Crypto { - /** - * Get transaction fee. - * @param {Transaction} transaction - * @return {Number} - */ - getFee(transaction) { - return feeManager.get(transaction.type) - } - - /** - * Get the byte representation of the transaction. - * @param {Transaction} transaction - * @param {Boolean} skipSignature - * @param {Boolean} skipSecondSignature - * @return {String} - */ - getBytes(transaction, skipSignature, skipSecondSignature) { - if (transaction.version && transaction.version !== 1) { - throw new Error('not supported yet') - } - - let assetSize = 0 - let assetBytes = null - - switch (transaction.type) { - case 1: { - // Signature - const { signature } = transaction.asset - const bb = new ByteBuffer(33, true) - const publicKeyBuffer = Buffer.from(signature.publicKey, 'hex') - - for (let i = 0; i < publicKeyBuffer.length; i++) { - bb.writeByte(publicKeyBuffer[i]) - } - - bb.flip() - - assetBytes = new Uint8Array(bb.toArrayBuffer()) - assetSize = assetBytes.length - break - } - - case 2: { - // Delegate - assetBytes = Buffer.from(transaction.asset.delegate.username, 'utf8') - assetSize = assetBytes.length - break - } - - case 3: { - // Vote - if (transaction.asset.votes !== null) { - assetBytes = Buffer.from(transaction.asset.votes.join(''), 'utf8') - assetSize = assetBytes.length - } - break - } - - case 4: { - // Multi-Signature - const keysgroupBuffer = Buffer.from( - transaction.asset.multisignature.keysgroup.join(''), - 'utf8', - ) - const bb = new ByteBuffer(1 + 1 + keysgroupBuffer.length, true) - - bb.writeByte(transaction.asset.multisignature.min) - bb.writeByte(transaction.asset.multisignature.lifetime) - - for (let i = 0; i < keysgroupBuffer.length; i++) { - bb.writeByte(keysgroupBuffer[i]) - } - - bb.flip() - - assetBytes = bb.toBuffer() - assetSize = assetBytes.length - break - } - } - - const bb = new ByteBuffer( - 1 + 4 + 32 + 8 + 8 + 21 + 64 + 64 + 64 + assetSize, - true, - ) - bb.writeByte(transaction.type) - bb.writeInt(transaction.timestamp) - - const senderPublicKeyBuffer = Buffer.from( - transaction.senderPublicKey, - 'hex', - ) - for (let i = 0; i < senderPublicKeyBuffer.length; i++) { - bb.writeByte(senderPublicKeyBuffer[i]) - } - - // Apply fix for broken type 1 and 4 transactions, which were - // erroneously calculated with a recipient id. - const isBrokenTransaction = Object.values(transactionIdFixTable).includes( - transaction.id, - ) - const correctType = transaction.type !== 1 && transaction.type !== 4 - if (transaction.recipientId && (isBrokenTransaction || correctType)) { - const recipient = bs58check.decode(transaction.recipientId) - for (let i = 0; i < recipient.length; i++) { - bb.writeByte(recipient[i]) - } - } else { - for (let i = 0; i < 21; i++) { - bb.writeByte(0) - } - } - - if (transaction.vendorFieldHex) { - const vf = Buffer.from(transaction.vendorFieldHex, 'hex') - const fillstart = vf.length - for (let i = 0; i < fillstart; i++) { - bb.writeByte(vf[i]) - } - for (let i = fillstart; i < 64; i++) { - bb.writeByte(0) - } - } else if (transaction.vendorField) { - const vf = Buffer.from(transaction.vendorField) - const fillstart = vf.length - for (let i = 0; i < fillstart; i++) { - bb.writeByte(vf[i]) - } - for (let i = fillstart; i < 64; i++) { - bb.writeByte(0) - } - } else { - for (let i = 0; i < 64; i++) { - bb.writeByte(0) - } - } - - bb.writeLong(+new Bignum(transaction.amount).toFixed()) - bb.writeLong(+new Bignum(transaction.fee).toFixed()) - - if (assetSize > 0) { - for (let i = 0; i < assetSize; i++) { - bb.writeByte(assetBytes[i]) - } - } - - if (!skipSignature && transaction.signature) { - const signatureBuffer = Buffer.from(transaction.signature, 'hex') - for (let i = 0; i < signatureBuffer.length; i++) { - bb.writeByte(signatureBuffer[i]) - } - } - - if (!skipSecondSignature && transaction.signSignature) { - const signSignatureBuffer = Buffer.from(transaction.signSignature, 'hex') - for (let i = 0; i < signSignatureBuffer.length; i++) { - bb.writeByte(signSignatureBuffer[i]) - } - } - - bb.flip() - const arrayBuffer = new Uint8Array(bb.toArrayBuffer()) - const buffer = [] - - for (let i = 0; i < arrayBuffer.length; i++) { - buffer[i] = arrayBuffer[i] - } - - return Buffer.from(buffer) - } - - /** - * Get transaction id. - * @param {Transaction} transaction - * @return {String} - */ - getId(transaction) { - if (transaction.version && transaction.version !== 1) { - throw new Error('not supported yet') - } - - const bytes = this.getBytes(transaction) - return crypto - .createHash('sha256') - .update(bytes) - .digest() - .toString('hex') - - // TODO: Enable AIP11 id here - } - - /** - * Get transaction hash. - * @param {Transaction} transaction - * @return {Buffer} - */ - getHash(transaction, skipSignature, skipSecondSignature) { - if (transaction.version && transaction.version !== 1) { - throw new Error('not supported yet') - } - - const bytes = this.getBytes(transaction, skipSignature, skipSecondSignature) - return crypto - .createHash('sha256') - .update(bytes) - .digest() - - // TODO: Enable AIP11 id here - } - - /** - * Sign transaction. - * @param {Transaction} transaction - * @param {Object} keys - * @return {Object} - */ - sign(transaction, keys) { - let hash - if (!transaction.version || transaction.version === 1) { - hash = this.getHash(transaction, true, true) - } else { - hash = this.getHash(transaction, false, false) - } - - const signature = this.signHash(hash, keys) - - if (!transaction.signature) { - transaction.signature = signature - } - - return signature - } - - /** - * Sign transaction with second signature. - * @param {Transaction} transaction - * @param {Object} keys - * @return {Object} - */ - secondSign(transaction, keys) { - const hash = this.getHash(transaction, false, true) - const signature = this.signHash(hash, keys) - - if (!transaction.secondSignature) { - transaction.secondSignature = signature - } - - return signature - } - - /** - * Sign a hash - * @param {Buffer} hash - * @param {Object} keys - * @return {String} - */ - signHash(hash, keys) { - const { signature } = secp256k1.sign( - hash, - Buffer.from(keys.privateKey, 'hex'), - ) - return secp256k1.signatureExport(signature).toString('hex') - } - - /** - * Verify transaction on the network. - * @param {Transaction} transaction - * @return {Boolean} - */ - verify(transaction) { - if (transaction.version && transaction.version !== 1) { - // TODO: enable AIP11 when ready here - return false - } - - if (!transaction.signature) { - return false - } - - const hash = this.getHash(transaction, true, true) - return this.verifyHash( - hash, - transaction.signature, - transaction.senderPublicKey, - ) - } - - /** - * Verify second signature for transaction. - * @param {Transaction} transaction - * @param {String} publicKey - * @return {Boolean} - */ - verifySecondSignature(transaction, publicKey) { - let hash - let secondSignature - if (transaction.version && transaction.version !== 1) { - hash = this.getHash(transaction) - secondSignature = transaction.secondSignature - } else { - hash = this.getHash(transaction, false, true) - secondSignature = transaction.signSignature - } - - if (!secondSignature) { - return false - } - - return this.verifyHash(hash, secondSignature, publicKey) - } - - /** - * Verify the hash. - * @param {Buffer} hash - * @param {(Buffer|String)} signature - * @param {(Buffer|String)} publicKey - * @return {Boolean} - */ - verifyHash(hash, signature, publicKey) { - signature = - signature instanceof Buffer ? signature : Buffer.from(signature, 'hex') - publicKey = - publicKey instanceof Buffer ? publicKey : Buffer.from(publicKey, 'hex') - return secp256k1.verify( - hash, - secp256k1.signatureImport(signature), - publicKey, - ) - } - - /** - * Get keys from secret. - * @param {String} secret - * @param {boolean} compressed - * @return {Object} - */ - getKeys(secret, compressed = true) { - const privateKey = utils.sha256(Buffer.from(secret, 'utf8')) - return this.getKeysByPrivateKey(privateKey, compressed) - } - - /** - * Get keys from a private key. - * @param {String|Buffer} privateKey - * @param {boolean} compressed - * @return {Object} - */ - getKeysByPrivateKey(privateKey, compressed = true) { - privateKey = - privateKey instanceof Buffer ? privateKey : Buffer.from(privateKey, 'hex') - - const publicKey = secp256k1.publicKeyCreate(privateKey, compressed) - const keyPair = { - publicKey: publicKey.toString('hex'), - privateKey: privateKey.toString('hex'), - compressed, - } - - return keyPair - } - - /** - * Get keys from WIF key. - * @param {String} wifKey - * @param {Object} network - * @return {Object} - */ - getKeysFromWIF(wifKey, network) { - const decoded = wif.decode(wifKey) - const version = decoded.version - - if (!network) { - network = configManager.all() - } - - if (version !== network.wif) { - throw new Error('Invalid network version') - } - - const privateKey = decoded.privateKey - const publicKey = secp256k1.publicKeyCreate(privateKey, decoded.compressed) - - const keyPair = { - publicKey: publicKey.toString('hex'), - privateKey: privateKey.toString('hex'), - compressed: decoded.compressed, - } - - return keyPair - } - - /** - * Get WIF key from keys - * @param {Object} keys - * @param {(Object|undefined)} network - * @returns {String} - */ - keysToWIF(keys, network) { - if (!network) { - network = configManager.all() - } - - return wif.encode( - network.wif, - Buffer.from(keys.privateKey, 'hex'), - keys.compressed, - ) - } - - /** - * Get address from public key. - * @param {String} publicKey - * @param {(Number|undefined)} networkVersion - * @return {String} - */ - getAddress(publicKey, networkVersion) { - const pubKeyRegex = /^[0-9A-Fa-f]{66}$/ - if (!pubKeyRegex.test(publicKey)) { - throw new Error(`publicKey '${publicKey}' is invalid`) - } - - if (!networkVersion) { - networkVersion = configManager.get('pubKeyHash') - } - - const buffer = utils.ripemd160(Buffer.from(publicKey, 'hex')) - const payload = Buffer.alloc(21) - - payload.writeUInt8(networkVersion, 0) - buffer.copy(payload, 1) - - return bs58check.encode(payload) - } - - /** - * Validate address. - * @param {String} address - * @param {(Number|undefined)} networkVersion - * @return {Boolean} - */ - validateAddress(address, networkVersion) { - if (!networkVersion) { - networkVersion = configManager.get('pubKeyHash') - } - - try { - const decode = bs58check.decode(address) - return decode[0] === networkVersion - } catch (e) { - return false - } - } - - /** - * Validate public key. - * @param {String} address - * @param {(Number|undefined)} networkVersion - * @return {Boolean} - */ - validatePublicKey(address, networkVersion) { - if (!networkVersion) { - networkVersion = configManager.get('pubKeyHash') - } - - try { - return this.getAddress(address, networkVersion).length === 34 - } catch (e) { - return false - } - } -} - -module.exports = new Crypto() diff --git a/packages/crypto/lib/crypto/hdwallet.js b/packages/crypto/lib/crypto/hdwallet.js deleted file mode 100644 index 84f6588b25..0000000000 --- a/packages/crypto/lib/crypto/hdwallet.js +++ /dev/null @@ -1,74 +0,0 @@ -const bip32 = require('bip32') -const bip39 = require('bip39') -const configManager = require('../managers/config') - -class HDWallet { - constructor() { - this.slip44 = 111 - } - - /** - * Get root node from the given mnemonic with an optional passphrase. - * @param {String} mnemonic - * @param {(String|undefined)} passphrase - * @returns {bip32} - */ - fromMnemonic(mnemonic, passphrase) { - const seed = bip39.mnemonicToSeed(mnemonic, passphrase) - return bip32.fromSeed(seed, configManager.config) - } - - /** - * Get bip32 node from keys. - * @param {Object} keys - * @param {Buffer} chainCode - * @returns {bip32} - */ - fromKeys(keys, chainCode) { - if (!keys.compressed) { - throw new TypeError('BIP32 only allows compressed keys.') - } - - return bip32.fromPrivateKey( - Buffer.from(keys.privateKey, 'hex'), - chainCode, - configManager.config, - ) - } - - /** - * Get key pair from the given node. - * @param {bip32} node - * @return {Object} - */ - getKeys(node) { - return { - publicKey: node.publicKey.toString('hex'), - privateKey: node.privateKey.toString('hex'), - compressed: true, - } - } - - /** - * Derives a node from the coin type as specified by slip44. - * @param {bip32} root - * @param {(Boolean|undefined)} hardened - * @returns {bip32} - */ - deriveSlip44(root, hardened = true) { - return root.derivePath(`m/44'/${this.slip44}${hardened ? "'" : ''}`) - } - - /** - * Derives a node from the network as specified by AIP20. - * @param {bip32} root - * @returns {bip32} - */ - deriveNetwork(root) { - return this.deriveSlip44(root).deriveHardened( - configManager.config.aip20 || 1, - ) - } -} - -module.exports = new HDWallet() diff --git a/packages/crypto/lib/crypto/index.js b/packages/crypto/lib/crypto/index.js deleted file mode 100644 index 580886af85..0000000000 --- a/packages/crypto/lib/crypto/index.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - crypto: require('./crypto'), - hdwallet: require('./hdwallet'), - Message: require('./message'), - slots: require('./slots'), - utils: require('./utils'), -} diff --git a/packages/crypto/lib/crypto/message.js b/packages/crypto/lib/crypto/message.js deleted file mode 100644 index 5ba4eebecf..0000000000 --- a/packages/crypto/lib/crypto/message.js +++ /dev/null @@ -1,58 +0,0 @@ -const crypto = require('crypto') -const arkCrypto = require('./crypto') -const configManager = require('../managers/config') - -const createHash = message => crypto - .createHash('sha256') - .update(Buffer.from(message, 'utf-8')) - .digest() - -module.exports = class Message { - /** - * Sign the given message. - * @param {String} message - * @param {String} passphrase - * @return {Object} - */ - static sign(message, passphrase) { - const keys = arkCrypto.getKeys(passphrase) - - return { - publicKey: keys.publicKey, - signature: arkCrypto.signHash(createHash(message), keys), - message, - } - } - - /** - * Sign the given message using a WIF. - * @param {String} message - * @param {String} wif - * @param {Object} network - * @return {Object} - */ - static signWithWif(message, wif, network) { - if (!network) { - network = configManager.all() - } - - const keys = arkCrypto.getKeysFromWIF(wif, network) - - return { - publicKey: keys.publicKey, - signature: arkCrypto.signHash(createHash(message), keys), - message, - } - } - - /** - * Verify the given message. - * @param {String} options.message - * @param {String} options.publicKey - * @param {String} options.signature - * @return {Boolean} - */ - static verify({ message, publicKey, signature }) { - return arkCrypto.verifyHash(createHash(message), signature, publicKey) - } -} diff --git a/packages/crypto/lib/crypto/slots.js b/packages/crypto/lib/crypto/slots.js deleted file mode 100644 index 900d7a3cf5..0000000000 --- a/packages/crypto/lib/crypto/slots.js +++ /dev/null @@ -1,148 +0,0 @@ -const dayjs = require('dayjs-ext') -const configManager = require('../managers/config') - -class Slots { - /** - * Create a new Slot instance. - */ - constructor() { - this.resetHeight() - } - - /** - * Get the height we are currently at. - * @return {Number} - */ - getHeight() { - return this.height - } - - /** - * Set the height we are currently at. - * @param {Number} height - * @return {void} - */ - setHeight(height) { - this.height = height - } - - /** - * Reset the height to the initial value. - * @return {void} - */ - resetHeight() { - this.height = 1 - } - - /** - * Get epoch time relative to beginning epoch time. - * @param {Number} time - * @return {Number} - */ - getEpochTime(time) { - if (time === undefined) { - time = dayjs().valueOf() - } - - const start = this.beginEpochTime().valueOf() - - return Math.floor((time - start) / 1000) - } - - /** - * Get beginning epoch time. - * @return {Moment} - */ - beginEpochTime() { - return dayjs(this.getConstant('epoch')).utc() - } - - /** - * Get epoch time relative to beginning epoch time. - * @param {Number} time - * @return {Number} - */ - getTime(time) { - return this.getEpochTime(time) - } - - /** - * Get real time from relative epoch time. - * @param {Number} epochTime - * @return {Number} - */ - getRealTime(epochTime) { - if (epochTime === undefined) { - epochTime = this.getTime() - } - - const start = Math.floor(this.beginEpochTime().valueOf() / 1000) * 1000 - - return start + epochTime * 1000 - } - - /** - * Get the current slot number. - * @param {Number} epochTime - * @return {Number} - */ - getSlotNumber(epochTime) { - if (epochTime === undefined) { - epochTime = this.getTime() - } - - return Math.floor(epochTime / this.getConstant('blocktime')) - } - - /** - * Get the current slot time. - * @param {Number} slot - * @return {Number} - */ - getSlotTime(slot) { - return slot * this.getConstant('blocktime') - } - - /** - * Get the next slot number. - * @return {Number} - */ - getNextSlot() { - return this.getSlotNumber() + 1 - } - - /** - * Get the last slot number. - * @param {Number} nextSlot - * @return {Number} - */ - getLastSlot(nextSlot) { - return nextSlot + this.getConstant('activeDelegates') - } - - /** - * Get constant from height 1. - * @param {String} key - * @return {*} - */ - getConstant(key) { - return configManager.getConstants(this.height)[key] - } - - /** - * Checks if forging is allowed - * @param {Number} epochTime - * @return {Boolean} - */ - isForgingAllowed(epochTime) { - if (epochTime === undefined) { - epochTime = this.getTime() - } - - const blockTime = this.getConstant('blocktime') - - return epochTime % blockTime < blockTime / 2 - } -} - -module.exports = new Slots() diff --git a/packages/crypto/lib/crypto/utils.js b/packages/crypto/lib/crypto/utils.js deleted file mode 100644 index 56abd43de4..0000000000 --- a/packages/crypto/lib/crypto/utils.js +++ /dev/null @@ -1,56 +0,0 @@ -const createHash = require('create-hash') - -class Utils { - /** - * Create a "ripemd160" buffer. - * @param {Buffer} buffer - * @return {Buffer} - */ - ripemd160(buffer) { - return createHash('rmd160') - .update(buffer) - .digest() - } - - /** - * Create a "sha1" buffer. - * @param {Buffer} buffer - * @return {Buffer} - */ - sha1(buffer) { - return createHash('sha1') - .update(buffer) - .digest() - } - - /** - * Create a "sha256" buffer. - * @param {Buffer} buffer - * @return {Buffer} - */ - sha256(buffer) { - return createHash('sha256') - .update(buffer) - .digest() - } - - /** - * Create a "hash160" buffer. - * @param {Buffer} buffer - * @return {Buffer} - */ - hash160(buffer) { - return this.ripemd160(this.sha256(buffer)) - } - - /** - * Create a "hash256" buffer. - * @param {Buffer} buffer - * @return {Buffer} - */ - hash256(buffer) { - return this.sha256(this.sha256(buffer)) - } -} - -module.exports = new Utils() diff --git a/packages/crypto/lib/handlers/transactions/delegate-registration.js b/packages/crypto/lib/handlers/transactions/delegate-registration.js deleted file mode 100644 index 80fbafea19..0000000000 --- a/packages/crypto/lib/handlers/transactions/delegate-registration.js +++ /dev/null @@ -1,47 +0,0 @@ -const Handler = require('./handler') - -class DelegateRegistrationHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - if (!super.canApply(wallet, transaction, errors)) { - return false - } - - const username = transaction.asset.delegate.username - // TODO: Checking whether the username is a lowercase version of itself seems silly. Why can't we mutate it to lowercase - const canApply = - !wallet.username && username && username === username.toLowerCase() - if (!canApply) { - errors.push('Wallet already has a registered username') - } - return canApply - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - wallet.username = transaction.asset.delegate.username - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - wallet.username = null - } -} - -module.exports = new DelegateRegistrationHandler() diff --git a/packages/crypto/lib/handlers/transactions/delegate-resignation.js b/packages/crypto/lib/handlers/transactions/delegate-resignation.js deleted file mode 100644 index eb913b545f..0000000000 --- a/packages/crypto/lib/handlers/transactions/delegate-resignation.js +++ /dev/null @@ -1,44 +0,0 @@ -const Handler = require('./handler') - -class DelegateResignationHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - if (!super.canApply(wallet, transaction, errors)) { - return false - } - - const canApply = !!wallet.username - if (!canApply) { - errors.push('Wallet has not registered a username') - } - return canApply - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - // - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - // - } -} - -module.exports = new DelegateResignationHandler() diff --git a/packages/crypto/lib/handlers/transactions/handler.js b/packages/crypto/lib/handlers/transactions/handler.js deleted file mode 100644 index 56a5ccfc04..0000000000 --- a/packages/crypto/lib/handlers/transactions/handler.js +++ /dev/null @@ -1,137 +0,0 @@ -const assert = require('assert') -const { crypto } = require('../../crypto') -const { transactionValidator } = require('../../validation') - -module.exports = class Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - const validationResult = transactionValidator.validate(transaction) - assert.ok(errors instanceof Array) - if (validationResult.fails) { - errors.push(validationResult.fails.message) - return false - } - - if (wallet.multisignature) { - if (!wallet.verifySignatures(transaction, wallet.multisignature)) { - errors.push('Failed to verify multi-signatures') - return false - } - } - - const balance = +wallet.balance - .minus(transaction.amount) - .minus(transaction.fee) - .toFixed() - if (balance < 0) { - errors.push('Insufficient balance in the wallet') - return false - } - if ( - !( - transaction.senderPublicKey.toLowerCase() === - wallet.publicKey.toLowerCase() - ) - ) { - errors.push( - 'wallet "publicKey" does not match transaction "senderPublicKey"', - ) - return false - } - - if ( - !wallet.secondPublicKey && - (transaction.secondSignature || transaction.signSignature) - ) { - errors.push('Invalid second-signature field') - return false - } - - // TODO: this can blow up if 2nd phrase and other transactions are in the wrong order - if ( - wallet.secondPublicKey && - !crypto.verifySecondSignature(transaction, wallet.secondPublicKey) - ) { - errors.push('Failed to verify second-signature') - return false - } - - return true - } - - /** - * Associate this wallet as the sender of a transaction. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - applyTransactionToSender(wallet, transaction) { - if ( - transaction.senderPublicKey.toLowerCase() === - wallet.publicKey.toLowerCase() || - crypto.getAddress(transaction.senderPublicKey) === wallet.address - ) { - wallet.balance = wallet.balance - .minus(transaction.amount) - .minus(transaction.fee) - - this.apply(wallet, transaction) - - wallet.dirty = true - } - } - - /** - * Remove this wallet as the sender of a transaction. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revertTransactionForSender(wallet, transaction) { - if ( - transaction.senderPublicKey.toLowerCase() === - wallet.publicKey.toLowerCase() || - crypto.getAddress(transaction.senderPublicKey) === wallet.address - ) { - wallet.balance = wallet.balance - .plus(transaction.amount) - .plus(transaction.fee) - - this.revert(wallet, transaction) - - wallet.dirty = true - } - } - - /** - * Add transaction balance to this wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - applyTransactionToRecipient(wallet, transaction) { - if (transaction.recipientId === wallet.address) { - wallet.balance = wallet.balance.plus(transaction.amount) - wallet.dirty = true - } - } - - /** - * Remove transaction balance from this wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revertTransactionForRecipient(wallet, transaction) { - if (transaction.recipientId === wallet.address) { - wallet.balance = wallet.balance.minus(transaction.amount) - wallet.dirty = true - } - } -} diff --git a/packages/crypto/lib/handlers/transactions/index.js b/packages/crypto/lib/handlers/transactions/index.js deleted file mode 100644 index 8d92b89d47..0000000000 --- a/packages/crypto/lib/handlers/transactions/index.js +++ /dev/null @@ -1,105 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../constants') - -class TransactionHandler { - /** - * [constructor description] - */ - constructor() { - this.handlers = { - [TRANSACTION_TYPES.TRANSFER]: require('./transfer'), - [TRANSACTION_TYPES.SECOND_SIGNATURE]: require('./second-signature'), - [TRANSACTION_TYPES.DELEGATE_REGISTRATION]: require('./delegate-registration'), - [TRANSACTION_TYPES.VOTE]: require('./vote'), - [TRANSACTION_TYPES.MULTI_SIGNATURE]: require('./multi-signature'), - [TRANSACTION_TYPES.IPFS]: require('./ipfs'), - [TRANSACTION_TYPES.TIMELOCK_TRANSFER]: require('./timelock-transfer'), - [TRANSACTION_TYPES.MULTI_PAYMENT]: require('./multi-payment'), - [TRANSACTION_TYPES.DELEGATE_RESIGNATION]: require('./delegate-resignation'), - } - } - - /** - * [canApply description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - return this.handlers[transaction.type].canApply(wallet, transaction, errors) - } - - /** - * [apply description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - return this.handlers[transaction.type].apply(wallet, transaction) - } - - /** - * [applyTransactionToSender description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - applyTransactionToSender(wallet, transaction) { - return this.handlers[transaction.type].applyTransactionToSender( - wallet, - transaction, - ) - } - - /** - * [applyTransactionToRecipient description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - applyTransactionToRecipient(wallet, transaction) { - return this.handlers[transaction.type].applyTransactionToRecipient( - wallet, - transaction, - ) - } - - /** - * [revert description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - return this.handlers[transaction.type].revert(wallet, transaction) - } - - /** - * [revertTransactionForSender description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revertTransactionForSender(wallet, transaction) { - return this.handlers[transaction.type].revertTransactionForSender( - wallet, - transaction, - ) - } - - /** - * [revertTransactionForRecipient description] - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revertTransactionForRecipient(wallet, transaction) { - return this.handlers[transaction.type].revertTransactionForRecipient( - wallet, - transaction, - ) - } -} - -module.exports = new TransactionHandler() diff --git a/packages/crypto/lib/handlers/transactions/ipfs.js b/packages/crypto/lib/handlers/transactions/ipfs.js deleted file mode 100644 index 56b434b50a..0000000000 --- a/packages/crypto/lib/handlers/transactions/ipfs.js +++ /dev/null @@ -1,36 +0,0 @@ -const Handler = require('./handler') - -class IpfsHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - return super.canApply(wallet, transaction, errors) - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - // - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - // - } -} - -module.exports = new IpfsHandler() diff --git a/packages/crypto/lib/handlers/transactions/multi-payment.js b/packages/crypto/lib/handlers/transactions/multi-payment.js deleted file mode 100644 index 0e308fd4da..0000000000 --- a/packages/crypto/lib/handlers/transactions/multi-payment.js +++ /dev/null @@ -1,54 +0,0 @@ -const Handler = require('./handler') -const Bignum = require('../../utils/bignum') - -class MultiPaymentHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - if (!super.canApply(wallet, transaction, errors)) { - return false - } - - const amount = transaction.asset.payments.reduce( - (total, payment) => total.plus(payment.amount), - Bignum.ZERO, - ) - - const canApply = - +wallet.balance - .minus(amount) - .minus(transaction.fee) - .toFixed() >= 0 - if (!canApply) { - errors.push('Insufficient balance in the wallet') - } - return canApply - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - // - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - // - } -} - -module.exports = new MultiPaymentHandler() diff --git a/packages/crypto/lib/handlers/transactions/multi-signature.js b/packages/crypto/lib/handlers/transactions/multi-signature.js deleted file mode 100644 index 1935b0b5cf..0000000000 --- a/packages/crypto/lib/handlers/transactions/multi-signature.js +++ /dev/null @@ -1,64 +0,0 @@ -const Handler = require('./handler') - -class MultiSignatureHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - if (!super.canApply(wallet, transaction, errors)) { - return false - } - - if (wallet.multisignature) { - errors.push('Wallet is already a multi-signature wallet') - return false - } - - const keysgroup = transaction.asset.multisignature.keysgroup - - if (keysgroup.length < transaction.asset.multisignature.min) { - errors.push('Specified key count does not meet minimum key count') - return false - } - - if (keysgroup.length !== transaction.signatures.length) { - errors.push('Specified key count does not equal signature count') - return false - } - - const canApply = wallet.verifySignatures( - transaction, - transaction.asset.multisignature, - ) - if (!canApply) { - errors.push('Failed to verify multi-signatures') - } - return canApply - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - wallet.multisignature = transaction.asset.multisignature - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - wallet.multisignature = null - } -} - -module.exports = new MultiSignatureHandler() diff --git a/packages/crypto/lib/handlers/transactions/second-signature.js b/packages/crypto/lib/handlers/transactions/second-signature.js deleted file mode 100644 index ca8901c515..0000000000 --- a/packages/crypto/lib/handlers/transactions/second-signature.js +++ /dev/null @@ -1,45 +0,0 @@ -const Handler = require('./handler') - -class SecondSignatureHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - if (wallet.secondPublicKey) { - errors.push('Wallet already has a second signature') - return false - } - - if (!super.canApply(wallet, transaction, errors)) { - return false - } - - return true - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - wallet.secondPublicKey = transaction.asset.signature.publicKey - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - delete wallet.secondPublicKey - } -} - -module.exports = new SecondSignatureHandler() diff --git a/packages/crypto/lib/handlers/transactions/timelock-transfer.js b/packages/crypto/lib/handlers/transactions/timelock-transfer.js deleted file mode 100644 index 260c748b25..0000000000 --- a/packages/crypto/lib/handlers/transactions/timelock-transfer.js +++ /dev/null @@ -1,36 +0,0 @@ -const Handler = require('./handler') - -class TimelockTransferHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - return super.canApply(wallet, transaction, errors) - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - // - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - // - } -} - -module.exports = new TimelockTransferHandler() diff --git a/packages/crypto/lib/handlers/transactions/transfer.js b/packages/crypto/lib/handlers/transactions/transfer.js deleted file mode 100644 index ee0560d71b..0000000000 --- a/packages/crypto/lib/handlers/transactions/transfer.js +++ /dev/null @@ -1,36 +0,0 @@ -const Handler = require('./handler') - -class TransferHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - return super.canApply(wallet, transaction, errors) - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - // - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - // - } -} - -module.exports = new TransferHandler() diff --git a/packages/crypto/lib/handlers/transactions/vote.js b/packages/crypto/lib/handlers/transactions/vote.js deleted file mode 100644 index 864f869058..0000000000 --- a/packages/crypto/lib/handlers/transactions/vote.js +++ /dev/null @@ -1,73 +0,0 @@ -const Handler = require('./handler') - -class VoteHandler extends Handler { - /** - * Check if the transaction can be applied to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(wallet, transaction, errors) { - if (!super.canApply(wallet, transaction, errors)) { - return false - } - - const vote = transaction.asset.votes[0] - if ( - vote.startsWith('-') && - (!wallet.vote || wallet.vote !== vote.slice(1)) - ) { - if (!wallet.vote) { - errors.push('Wallet has not voted yet') - } else { - errors.push('Wallet vote-choice does not match transaction vote-choice') - } - return false - } - - if (vote.startsWith('+') && wallet.vote) { - errors.push('Wallet has already voted') - return false - } - return true - } - - /** - * Apply the transaction to the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - apply(wallet, transaction) { - const vote = transaction.asset.votes[0] - - if (vote.startsWith('+')) { - wallet.vote = vote.slice(1) - } - - if (vote.startsWith('-')) { - wallet.vote = null - } - } - - /** - * Revert the transaction from the wallet. - * @param {Wallet} wallet - * @param {Transaction} transaction - * @return {void} - */ - revert(wallet, transaction) { - const vote = transaction.asset.votes[0] - - if (vote.startsWith('+')) { - wallet.vote = null - } - - if (vote.startsWith('-')) { - wallet.vote = vote.slice(1) - } - } -} - -module.exports = new VoteHandler() diff --git a/packages/crypto/lib/identities/address.js b/packages/crypto/lib/identities/address.js deleted file mode 100644 index 9d317d941f..0000000000 --- a/packages/crypto/lib/identities/address.js +++ /dev/null @@ -1,49 +0,0 @@ -const bs58check = require('bs58check') -const configManager = require('../managers/config') -const utils = require('../crypto/utils') -const PublicKey = require('./public-key') - -module.exports = class Address { - static fromPassphrase(passphrase, networkVersion) { - return Address.fromPublicKey( - PublicKey.fromPassphrase(passphrase), - networkVersion, - ) - } - - static fromPublicKey(publicKey, networkVersion) { - const pubKeyRegex = /^[0-9A-Fa-f]{66}$/ - if (!pubKeyRegex.test(publicKey)) { - throw new Error(`publicKey '${publicKey}' is invalid`) - } - - if (!networkVersion) { - networkVersion = configManager.get('pubKeyHash') - } - - const buffer = utils.ripemd160(Buffer.from(publicKey, 'hex')) - const payload = Buffer.alloc(21) - - payload.writeUInt8(networkVersion, 0) - buffer.copy(payload, 1) - - return bs58check.encode(payload) - } - - static fromPrivateKey(privateKey, networkVersion) { - return Address.fromPublicKey(privateKey.publicKey, networkVersion) - } - - static validate(address, networkVersion) { - if (!networkVersion) { - networkVersion = configManager.get('pubKeyHash') - } - - try { - const decode = bs58check.decode(address) - return decode[0] === networkVersion - } catch (e) { - return false - } - } -} diff --git a/packages/crypto/lib/identities/keys.js b/packages/crypto/lib/identities/keys.js deleted file mode 100644 index 2cd57ca74d..0000000000 --- a/packages/crypto/lib/identities/keys.js +++ /dev/null @@ -1,49 +0,0 @@ -const secp256k1 = require('secp256k1') -const wif = require('wif') - -const configManager = require('../managers/config') -const utils = require('../crypto/utils') - -module.exports = class Keys { - static fromPassphrase(passphrase, compressed = true) { - const privateKey = utils.sha256(Buffer.from(passphrase, 'utf8')) - return Keys.fromPrivateKey(privateKey, compressed) - } - - static fromPrivateKey(privateKey, compressed = true) { - privateKey = privateKey instanceof Buffer ? privateKey : Buffer.from(privateKey, 'hex') - - const publicKey = secp256k1.publicKeyCreate(privateKey, compressed) - const keyPair = { - publicKey: publicKey.toString('hex'), - privateKey: privateKey.toString('hex'), - compressed, - } - - return keyPair - } - - static fromWIF(wifKey, network) { - const decoded = wif.decode(wifKey) - const version = decoded.version - - if (!network) { - network = configManager.all() - } - - if (version !== network.wif) { - throw new Error('Invalid network version') - } - - const privateKey = decoded.privateKey - const publicKey = secp256k1.publicKeyCreate(privateKey, decoded.compressed) - - const keyPair = { - publicKey: publicKey.toString('hex'), - privateKey: privateKey.toString('hex'), - compressed: decoded.compressed, - } - - return keyPair - } -} diff --git a/packages/crypto/lib/identities/private-key.js b/packages/crypto/lib/identities/private-key.js deleted file mode 100644 index 98e351e0b4..0000000000 --- a/packages/crypto/lib/identities/private-key.js +++ /dev/null @@ -1,13 +0,0 @@ -const Keys = require('./keys') - -module.exports = class PrivateKey { - static fromPassphrase(passphrase) { - return Keys.fromPassphrase(passphrase).privateKey - } - - // static fromHex (privateKey) {} - - static fromWIF(wif, network) { - return Keys.fromWIF(wif, network).privateKey - } -} diff --git a/packages/crypto/lib/identities/public-key.js b/packages/crypto/lib/identities/public-key.js deleted file mode 100644 index c4d67d2d1a..0000000000 --- a/packages/crypto/lib/identities/public-key.js +++ /dev/null @@ -1,27 +0,0 @@ -const configManager = require('../managers/config') -const Address = require('./address') -const Keys = require('./keys') - -module.exports = class PublicKey { - static fromPassphrase(passphrase) { - return Keys.fromPassphrase(passphrase).publicKey - } - - // static fromHex (publicKey) {} - - static fromWIF(wif, network) { - return Keys.fromWIF(wif, network).publicKey - } - - static validate(publicKey, networkVersion) { - if (!networkVersion) { - networkVersion = configManager.get('pubKeyHash') - } - - try { - return Address.fromPublicKey(publicKey, networkVersion).length === 34 - } catch (e) { - return false - } - } -} diff --git a/packages/crypto/lib/identities/wif.js b/packages/crypto/lib/identities/wif.js deleted file mode 100644 index 5b380159c8..0000000000 --- a/packages/crypto/lib/identities/wif.js +++ /dev/null @@ -1,19 +0,0 @@ -const wif = require('wif') -const configManager = require('../managers/config') -const Keys = require('./keys') - -module.exports = class WIF { - static fromPassphrase(passphrase, network) { - const keys = Keys.fromPassphrase(passphrase) - - if (!network) { - network = configManager.all() - } - - return wif.encode( - network.wif, - Buffer.from(keys.privateKey, 'hex'), - keys.compressed, - ) - } -} diff --git a/packages/crypto/lib/index.js b/packages/crypto/lib/index.js deleted file mode 100644 index 00a93f966a..0000000000 --- a/packages/crypto/lib/index.js +++ /dev/null @@ -1,42 +0,0 @@ -module.exports = { - // Client... - client: require('./client'), - - // Models... - models: { - Block: require('./models/block'), - Delegate: require('./models/delegate'), - Transaction: require('./models/transaction'), - Wallet: require('./models/wallet'), - }, - - // Identities... - identities: { - address: require('./identities/address'), - keys: require('./identities/keys'), - privateKey: require('./identities/private-key'), - publicKey: require('./identities/public-key'), - wif: require('./identities/wif'), - }, - - // Builder... - transactionBuilder: require('./builder'), - - // Crypto... - ...require('./crypto'), - - // Managers... - configManager: require('./managers/config'), - feeManager: require('./managers/fee'), - NetworkManager: require('./managers/network'), - dynamicFeeManager: require('./managers/dynamic-fee'), - - // Constants... - constants: require('./constants'), - - // Utils... - ...require('./utils'), - - // Validations - ...require('./validation'), -} diff --git a/packages/crypto/lib/managers/config.js b/packages/crypto/lib/managers/config.js deleted file mode 100644 index 0e3859417c..0000000000 --- a/packages/crypto/lib/managers/config.js +++ /dev/null @@ -1,171 +0,0 @@ -const camelCase = require('lodash/camelCase') -const deepmerge = require('deepmerge') -const feeManager = require('./fee') -const dynamicFeeManager = require('./dynamic-fee') - -const { TRANSACTION_TYPES, CONFIGURATIONS } = require('../constants') -const defaultConfig = require('../networks/ark/devnet.json') - -class ConfigManager { - /** - * @constructor - */ - constructor() { - this.setConfig(defaultConfig) - } - - /** - * Set config data. - * @param {Object} config - */ - setConfig(config) { - this.config = {} - - for (const [key, value] of Object.entries(config)) { - this.config[key] = value - } - - this.buildConstants() - this.buildFees() - this.buildAddonBytes() - } - - /** - * Get config from preset configurations. - * @param {String} coin - * @param {String} network - */ - setFromPreset(coin, network) { - this.setConfig(CONFIGURATIONS[coin.toUpperCase()][network.toUpperCase()]) - } - - /** - * Get all config data. - * @return {Object} - */ - all() { - return this.config - } - - /** - * Set individual config value. - * @param {String} key - * @param {*} value - */ - set(key, value) { - this.config[key] = value - } - - /** - * Get specific config value. - * @param {String} key - * @return {*} - */ - get(key) { - return this.config[key] - } - - /** - * Set config manager height. - * @param {Number} value - */ - setHeight(value) { - this.height = value - } - - /** - * Get config manager height. - * @return {Number} - */ - getHeight() { - return this.height - } - - /** - * Get specific config constant based on height 1. - * @param {String} key - * @return {*} - */ - getConstant(key) { - return this.getConstants()[key] - } - - /** - * Get all config constants based on height. - * @param {(Number|undefined)} height - * @return {*} - */ - getConstants(height) { - if (!height && this.height) { - height = this.height - } - - if (!height) { - height = 1 - } - - while ( - this.constant.index < this.constants.length - 1 && - height >= this.constants[this.constant.index + 1].height - ) { - this.constant.index++ - this.constant.data = this.constants[this.constant.index] - } - - while (height < this.constants[this.constant.index].height) { - this.constant.index-- - this.constant.data = this.constants[this.constant.index] - } - - return this.constant.data - } - - /** - * Build constant data based on active heights. - */ - buildConstants() { - this.constants = this.config.constants.sort((a, b) => a.height - b.height) - this.constant = { - index: 0, - data: this.constants[0], - } - - let lastmerged = 0 - - while (lastmerged < this.constants.length - 1) { - this.constants[lastmerged + 1] = deepmerge( - this.constants[lastmerged], - this.constants[lastmerged + 1], - ) - lastmerged++ - } - } - - /** - * Build fees from config constants. - */ - buildFees() { - Object.keys(TRANSACTION_TYPES).forEach(type => - feeManager.set( - TRANSACTION_TYPES[type], - this.getConstant('fees').staticFees[camelCase(type)], - ), - ) - } - - /** - * Build addon bytes from config constants. - */ - buildAddonBytes() { - if (this.getConstant('fees').dynamicFees.addonBytes) { - Object.keys(TRANSACTION_TYPES).forEach(type => - dynamicFeeManager.set( - TRANSACTION_TYPES[type], - this.getConstant('fees').dynamicFees.addonBytes[camelCase(type)], - ), - ) - } - } -} - -module.exports = new ConfigManager() diff --git a/packages/crypto/lib/managers/dynamic-fee.js b/packages/crypto/lib/managers/dynamic-fee.js deleted file mode 100644 index d2a73b5e63..0000000000 --- a/packages/crypto/lib/managers/dynamic-fee.js +++ /dev/null @@ -1,62 +0,0 @@ -const { TRANSACTION_TYPES } = require('../constants') - -class DynamicFeeManager { - /** - * @constructor - */ - constructor() { - this.offsets = {} - } - - /** - * Calculate minimum fee of a transaction for entering the pool. - * @param {Number} Minimum fee ARKTOSHI/byte - * @param {Transaction} Transaction for which we calculate the fee - * @returns {Number} Calculated minimum acceptable fee in ARKTOSHI - */ - calculateFee(arktoshiPerByte, transaction) { - if (arktoshiPerByte <= 0) { - arktoshiPerByte = 1 - } - - // serialized is in hex - const transactionSizeInBytes = transaction.serialized.length / 2 - - return ( - (this.get(transaction.type) + transactionSizeInBytes) * arktoshiPerByte - ) - } - - /** - * Get offsset value based on transaction. - * @param {Number} type - * @return {Number} - */ - get(type) { - return this.offsets[type] - } - - /** - * Set offset value based on type. - * @param {Number} type - * @param {Number} value - */ - set(type, value) { - if (!this.__validType(type)) { - throw new Error('Invalid transaction type.') - } - - this.offsets[type] = value - } - - /** - * Ensure transaction type is valid. - * @param {Number} type - * @return {Boolean} - */ - __validType(type) { - return Object.values(TRANSACTION_TYPES).indexOf(type) > -1 - } -} - -module.exports = new DynamicFeeManager() diff --git a/packages/crypto/lib/managers/fee.js b/packages/crypto/lib/managers/fee.js deleted file mode 100644 index 6b791a8721..0000000000 --- a/packages/crypto/lib/managers/fee.js +++ /dev/null @@ -1,59 +0,0 @@ -const { TRANSACTION_TYPES } = require('../constants') - -class FeeManager { - /** - * @constructor - */ - constructor() { - this.fees = {} - } - - /** - * Set fee value based on type. - * @param {Number} type - * @param {Number} value - */ - set(type, value) { - if (!this.__validType(type)) { - throw new Error('Invalid transaction type.') - } - - this.fees[type] = value - } - - /** - * Get fee value based on type. - * @param {Number} type - * @return {Number} - */ - get(type) { - return this.fees[type] - } - - /** - * Get fee value based on type. - * @param {Transaction} transaction - * @return {Number} - */ - getForTransaction(transaction) { - if (transaction.type === TRANSACTION_TYPES.MULTI_SIGNATURE) { - return ( - this.fees[transaction.type] - * (transaction.asset.multisignature.keysgroup.length + 1) - ) - } - - return this.fees[transaction.type] - } - - /** - * Ensure fee type is valid. - * @param {Number} type - * @return {Boolean} - */ - __validType(type) { - return Object.values(TRANSACTION_TYPES).indexOf(type) > -1 - } -} - -module.exports = new FeeManager() diff --git a/packages/crypto/lib/managers/network.js b/packages/crypto/lib/managers/network.js deleted file mode 100644 index 150a02f16a..0000000000 --- a/packages/crypto/lib/managers/network.js +++ /dev/null @@ -1,22 +0,0 @@ -const get = require('lodash/get') -const networks = require('../networks') - -module.exports = class NetworkManager { - /** - * Get all network types. - * @return {Object} - */ - static getAll() { - return networks - } - - /** - * Find network by token and name. - * @param {String} name - * @param {String} [token=ark] - * @return {Object} - */ - static findByName(name, token = 'ark') { - return get(networks, `${token.toLowerCase()}.${name}`) - } -} diff --git a/packages/crypto/lib/models/block.js b/packages/crypto/lib/models/block.js deleted file mode 100644 index be66fef1e9..0000000000 --- a/packages/crypto/lib/models/block.js +++ /dev/null @@ -1,585 +0,0 @@ -const cloneDeepWith = require('lodash/cloneDeepWith') -const { createHash } = require('crypto') -const pluralize = require('pluralize') -const ByteBuffer = require('bytebuffer') -const { Bignum } = require('../utils') -const Transaction = require('./transaction') -const configManager = require('../managers/config') -const { crypto, slots } = require('../crypto') -const { outlookTable } = require('../constants').CONFIGURATIONS.ARK.MAINNET - -const toBytesHex = data => { - const temp = data ? new Bignum(data).toString(16) : '' - return '0'.repeat(16 - temp.length) + temp -} - -/** - * TODO copy some parts to ArkDocs - * @classdesc This model holds the block data, its verification and serialization - * - * A Block model stores on the db: - * - id - * - version (version of the block: could be used for changing how they are forged) - * - timestamp (related to the genesis block) - * - previousBlock (id of the previous block) - * - height - * - numberOfTransactions - * - totalAmount (in arktoshi) - * - totalFee (in arktoshi) - * - reward (in arktoshi) - * - payloadHash (hash of the transactions) - * - payloadLength (total length in bytes of the IDs of the transactions) - * - generatorPublicKey (public key of the delegate that forged this block) - * - blockSignature - * - * The `transactions` are stored too, but in a different table. - * - * These data is exposed through the `data` attributed as a plain object and - * serialized through the `serialized` attribute. - * - * In the future the IDs could be changed to use the hexadecimal version of them, - * which would be more efficient for performance, disk usage and bandwidth reasons. - * That is why there are some attributes, such as `idHex` and `previousBlockHex`. - */ -module.exports = class Block { - /** - * @constructor - * @param {Object} data - The data of the block - */ - constructor(data) { - if (typeof data === 'string') { - data = Block.deserialize(data) - } - - if (!data.transactions) { - data.transactions = [] - } - if ( - data.numberOfTransactions > 0 && - data.transactions.length === data.numberOfTransactions - ) { - delete data.transactionIds - } - - this.headerOnly = - data.numberOfTransactions > 0 && - data.transactionIds && - data.transactionIds.length === data.numberOfTransactions - if (this.headerOnly) { - this.serialized = Block.serialize(data).toString('hex') - } else { - this.serialized = Block.serializeFull(data).toString('hex') - } - this.data = Block.deserialize(this.serialized) - - this.data.id = Block.getId(this.data) - this.data.idHex = Block.getIdHex(this.data) - - if (outlookTable[this.data.id]) { - this.data.id = outlookTable[this.data.id] - this.data.idHex = toBytesHex(this.data.id) - } - - if (data.height === 1) { - this.genesis = true - // TODO genesis block calculated id is wrong for some reason - this.data.id = data.id - this.data.idHex = toBytesHex(this.data.id) - delete this.data.previousBlock - } - - // fix on real timestamp, this is overloading transaction - // timestamp with block timestamp for storage only - // also add sequence to keep database sequence - let sequence = 0 - this.transactions = data.transactions.map(transaction => { - const stampedTransaction = new Transaction(transaction) - stampedTransaction.blockId = this.data.id - stampedTransaction.timestamp = this.data.timestamp - stampedTransaction.sequence = sequence++ - return stampedTransaction - }) - - delete this.data.transactions - if (data.transactionIds && data.transactionIds.length > 0) { - this.transactionIds = data.transactionIds - } - - this.verification = this.verify() - - // order of transactions messed up in mainnet V1 - // TODO: move this to network constants exception using block ids - if ( - this.transactions && - this.data.numberOfTransactions === 2 && - (this.data.height === 3084276 || this.data.height === 34420) - ) { - const temp = this.transactions[0] - this.transactions[0] = this.transactions[1] - this.transactions[1] = temp - } - } - - /** - * Create block from data. - * @param {Object} data - * @param {Object} keys - * @return {Block} - * @static - */ - static create(data, keys) { - data.generatorPublicKey = keys.publicKey - - const payloadHash = Block.serialize(data, false) - const hash = createHash('sha256') - .update(payloadHash) - .digest() - - data.blockSignature = crypto.signHash(hash, keys) - data.id = Block.getId(data) - - return new Block(data) - } - - /** - * Return block as string. - * @return {String} - */ - toString() { - return `${ - this.data.id - }, height: ${this.data.height.toLocaleString()}, ${pluralize( - 'transaction', - this.data.numberOfTransactions, - true, - )}, verified: ${this.verification.verified}, errors: ${ - this.verification.errors - }` // eslint-disable-line max-len - } - - /* - * Get block id - * @param {Object} data - * @return {String} - * @static - */ - static getIdHex(data) { - const hash = createHash('sha256') - .update(Block.serialize(data, true)) - .digest() - const temp = Buffer.alloc(8) - - for (let i = 0; i < 8; i++) { - temp[i] = hash[7 - i] - } - return temp.toString('hex') - } - - /** - * Get block id from already serialized buffer - * @param {Buffer} serialized block buffer with block-signature included - * @return {String} - * @static - */ - static getIdFromSerialized(serializedBuffer) { - const hash = createHash('sha256') - .update(serializedBuffer) - .digest() - const temp = Buffer.alloc(8) - - for (let i = 0; i < 8; i++) { - temp[i] = hash[7 - i] - } - return new Bignum(temp.toString('hex'), 16).toFixed() - } - - static getId(data) { - const idHex = Block.getIdHex(data) - return new Bignum(idHex, 16).toFixed() - } - - /** - * Get header from block. - * @return {Object} The block data, without the transactions - */ - getHeader() { - const header = Object.assign({}, this.data) - delete header.transactions - return header - } - - /** - * Verify signature associated with this block. - * @return {Boolean} - */ - verifySignature() { - const bytes = Block.serialize(this.data, false) - const hash = createHash('sha256') - .update(bytes) - .digest() - - return crypto.verifyHash( - hash, - this.data.blockSignature, - this.data.generatorPublicKey, - ) - } - - /** - * Verify this block. - * @return {Object} - */ - verify() { - const block = this.data - const result = { - verified: false, - errors: [], - } - - try { - const constants = configManager.getConstants(block.height) - - // let previousBlock = null - - if (block.height !== 1) { - if (!block.previousBlock) { - result.errors.push('Invalid previous block') - } - } - - if (!block.reward.isEqualTo(constants.reward)) { - result.errors.push( - [ - 'Invalid block reward:', - block.reward, - 'expected:', - constants.reward, - ].join(' '), - ) - } - - const valid = this.verifySignature(block) - - if (!valid) { - result.errors.push('Failed to verify block signature') - } - - if (block.version !== constants.block.version) { - result.errors.push('Invalid block version') - } - - if (slots.getSlotNumber(block.timestamp) > slots.getSlotNumber()) { - result.errors.push('Invalid block timestamp') - } - - // Disabling to allow orphanedBlocks? - // if(previousBlock){ - // const lastBlockSlotNumber = slots.getSlotNumber(previousBlock.timestamp) - // if(blockSlotNumber < lastBlockSlotNumber) { - // result.errors.push('block timestamp is smaller than previous block timestamp') - // } - // } - - let size = 0 - const payloadHash = createHash('sha256') - - if (this.headerOnly) { - if (this.transactionIds.length !== block.numberOfTransactions) { - result.errors.push('Invalid number of transactions') - } - - if (this.transactionIds.length > constants.block.maxTransactions) { - if (block.height > 1) - result.errors.push('Transactions length is too high') - } - - // Checking if transactions of the block adds up to block values. - const appliedTransactions = {} - this.transactionIds.forEach(id => { - const bytes = Buffer.from(id, 'hex') - - if (appliedTransactions[id]) { - result.errors.push(`Encountered duplicate transaction: ${id}`) - } - - appliedTransactions[id] = id - size += bytes.length - - payloadHash.update(bytes) - }) - } else { - const invalidTransactions = this.transactions.filter(tx => !tx.verified) - if (invalidTransactions.length > 0) { - result.errors.push('One or more transactions are not verified:') - invalidTransactions.forEach(tx => - result.errors.push(`=> ${tx.serialized}`), - ) - } - - if (this.transactions.length !== block.numberOfTransactions) { - result.errors.push('Invalid number of transactions') - } - - if (this.transactions.length > constants.block.maxTransactions) { - if (block.height > 1) - result.errors.push('Transactions length is too high') - } - - // Checking if transactions of the block adds up to block values. - const appliedTransactions = {} - let totalAmount = Bignum.ZERO - let totalFee = Bignum.ZERO - this.transactions.forEach(transaction => { - const bytes = Buffer.from(transaction.data.id, 'hex') - - if (appliedTransactions[transaction.data.id]) { - result.errors.push( - `Encountered duplicate transaction: ${transaction.data.id}`, - ) - } - - appliedTransactions[transaction.data.id] = transaction.data - - totalAmount = totalAmount.plus(transaction.data.amount) - totalFee = totalFee.plus(transaction.data.fee) - size += bytes.length - - payloadHash.update(bytes) - }) - - if (!totalAmount.isEqualTo(block.totalAmount)) { - result.errors.push('Invalid total amount') - } - - if (!totalFee.isEqualTo(block.totalFee)) { - result.errors.push('Invalid total fee') - } - } - - if (size > constants.block.maxPayload) { - result.errors.push('Payload is too large') - } - - if ( - !this.genesis && - payloadHash.digest().toString('hex') !== block.payloadHash - ) { - result.errors.push('Invalid payload hash') - } - } catch (error) { - result.errors.push(error) - } - - result.verified = result.errors.length === 0 - - return result - } - - /** - * Deserialize block from hex string. - * @param {String} hexString - * @param {Boolean} headerOnly - deserialize onlu headers - * @return {Object} - * @static - */ - static deserialize(hexString, headerOnly = false) { - const block = {} - const buf = ByteBuffer.fromHex(hexString, true) - block.version = buf.readUInt32(0) - block.timestamp = buf.readUInt32(4) - block.height = buf.readUInt32(8) - block.previousBlockHex = buf.slice(12, 20).toString('hex') - block.previousBlock = new Bignum(block.previousBlockHex, 16).toFixed() - block.numberOfTransactions = buf.readUInt32(20) - block.totalAmount = new Bignum(buf.readUInt64(24)) - block.totalFee = new Bignum(buf.readUInt64(32)) - block.reward = new Bignum(buf.readUInt64(40)) - block.payloadLength = buf.readUInt32(48) - block.payloadHash = hexString.substring(104, 104 + 64) - block.generatorPublicKey = hexString.substring(104 + 64, 104 + 64 + 33 * 2) - - const length = - parseInt( - `0x${hexString.substring( - 104 + 64 + 33 * 2 + 2, - 104 + 64 + 33 * 2 + 4, - )}`, - 16, - ) + 2 - block.blockSignature = hexString.substring( - 104 + 64 + 33 * 2, - 104 + 64 + 33 * 2 + length * 2, - ) - - if (headerOnly) return block - - let transactionOffset = (104 + 64 + 33 * 2 + length * 2) / 2 - block.transactions = [] - if (hexString.length === transactionOffset * 2) return block - - // A serialized block stores transactions like this: - // |L1|L2|L3|...|LN| TX1 | TX2 | TX3 | ... | TXN | - // Each L is 4 bytes and denotes the length in bytes of the corresponding TX. - const lengthOffset = transactionOffset // Position right before L1 - transactionOffset += block.numberOfTransactions * 4 // Position right after LN - - for (let i = 0; i < block.numberOfTransactions; i++) { - const transactionLength = buf.readUint32(lengthOffset + i * 4) - - const transaction = Transaction.deserialize( - buf - .slice(transactionOffset, transactionOffset + transactionLength) - .toString('hex'), - ) - block.transactions.push(transaction) - - transactionOffset += transactionLength - } - - return block - } - - /** - * Serialize block. - * @param {Object} data - * @return {Buffer} - * @static - */ - static serializeFull(block) { - const serializedBlock = Block.serialize(block, true) - const transactions = block.transactions - - const buf = new ByteBuffer( - serializedBlock.length + transactions.length * 4, - true, - ) - .append(serializedBlock) - .skip(transactions.length * 4) - - for (let i = 0; i < transactions.length; i++) { - const serialized = Transaction.serialize(transactions[i]) - buf.writeUint32(serialized.length, serializedBlock.length + i * 4) - buf.append(serialized) - } - - return buf.flip().toBuffer() - } - - /** - * Serialize block - * TODO split this method between bufferize (as a buffer) and serialize (as hex) - * @param {Object} block - * @param {(Boolean|undefined)} includeSignature - * @return {Buffer} - * @static - */ - static serialize(block, includeSignature = true) { - block.previousBlockHex = toBytesHex(block.previousBlock) - - const bb = new ByteBuffer(256, true) - bb.writeUInt32(block.version) - bb.writeUInt32(block.timestamp) - bb.writeUInt32(block.height) - bb.append(block.previousBlockHex, 'hex') - bb.writeUInt32(block.numberOfTransactions) - bb.writeUInt64(+new Bignum(block.totalAmount).toFixed()) - bb.writeUInt64(+new Bignum(block.totalFee).toFixed()) - bb.writeUInt64(+new Bignum(block.reward).toFixed()) - bb.writeUInt32(block.payloadLength) - bb.append(block.payloadHash, 'hex') - bb.append(block.generatorPublicKey, 'hex') - - if (includeSignature && block.blockSignature) { - bb.append(block.blockSignature, 'hex') - } - - bb.flip() - return bb.toBuffer() - } - - static getBytesV1(block, includeSignature) { - if (includeSignature === undefined) { - includeSignature = block.blockSignature !== undefined - } - - let size = 4 + 4 + 4 + 8 + 4 + 4 + 8 + 8 + 4 + 4 + 4 + 32 + 33 - let blockSignatureBuffer = null - - if (includeSignature) { - blockSignatureBuffer = Buffer.from(block.blockSignature, 'hex') - size += blockSignatureBuffer.length - } - - let b - - try { - const bb = new ByteBuffer(size, true) - bb.writeInt(block.version) - bb.writeInt(block.timestamp) - bb.writeInt(block.height) - - let i - - if (block.previousBlock) { - const pb = Buffer.from( - new Bignum(block.previousBlock).toString(16), - 'hex', - ) - - for (i = 0; i < 8; i++) { - bb.writeByte(pb[i]) - } - } else { - for (i = 0; i < 8; i++) { - bb.writeByte(0) - } - } - - bb.writeInt(block.numberOfTransactions) - bb.writeLong(+block.totalAmount.toFixed()) - bb.writeLong(+block.totalFee.toFixed()) - bb.writeLong(+block.reward.toFixed()) - - bb.writeInt(block.payloadLength) - - const payloadHashBuffer = Buffer.from(block.payloadHash, 'hex') - for (i = 0; i < payloadHashBuffer.length; i++) { - bb.writeByte(payloadHashBuffer[i]) - } - - const generatorPublicKeyBuffer = Buffer.from( - block.generatorPublicKey, - 'hex', - ) - for (i = 0; i < generatorPublicKeyBuffer.length; i++) { - bb.writeByte(generatorPublicKeyBuffer[i]) - } - - if (includeSignature) { - for (i = 0; i < blockSignatureBuffer.length; i++) { - bb.writeByte(blockSignatureBuffer[i]) - } - } - - bb.flip() - b = bb.toBuffer() - } catch (e) { - throw e - } - - return b - } - - toJson() { - // Convert Bignums - const blockData = cloneDeepWith(this.data, (value, key) => { - if (['reward', 'totalAmount', 'totalFee'].indexOf(key) !== -1) { - return +value.toFixed() - } - }) - - return Object.assign(blockData, { - transactions: this.transactions.map(transaction => transaction.toJson()), - }) - } -} diff --git a/packages/crypto/lib/models/delegate.js b/packages/crypto/lib/models/delegate.js deleted file mode 100644 index 3fa3eb454f..0000000000 --- a/packages/crypto/lib/models/delegate.js +++ /dev/null @@ -1,208 +0,0 @@ -const bip38 = require('bip38') -const wif = require('wif') -const { createHash } = require('crypto') -const otplib = require('otplib') -const forge = require('node-forge') -const Bignum = require('../utils/bignum') - -const Block = require('./block') -const crypto = require('../crypto/crypto') -const sortTransactions = require('../utils/sort-transactions') - -/** - * TODO copy some parts to ArkDocs - * @classdesc The delegate model - * - * The Delegate model does not store anything on db, but the object contains: - * - network - * - keySize - * - iterations (used for generating the cypher) - * - publicKey - * - address - * - keys - * - otpSecret - * - bip38 - */ -module.exports = class Delegate { - /** - * @constructor - * @param {String} passphrase - * @param {Number} network - * @param {String} password - */ - constructor(passphrase, network, password) { - this.network = network - this.keySize = 32 // AES-256 - this.iterations = 5000 - - if (bip38.verify(passphrase)) { - try { - this.keys = Delegate.decryptPassphrase(passphrase, network, password) - this.publicKey = this.keys.publicKey - this.address = crypto.getAddress( - this.keys.publicKey, - network.pubKeyHash, - ) - this.otpSecret = otplib.authenticator.generateSecret() - this.bip38 = true - this.encryptKeysWithOtp() - } catch (error) { - this.publicKey = null - this.keys = null - this.address = null - } - } else { - this.keys = crypto.getKeys(passphrase) - this.publicKey = this.keys.publicKey - this.address = crypto.getAddress(this.publicKey, network.pubKeyHash) - } - } - - /** - * BIP38 encrypt passphrase. - * @param {String} passphrase - * @param {Number} network - * @param {String} password - * @return {String} - * @static - */ - static encryptPassphrase(passphrase, network, password) { - const keys = crypto.getKeys(passphrase) - const decoded = wif.decode(crypto.keysToWIF(keys, network)) - - return bip38.encrypt(decoded.privateKey, decoded.compressed, password) - } - - /** - * BIP38 decrypt passphrase keys. - * @param {String} passphrase - * @param {Number} network - * @param {String} password - * @return {Object} - * @static - */ - static decryptPassphrase(passphrase, network, password) { - const decryptedWif = bip38.decrypt(passphrase, password) - const wifKey = wif.encode( - network.wif, - decryptedWif.privateKey, - decryptedWif.compressed, - ) - return crypto.getKeysFromWIF(wifKey, network) - } - - /** - * Encrypt keys with one time password - used to store encrypted in memory. - */ - encryptKeysWithOtp() { - this.otp = otplib.authenticator.generate(this.otpSecret) - const wifKey = crypto.keysToWIF(this.keys, this.network) - this.encryptedKeys = this.__encryptData(wifKey, this.otp) - this.keys = null - } - - /** - * Decrypt keys with one time password. - */ - decryptKeysWithOtp() { - const wifKey = this.__decryptData(this.encryptedKeys, this.otp) - this.keys = crypto.getKeysFromWIF(wifKey, this.network) - this.otp = null - this.encryptedKeys = null - } - - /** - * Forge block - we consider transactions are signed, verified and unique. - * @param {Transaction[]} transactions - * @param {Object} options - * @return {(Block|undefined)} - */ - forge(transactions, options) { - if (!options.version && (this.encryptedKeys || !this.bip38)) { - const transactionData = { - amount: Bignum.ZERO, - fee: Bignum.ZERO, - sha256: createHash('sha256'), - } - - const sortedTransactions = sortTransactions(transactions) - sortedTransactions.forEach(transaction => { - transactionData.amount = transactionData.amount.plus(transaction.amount) - transactionData.fee = transactionData.fee.plus(transaction.fee) - transactionData.sha256.update(Buffer.from(transaction.id, 'hex')) - }) - - const data = { - version: 0, - generatorPublicKey: this.publicKey, - timestamp: options.timestamp, - previousBlock: options.previousBlock.id, - previousBlockHex: options.previousBlock.idHex, - height: options.previousBlock.height + 1, - numberOfTransactions: sortedTransactions.length, - totalAmount: transactionData.amount, - totalFee: transactionData.fee, - reward: options.reward, - payloadLength: 32 * sortedTransactions.length, - payloadHash: transactionData.sha256.digest().toString('hex'), - transactions: sortedTransactions, - } - - if (this.bip38) { - this.decryptKeysWithOtp() - } - - const block = Block.create(data, this.keys) - - if (this.bip38) { - this.encryptKeysWithOtp() - } - - return block - } - - return false - } - - /** - * Perform OTP encryption. - * @param {String} content - * @param {String} password - * @return {String} - */ - __encryptData(content, password) { - const derivedKey = forge.pkcs5.pbkdf2( - password, - this.otpSecret, - this.iterations, - this.keySize, - ) - const cipher = forge.cipher.createCipher('AES-CBC', derivedKey) - cipher.start({ iv: forge.util.decode64(this.otp) }) - cipher.update(forge.util.createBuffer(content)) - cipher.finish() - - return forge.util.encode64(cipher.output.getBytes()) - } - - /** - * Perform OTP decryption. - * @param {String} cipherText - * @param {String} password - * @return {String} - */ - __decryptData(cipherText, password) { - const derivedKey = forge.pkcs5.pbkdf2( - password, - this.otpSecret, - this.iterations, - this.keySize, - ) - const decipher = forge.cipher.createDecipher('AES-CBC', derivedKey) - decipher.start({ iv: forge.util.decode64(this.otp) }) - decipher.update(forge.util.createBuffer(forge.util.decode64(cipherText))) - decipher.finish() - - return decipher.output.toString() - } -} diff --git a/packages/crypto/lib/models/transaction.js b/packages/crypto/lib/models/transaction.js deleted file mode 100644 index 95ea3c70c1..0000000000 --- a/packages/crypto/lib/models/transaction.js +++ /dev/null @@ -1,494 +0,0 @@ -/* eslint no-bitwise: "off" */ - -const bs58check = require('bs58check') -const cloneDeepWith = require('lodash/cloneDeepWith') -const ByteBuffer = require('bytebuffer') -const { createHash } = require('crypto') -const { Bignum } = require('../utils') -const crypto = require('../crypto/crypto') -const configManager = require('../managers/config') -const { TRANSACTION_TYPES } = require('../constants') -const { - transactionIdFixTable, -} = require('../constants').CONFIGURATIONS.ARK.MAINNET - -/** - * TODO copy some parts to ArkDocs - * @classdesc This model holds the transaction data and its serialization - * - * A Transaction stores on the db: - * - id - * - version (version of the transaction generation process, ie: serialization) - * - blockId (id of the block that contains the transaction) - * - timestamp (related to the genesis block) - * - senderPublicKey (public key of the sender) - * - recipientId (address of the recipient) - * - type - * - vendorFieldHex (hexadecimal version of the vendorField) - * - amount (in arktoshi) - * - fee (in arktoshi) - * - serialized - * - * Apart, the Model includes other fields: - * - signature - * - secondSignature - * - vendorField - * - * - assets - * - network - */ -module.exports = class Transaction { - constructor(data) { - if (typeof data === 'string') { - this.serialized = data - } else { - this.serialized = Transaction.serialize(data).toString('hex') - } - const deserialized = Transaction.deserialize(this.serialized) - - if (deserialized.version === 1) { - Transaction.applyV1Compatibility(deserialized) - this.verified = deserialized.verified - delete deserialized.verified - } else if (deserialized.version === 2) { - deserialized.id = createHash('sha256') - .update(Buffer.from(this.serialized, 'hex')) - .digest() - .toString('hex') - - // TODO: enable AIP11 when network ready - this.verified = false - } - ;[ - 'id', - 'sequence', - 'version', - 'timestamp', - 'senderPublicKey', - 'recipientId', - 'type', - 'vendorField', - 'vendorFieldHex', - 'amount', - 'fee', - 'blockId', - 'signature', - 'signatures', - 'secondSignature', - 'signSignature', - 'asset', - 'expiration', - 'timelock', - 'timelockType', - ].forEach(key => { - this[key] = deserialized[key] - }, this) - - this.data = deserialized - } - - static applyV1Compatibility(deserialized) { - if (deserialized.secondSignature) { - deserialized.signSignature = deserialized.secondSignature - } - - if (deserialized.type === TRANSACTION_TYPES.VOTE) { - deserialized.recipientId = crypto.getAddress( - deserialized.senderPublicKey, - deserialized.network, - ) - } - - if (deserialized.vendorFieldHex) { - deserialized.vendorField = Buffer.from( - deserialized.vendorFieldHex, - 'hex', - ).toString('utf8') - } - - if (deserialized.type === TRANSACTION_TYPES.MULTI_SIGNATURE) { - deserialized.asset.multisignature.keysgroup = deserialized.asset.multisignature.keysgroup.map( - k => `+${k}`, - ) - } - - if ( - deserialized.type === TRANSACTION_TYPES.SECOND_SIGNATURE || - deserialized.type === TRANSACTION_TYPES.MULTI_SIGNATURE - ) { - deserialized.recipientId = crypto.getAddress( - deserialized.senderPublicKey, - deserialized.network, - ) - } - - if (!deserialized.id) { - deserialized.id = crypto.getId(deserialized) - - // Apply fix for broken type 1 and 4 transactions, which were - // erroneously calculated with a recipient id. - if (transactionIdFixTable[deserialized.id]) { - deserialized.id = transactionIdFixTable[deserialized.id] - } - } - - if (deserialized.type <= 4) { - deserialized.verified = crypto.verify(deserialized) - } else { - deserialized.verified = false - } - } - - /* - * Return a clean transaction data from the serialized form. - * @return {Transaction} - */ - static fromBytes(hexString) { - return new Transaction(hexString) - } - - verify() { - return this.verified - } - - /* - * Return transaction data. - * @return {Object} - */ - toJson() { - // Convert Bignums - return cloneDeepWith(this.data, (value, key) => { - if (['amount', 'fee'].indexOf(key) !== -1) { - return +value.toFixed() - } - }) - } - - // AIP11 serialization - static serialize(transaction) { - const bb = new ByteBuffer(512, true) - bb.writeByte(0xff) // fill, to disambiguate from v1 - bb.writeByte(transaction.version || 0x01) // version - bb.writeByte(transaction.network || configManager.get('pubKeyHash')) // ark = 0x17, devnet = 0x30 - bb.writeByte(transaction.type) - bb.writeUInt32(transaction.timestamp) - bb.append(transaction.senderPublicKey, 'hex') - bb.writeUInt64(+new Bignum(transaction.fee).toFixed()) - - if (transaction.vendorField) { - const vf = Buffer.from(transaction.vendorField, 'utf8') - bb.writeByte(vf.length) - bb.append(vf) - } else if (transaction.vendorFieldHex) { - bb.writeByte(transaction.vendorFieldHex.length / 2) - bb.append(transaction.vendorFieldHex, 'hex') - } else { - bb.writeByte(0x00) - } - - if (transaction.type === TRANSACTION_TYPES.TRANSFER) { - bb.writeUInt64(+new Bignum(transaction.amount).toFixed()) - bb.writeUInt32(transaction.expiration || 0) - bb.append(bs58check.decode(transaction.recipientId)) - } else if (transaction.type === TRANSACTION_TYPES.VOTE) { - const voteBytes = transaction.asset.votes - .map(vote => (vote[0] === '+' ? '01' : '00') + vote.slice(1)) - .join('') - bb.writeByte(transaction.asset.votes.length) - bb.append(voteBytes, 'hex') - } else if (transaction.type === TRANSACTION_TYPES.SECOND_SIGNATURE) { - bb.append(transaction.asset.signature.publicKey, 'hex') - } else if (transaction.type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - const delegateBytes = Buffer.from( - transaction.asset.delegate.username, - 'utf8', - ) - bb.writeByte(delegateBytes.length) - bb.append(delegateBytes, 'hex') - } else if (transaction.type === TRANSACTION_TYPES.MULTI_SIGNATURE) { - let joined = null - - if (!transaction.version || transaction.version === 1) { - joined = transaction.asset.multisignature.keysgroup - .map(k => (k[0] === '+' ? k.slice(1) : k)) - .join('') // eslint-disable-line max-len - } else { - joined = transaction.asset.multisignature.keysgroup.join('') - } - - const keysgroupBuffer = Buffer.from(joined, 'hex') - bb.writeByte(transaction.asset.multisignature.min) - bb.writeByte(transaction.asset.multisignature.keysgroup.length) - bb.writeByte(transaction.asset.multisignature.lifetime) - bb.append(keysgroupBuffer, 'hex') - } else if (transaction.type === TRANSACTION_TYPES.IPFS) { - bb.writeByte(transaction.asset.ipfs.dag.length / 2) - bb.append(transaction.asset.ipfs.dag, 'hex') - } else if (transaction.type === TRANSACTION_TYPES.TIMELOCK_TRANSFER) { - bb.writeUInt64(+transaction.amount.toFixed()) - bb.writeByte(transaction.timelockType) - bb.writeUInt32(transaction.timelock) - bb.append(bs58check.decode(transaction.recipientId)) - } else if (transaction.type === TRANSACTION_TYPES.MULTI_PAYMENT) { - bb.writeUInt32(transaction.asset.payments.length) - transaction.asset.payments.forEach(p => { - bb.writeUInt64(p.amount) - bb.append(bs58check.decode(p.recipientId)) - }) - } else if (transaction.type === TRANSACTION_TYPES.DELEGATE_RESIGNATION) { - // delegate resignation - empty payload - } - - if (transaction.signature) { - bb.append(transaction.signature, 'hex') - } - - if (transaction.secondSignature) { - bb.append(transaction.secondSignature, 'hex') - } else if (transaction.signSignature) { - bb.append(transaction.signSignature, 'hex') - } - - if (transaction.signatures) { - bb.append('ff', 'hex') // 0xff separator to signal start of multi-signature transactions - bb.append(transaction.signatures.join(''), 'hex') - } - - bb.flip() - - return bb.toBuffer() - } - - static deserialize(hexString) { - const transaction = {} - const buf = ByteBuffer.fromHex(hexString, true) - transaction.version = buf.readInt8(1) - transaction.network = buf.readInt8(2) - transaction.type = buf.readInt8(3) - transaction.timestamp = buf.readUInt32(4) - transaction.senderPublicKey = hexString.substring(16, 16 + 33 * 2) - transaction.fee = new Bignum(buf.readUInt64(41)) - - const vflength = buf.readInt8(41 + 8) - if (vflength > 0) { - transaction.vendorFieldHex = hexString.substring( - (41 + 8 + 1) * 2, - (41 + 8 + 1) * 2 + vflength * 2, - ) - } - - const assetOffset = (41 + 8 + 1) * 2 + vflength * 2 - - if (transaction.type === TRANSACTION_TYPES.TRANSFER) { - transaction.amount = new Bignum(buf.readUInt64(assetOffset / 2)) - transaction.expiration = buf.readUInt32(assetOffset / 2 + 8) - transaction.recipientId = bs58check.encode( - buf.buffer.slice(assetOffset / 2 + 12, assetOffset / 2 + 12 + 21), - ) - - Transaction.parseSignatures( - hexString, - transaction, - assetOffset + (21 + 12) * 2, - ) - } - - if (transaction.type === TRANSACTION_TYPES.VOTE) { - const votelength = buf.readInt8(assetOffset / 2) & 0xff - transaction.asset = { votes: [] } - - let vote - for (let i = 0; i < votelength; i++) { - vote = hexString.substring( - assetOffset + 2 + i * 2 * 34, - assetOffset + 2 + (i + 1) * 2 * 34, - ) - vote = (vote[1] === '1' ? '+' : '-') + vote.slice(2) - transaction.asset.votes.push(vote) - } - - Transaction.parseSignatures( - hexString, - transaction, - assetOffset + 2 + votelength * 34 * 2, - ) - } - - if (transaction.type === TRANSACTION_TYPES.SECOND_SIGNATURE) { - transaction.asset = { - signature: { - publicKey: hexString.substring(assetOffset, assetOffset + 66), - }, - } - - Transaction.parseSignatures(hexString, transaction, assetOffset + 66) - } - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - const usernamelength = buf.readInt8(assetOffset / 2) & 0xff - - transaction.asset = { - delegate: { - username: buf - .slice(assetOffset / 2 + 1, assetOffset / 2 + 1 + usernamelength) - .toString('utf8'), - }, - } - - Transaction.parseSignatures( - hexString, - transaction, - assetOffset + (usernamelength + 1) * 2, - ) - } - - if (transaction.type === TRANSACTION_TYPES.MULTI_SIGNATURE) { - transaction.asset = { multisignature: { keysgroup: [] } } - transaction.asset.multisignature.min = - buf.readInt8(assetOffset / 2) & 0xff - - const num = buf.readInt8(assetOffset / 2 + 1) & 0xff - transaction.asset.multisignature.lifetime = - buf.readInt8(assetOffset / 2 + 2) & 0xff - - for (let index = 0; index < num; index++) { - const key = hexString.slice( - assetOffset + 6 + index * 66, - assetOffset + 6 + (index + 1) * 66, - ) - transaction.asset.multisignature.keysgroup.push(key) - } - Transaction.parseSignatures( - hexString, - transaction, - assetOffset + 6 + num * 66, - ) - } - - if (transaction.type === TRANSACTION_TYPES.IPFS) { - transaction.asset = {} - - const l = buf.readInt8(assetOffset / 2) & 0xff - transaction.asset.dag = hexString.substring( - assetOffset + 2, - assetOffset + 2 + l * 2, - ) - Transaction.parseSignatures( - hexString, - transaction, - assetOffset + 2 + l * 2, - ) - } - - if (transaction.type === TRANSACTION_TYPES.TIMELOCK_TRANSFER) { - transaction.amount = new Bignum(buf.readUInt64(assetOffset / 2)) - transaction.timelockType = buf.readInt8(assetOffset / 2 + 8) & 0xff - transaction.timelock = buf.readUInt64(assetOffset / 2 + 9).toNumber() - transaction.recipientId = bs58check.encode( - buf.buffer.slice(assetOffset / 2 + 13, assetOffset / 2 + 13 + 21), - ) - - Transaction.parseSignatures( - hexString, - transaction, - assetOffset + (21 + 13) * 2, - ) - } - - if (transaction.type === TRANSACTION_TYPES.MULTI_PAYMENT) { - transaction.asset = { payments: [] } - - const total = buf.readInt8(assetOffset / 2) & 0xff - let offset = assetOffset / 2 + 1 - - for (let j = 0; j < total; j++) { - const payment = {} - payment.amount = new Bignum(buf.readUInt64(offset)) - payment.recipientId = bs58check.encode( - buf.buffer.slice(offset + 1, offset + 1 + 21), - ) - transaction.asset.payments.push(payment) - offset += 22 - } - - transaction.amount = transaction.asset.payments.reduce( - (a, p) => a.plus(p.amount), - Bignum.ZERO, - ) - - Transaction.parseSignatures(hexString, transaction, offset * 2) - } - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_RESIGNATION) { - Transaction.parseSignatures(hexString, transaction, assetOffset) - } - - if (!transaction.amount) { - // this is needed for computation over the blockchain - transaction.amount = Bignum.ZERO - } - - return transaction - } - - static parseSignatures(hexString, transaction, startOffset) { - transaction.signature = hexString.substring(startOffset) - - let multioffset = 0 - - if (transaction.signature.length === 0) { - delete transaction.signature - } else { - const length1 = - parseInt(`0x${transaction.signature.substring(2, 4)}`, 16) + 2 - transaction.signature = hexString.substring( - startOffset, - startOffset + length1 * 2, - ) - multioffset += length1 * 2 - transaction.secondSignature = hexString.substring( - startOffset + length1 * 2, - ) - - if (transaction.secondSignature.length === 0) { - delete transaction.secondSignature - } else if (transaction.secondSignature.slice(0, 2) === 'ff') { - // start of multisign - delete transaction.secondSignature - } else { - const length2 = - parseInt(`0x${transaction.secondSignature.substring(2, 4)}`, 16) + 2 - transaction.secondSignature = transaction.secondSignature.substring( - 0, - length2 * 2, - ) - multioffset += length2 * 2 - } - - let signatures = hexString.substring(startOffset + multioffset) - if (!signatures.length) { - return - } - - if (signatures.slice(0, 2) !== 'ff') { - return - } - - signatures = signatures.slice(2) - transaction.signatures = [] - - let moreSignatures = true - while (moreSignatures) { - const mlength = parseInt(`0x${signatures.substring(2, 4)}`, 16) + 2 - - if (mlength > 0) { - transaction.signatures.push(signatures.substring(0, mlength * 2)) - } else { - moreSignatures = false - } - - signatures = signatures.substring(mlength * 2) - } - } - } -} diff --git a/packages/crypto/lib/models/wallet.js b/packages/crypto/lib/models/wallet.js deleted file mode 100644 index fa29fbcb4c..0000000000 --- a/packages/crypto/lib/models/wallet.js +++ /dev/null @@ -1,339 +0,0 @@ -const configManager = require('../managers/config') -const { TRANSACTION_TYPES } = require('../constants') -const { Bignum, formatArktoshi } = require('../utils') -const crypto = require('../crypto/crypto') -const transactionHandler = require('../handlers/transactions') - -/** - * TODO copy some parts to ArkDocs - * @classdesc This class holds the wallet data, verifies it and applies the - * transaction and blocks to it - * - * Wallet attributes that are stored on the db: - * - address - * - publicKey - * - secondPublicKey - * - balance - * - vote - * - username (name, if the wallet is a delegate) - * - voteBalance (number of votes if the wallet is a delegate) - * - producedBlocks - * - missedBlocks - * - * This other attributes are not stored on the db: - * - multisignature - * - lastBlock (last block applied or `null``) - * - dirty - */ -module.exports = class Wallet { - /** - * @constructor - * @param {String} address - */ - constructor(address) { - this.address = address - this.publicKey = null - this.secondPublicKey = null - this.balance = Bignum.ZERO - this.vote = null - this.voted = false - this.username = null - this.lastBlock = null - this.voteBalance = Bignum.ZERO - this.multisignature = null - this.dirty = true - this.producedBlocks = 0 - this.missedBlocks = 0 - this.forgedFees = Bignum.ZERO - this.forgedRewards = Bignum.ZERO - } - - /** - * Check if can apply a transaction to the wallet. - * @param {Transaction} transaction - * @param {Array} errors - * @return {Boolean} - */ - canApply(transaction, errors) { - return transactionHandler.canApply(this, transaction, errors) - } - - /** - * Apply the specified transaction to this wallet. - * @param {Transaction} transaction - * @return {Boolean} - */ - apply(transaction) { - return transactionHandler.apply(this, transaction) - } - - /** - * Revert the specified transaction from this wallet. - * @param {Transaction} transaction - * @return {Boolean} - */ - revert(transaction) { - return transactionHandler.revert(this, transaction) - } - - /** - * Associate this wallet as the sender of a transaction. - * @param {Transaction} transaction - */ - applyTransactionToSender(transaction) { - return transactionHandler.applyTransactionToSender(this, transaction) - } - - /** - * Remove this wallet as the sender of a transaction. - * @param {Transaction} transaction - */ - revertTransactionForSender(transaction) { - return transactionHandler.revertTransactionForSender(this, transaction) - } - - /** - * Add transaction balance to this wallet. - * @param {Transaction} transaction - */ - applyTransactionToRecipient(transaction) { - return transactionHandler.applyTransactionToRecipient(this, transaction) - } - - /** - * Remove transaction balance from this wallet. - * @param {Transaction} transaction - */ - revertTransactionForRecipient(transaction) { - return transactionHandler.revertTransactionForRecipient(this, transaction) - } - - /** - * Add block data to this wallet. - * @param {Block} block - * @returns {Boolean} - */ - applyBlock(block) { - this.dirty = true - - if ( - block.generatorPublicKey === this.publicKey || - crypto.getAddress(block.generatorPublicKey) === this.address - ) { - this.balance = this.balance.plus(block.reward).plus(block.totalFee) - - // update stats - this.producedBlocks++ - this.forgedFees = this.forgedFees.plus(block.totalFee) - this.forgedRewards = this.forgedRewards.plus(block.reward) - this.lastBlock = block - return true - } - - return false - } - - /** - * Remove block data from this wallet. - * @param {Block} block - */ - revertBlock(block) { - this.dirty = true - - if ( - block.generatorPublicKey === this.publicKey || - crypto.getAddress(block.generatorPublicKey) === this.address - ) { - this.balance = this.balance.minus(block.reward).minus(block.totalFee) - - // update stats - this.forgedFees = this.forgedFees.minus(block.totalFee) - this.forgedRewards = this.forgedRewards.minus(block.reward) - this.lastBlock = block - this.producedBlocks-- - - // TODO: get it back from database? - this.lastBlock = null - return true - } - - return false - } - - /** - * Verify the wallet. - * @param {Transaction} transaction - * @param {String} signature - * @param {String} publicKey - * @return {Boolean} - */ - verify(transaction, signature, publicKey) { - const hash = crypto.getHash(transaction, true, true) - return crypto.verifyHash(hash, signature, publicKey) - } - - /** - * Verify multi-signatures for the wallet. - * @param {Transaction} transaction - * @param {MultiSignature} multisignature - * @return {Boolean} - */ - verifySignatures(transaction, multisignature) { - if ( - !transaction.signatures || - transaction.signatures.length < multisignature.min - ) { - return false - } - - const keysgroup = multisignature.keysgroup.map(publicKey => - publicKey.startsWith('+') ? publicKey.slice(1) : publicKey, - ) - const signatures = Object.values(transaction.signatures) - - let valid = 0 - for (const publicKey of keysgroup) { - const signature = this.__verifyTransactionSignatures( - transaction, - signatures, - publicKey, - ) - if (signature) { - signatures.splice(signatures.indexOf(signature), 1) - valid++ - if (valid === multisignature.min) { - return true - } - } - } - - return false - } - - /** - * Audit the specified transaction. - * @param {Transaction} transaction - * @return {[type]} - */ - auditApply(transaction) { - const audit = [] - - if (this.multisignature) { - audit.push({ - Mutisignature: this.verifySignatures(transaction, this.multisignature), - }) - } else { - audit.push({ - 'Remaining amount': +this.balance - .minus(transaction.amount) - .minus(transaction.fee) - .toFixed(), - }) - audit.push({ 'Signature validation': crypto.verify(transaction) }) - // TODO: this can blow up if 2nd phrase and other transactions are in the wrong order - if (this.secondPublicKey) { - audit.push({ - 'Second Signature Verification': crypto.verifySecondSignature( - transaction, - this.secondPublicKey, - configManager.config, - ), // eslint-disable-line max-len - }) - } - } - - if (transaction.type === TRANSACTION_TYPES.TRANSFER) { - audit.push({ Transfer: true }) - } - - if (transaction.type === TRANSACTION_TYPES.SECOND_SIGNATURE) { - audit.push({ 'Second public key': this.secondPublicKey }) - } - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_REGISTRATION) { - const username = transaction.asset.delegate.username - audit.push({ 'Current username': this.username }) - audit.push({ 'New username': username }) - } - - if (transaction.type === TRANSACTION_TYPES.VOTE) { - audit.push({ 'Current vote': this.vote }) - audit.push({ 'New vote': transaction.asset.votes[0] }) - } - - if (transaction.type === TRANSACTION_TYPES.MULTI_SIGNATURE) { - const keysgroup = transaction.asset.multisignature.keysgroup - audit.push({ 'Multisignature not yet registered': !this.multisignature }) - audit.push({ - 'Multisignature enough keys': - keysgroup.length >= transaction.asset.multisignature.min, - }) - audit.push({ - 'Multisignature all keys signed': - keysgroup.length === transaction.signatures.length, - }) - audit.push({ - 'Multisignature verification': this.verifySignatures( - transaction, - transaction.asset.multisignature, - ), - }) - } - - if (transaction.type === TRANSACTION_TYPES.IPFS) { - audit.push({ IPFS: true }) - } - - if (transaction.type === TRANSACTION_TYPES.TIMELOCK_TRANSFER) { - audit.push({ Timelock: true }) - } - - if (transaction.type === TRANSACTION_TYPES.MULTI_PAYMENT) { - const amount = transaction.asset.payments.reduce( - (a, p) => a.plus(p.amount), - Bignum.ZERO, - ) - audit.push({ 'Multipayment remaining amount': amount }) - } - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_RESIGNATION) { - audit.push({ 'Resignate Delegate': this.username }) - } - - if (transaction.type === TRANSACTION_TYPES.DELEGATE_RESIGNATION) { - audit.push({ 'Resignate Delegate': this.username }) - } - - if (!Object.values(TRANSACTION_TYPES).includes(transaction.type)) { - audit.push({ 'Unknown Type': true }) - } - - return audit - } - - /** - * Get formatted wallet address and balance as string. - * @return {String} - */ - toString() { - return `${this.address} (${formatArktoshi(this.balance)})` - } - - /** - * Goes through signatures to check if public key matches. Can also remove valid signatures. - * @param {Transaction} transaction - * @param {String[]} signatures - * @param {String} publicKey - * @return {Boolean} - */ - __verifyTransactionSignatures(transaction, signatures, publicKey) { - for (let i = 0; i < signatures.length; i++) { - const signature = signatures[i] - if (this.verify(transaction, signature, publicKey)) { - return signature - } - } - - return false - } -} diff --git a/packages/crypto/lib/networks/ark/bitcoin.json b/packages/crypto/lib/networks/ark/bitcoin.json deleted file mode 100644 index fb66a4c105..0000000000 --- a/packages/crypto/lib/networks/ark/bitcoin.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "bitcoin", - "messagePrefix": "\u0018Bitcoin Signed Message:\n", - "bip32": { - "public": 76067358, - "private": 76066276 - }, - "pubKeyHash": 0, - "wif": 128 -} diff --git a/packages/crypto/lib/networks/ark/devnet.json b/packages/crypto/lib/networks/ark/devnet.json deleted file mode 100644 index 1137b43cbf..0000000000 --- a/packages/crypto/lib/networks/ark/devnet.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "name": "devnet", - "messagePrefix": "DARK message:\n", - "bip32": { - "public": 46090600, - "private": 46089520 - }, - "pubKeyHash": 30, - "nethash": "2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867", - "wif": 170, - "aip20": 1, - "client": { - "token": "DARK", - "symbol": "DѦ", - "explorer": "https://dexplorer.ark.io" - }, - "constants": [ - { - "height": 1, - "reward": 0, - "activeDelegates": 51, - "blocktime": 8, - "block": { - "version": 0, - "maxTransactions": 50, - "maxPayload": 2097152 - }, - "epoch": "2017-03-21T13:00:00.000Z", - "fees": { - "dynamic": true, - "dynamicFees": { - "minFeePool": 1000, - "minFeeBroadcast": 1000, - "addonBytes": { - "transfer": 100, - "secondSignature": 250, - "delegateRegistration": 400000, - "vote": 100, - "multiSignature": 500, - "ipfs": 250, - "timelockTransfer": 500, - "multiPayment": 500, - "delegateResignation": 400000 - } - }, - "staticFees": { - "transfer": 10000000, - "secondSignature": 500000000, - "delegateRegistration": 2500000000, - "vote": 100000000, - "multiSignature": 500000000, - "ipfs": 0, - "timelockTransfer": 0, - "multiPayment": 0, - "delegateResignation": 0 - } - } - }, - { - "height": 10800, - "reward": 200000000 - }, - { - "height": 21600, - "block": { - "maxTransactions": 150, - "maxPayload": 6300000 - } - }, - { - "height": 910000, - "block": { - "maxTransactions": 500, - "maxPayload": 21000000 - } - } - ], - "exceptions": { - "blocks": ["15895730198424359628", "14746174532446639362"], - "transactions": [ - "76bd168e57a4431a64617c4e7864df1e0be89831eabaa230e37643efae2def6f", - "90d06cb306dcc33faba59545e03d91ee83b0409e66a45ffe6a9e3b1049a0c521", - "0f7a3e8036fbaae7c76f7615b09b8c4f1337e96d87042d86e03cc5ab9b6ed745", - "23733214f347970a34ccd772f89396056309744688bd6dbc35129e1f12d46d2f", - "e30d7290ca9cab570aa72bf0365dde39b8d75fe65a4e804844e5708a021f8ab4", - "03d3902bb30f71c29151f8b85ff478be1dc5264785c8c84550b6b59339dc03c9", - "7a00dc347a50186bc0d789a7899f1a4dcbc1e5d5adbb5359df238cd2b9363b41", - "bbe266eac2bbc505b40e74ae6d1960d7c76d3ca8d4b942b6046f0c5f750ff9f4", - "cb63ee14068a8d2987c90ecb12998653161cd8748af7790c75592647260d3266", - "d184752c1546c366866013aa00a2a0f9b40463656072334fc302ff783ff4ee98", - "f8122e3d8b7ad31c58ed3254196b16c23249b8372f06de42191c43bfcf39849d" - ] - } -} diff --git a/packages/crypto/lib/networks/ark/index.js b/packages/crypto/lib/networks/ark/index.js deleted file mode 100644 index 542122e472..0000000000 --- a/packages/crypto/lib/networks/ark/index.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - bitcoin: require('./bitcoin.json'), - devnet: require('./devnet.json'), - mainnet: require('./mainnet.json'), - testnet: require('./testnet.json'), -} diff --git a/packages/crypto/lib/networks/ark/mainnet.json b/packages/crypto/lib/networks/ark/mainnet.json deleted file mode 100644 index b3381f95a5..0000000000 --- a/packages/crypto/lib/networks/ark/mainnet.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "name": "mainnet", - "messagePrefix": "ARK message:\n", - "bip32": { - "public": 46090600, - "private": 46089520 - }, - "pubKeyHash": 23, - "nethash": "6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988", - "wif": 170, - "aip20": 0, - "client": { - "token": "ARK", - "symbol": "Ѧ", - "explorer": "https://explorer.ark.io" - }, - "constants": [ - { - "height": 1, - "reward": 0, - "activeDelegates": 51, - "blocktime": 8, - "block": { - "version": 0, - "maxTransactions": 50, - "maxPayload": 2097152 - }, - "epoch": "2017-03-21T13:00:00.000Z", - "fees": { - "dynamic": true, - "dynamicFees": { - "minFeePool": 3000, - "minFeeBroadcast": 3000, - "addonBytes": { - "transfer": 100, - "secondSignature": 250, - "delegateRegistration": 400000, - "vote": 100, - "multiSignature": 500, - "ipfs": 250, - "timelockTransfer": 500, - "multiPayment": 500, - "delegateResignation": 400000 - } - }, - "staticFees": { - "transfer": 10000000, - "secondSignature": 500000000, - "delegateRegistration": 2500000000, - "vote": 100000000, - "multiSignature": 500000000, - "ipfs": 0, - "timelockTransfer": 0, - "multiPayment": 0, - "delegateResignation": 0 - } - } - }, - { - "height": 75600, - "reward": 200000000 - }, - { - "height": 6600000, - "block": { - "maxTransactions": 150, - "maxPayload": 6300000 - } - } - ], - "exceptions": { - "transactions": [ - "608c7aeba0895da4517496590896eb325a0b5d367e1b186b1c07d7651a568b9e" - ] - }, - "outlookTable": { - "5139199631254983076": "1000099631254983076", - "4683900276587456793": "1000000276587456793", - "4719273207090574361": "1000073207090574361", - "10008425497949974873": "10000425497949974873", - "3011426208694781338": "1000026208694781338", - "122506651077645039": "100006651077645039", - "5720847785115142568": "1000047785115142568", - "7018402152859193732": "1000002152859193732", - "12530635932931954947": "10000635932931954947", - "7061061305098280027": "1000061305098280027", - "3983271186026110297": "1000071186026110297", - "3546732630357730082": "1000032630357730082", - "14024378732446299587": "10000378732446299587", - "5160516564770509401": "1000016564770509401", - "241883250703033792": "100003250703033792", - "18238049267092652511": "10000049267092652511", - "3824223895435898486": "1000023895435898486", - "4888561739037785996": "1000061739037785996", - "1256478353465481084": "1000078353465481084", - "12598210368652133913": "10000210368652133913", - "17559226088420912749": "10000226088420912749", - "13894975866600060289": "10000975866600060289", - "11710672157782824154": "10000672157782824154", - "5509880884401609373": "1000080884401609373", - "11486353335769396593": "10000353335769396593", - "10147280738049458646": "10000280738049458646", - "5684621525438367021": "1000021525438367021", - "719490120693255848": "100000120693255848", - "7154018532147250826": "1000018532147250826", - "38016207884795383": "10000207884795383", - "8324387831264270399": "1000087831264270399", - "10123661368384267251": "10000661368384267251", - "2222163236406460530": "1000063236406460530", - "5059382813585250340": "1000082813585250340", - "7091362542116598855": "1000062542116598855", - "8225244493039935740": "1000044493039935740" - }, - "transactionIdFixTable": { - "ca764c01dd78f93393b02f7f6c4f0c12ed8e7ca26d3098e91d6e461a238a6b33": "80d75c7b90288246199e4a97ba726bad6639595ef92ad7c2bd14fd31563241ab" - } -} diff --git a/packages/crypto/lib/networks/ark/testnet.json b/packages/crypto/lib/networks/ark/testnet.json deleted file mode 100644 index f342a0d9ec..0000000000 --- a/packages/crypto/lib/networks/ark/testnet.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "testnet", - "messagePrefix": "TEST message:\n", - "bip32": { - "public": 70617039, - "private": 70615956 - }, - "pubKeyHash": 23, - "nethash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", - "wif": 186, - "aip20": 0, - "client": { - "token": "TARK", - "symbol": "TѦ", - "explorer": "http://texplorer.ark.io" - }, - "constants": [ - { - "height": 1, - "reward": 0, - "activeDelegates": 51, - "blocktime": 8, - "block": { - "version": 0, - "maxTransactions": 150, - "maxPayload": 2097152 - }, - "epoch": "2017-03-21T13:00:00.000Z", - "fees": { - "dynamic": true, - "dynamicFees": { - "minFeePool": 1000, - "minFeeBroadcast": 1000, - "addonBytes": { - "transfer": 100, - "secondSignature": 250, - "delegateRegistration": 400000, - "vote": 100, - "multiSignature": 500, - "ipfs": 250, - "timelockTransfer": 500, - "multiPayment": 500, - "delegateResignation": 400000 - } - }, - "staticFees": { - "transfer": 10000000, - "secondSignature": 500000000, - "delegateRegistration": 2500000000, - "vote": 100000000, - "multiSignature": 500000000, - "ipfs": 0, - "timelockTransfer": 0, - "multiPayment": 0, - "delegateResignation": 0 - } - } - }, - { - "height": 10, - "fees": { - "dynamic": true - } - }, - { - "height": 75600, - "reward": 200000000 - } - ], - "exceptions": {} -} diff --git a/packages/crypto/lib/networks/index.js b/packages/crypto/lib/networks/index.js deleted file mode 100644 index d0645a65e8..0000000000 --- a/packages/crypto/lib/networks/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ark: require('./ark'), -} diff --git a/packages/crypto/lib/utils/bignum.js b/packages/crypto/lib/utils/bignum.js deleted file mode 100644 index 5032dac9d6..0000000000 --- a/packages/crypto/lib/utils/bignum.js +++ /dev/null @@ -1,8 +0,0 @@ -const BigNumber = require('bignumber.js') - -BigNumber.config({ DECIMAL_PLACES: 0 }) - -BigNumber.ZERO = new BigNumber(0) -BigNumber.ONE = new BigNumber(1) - -module.exports = BigNumber diff --git a/packages/crypto/lib/utils/format-arktoshi.js b/packages/crypto/lib/utils/format-arktoshi.js deleted file mode 100644 index 96eb5d4162..0000000000 --- a/packages/crypto/lib/utils/format-arktoshi.js +++ /dev/null @@ -1,16 +0,0 @@ -const { ARKTOSHI } = require('../constants') -const configManager = require('../managers/config') - -/** - * Get human readable string from arktoshis - * @param {Number|String|Bignum} amount - * @return {String} - */ -module.exports = amount => { - const localeString = (+amount / ARKTOSHI).toLocaleString('en', { - minimumFractionDigits: 0, - maximumFractionDigits: 8, - }) - - return `${localeString} ${configManager.config.client.symbol}` -} diff --git a/packages/crypto/lib/utils/index.js b/packages/crypto/lib/utils/index.js deleted file mode 100644 index 2c5be9bd0e..0000000000 --- a/packages/crypto/lib/utils/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - Bignum: require('./bignum'), - formatArktoshi: require('./format-arktoshi'), - sortTransactions: require('./sort-transactions'), -} diff --git a/packages/crypto/lib/utils/sort-transactions.js b/packages/crypto/lib/utils/sort-transactions.js deleted file mode 100644 index e06d7f6c66..0000000000 --- a/packages/crypto/lib/utils/sort-transactions.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Sort transactions by type, then id. - * @param {Transaction[]} transactions - * @return {Transaction[]} - */ -module.exports = transactions => transactions.sort((a, b) => { - if (a.type < b.type) { - return -1 - } - - if (a.type > b.type) { - return 1 - } - - if (a.id < b.id) { - return -1 - } - - if (a.id > b.id) { - return 1 - } - - return 0 -}) diff --git a/packages/crypto/lib/validation/engine.js b/packages/crypto/lib/validation/engine.js deleted file mode 100644 index a24f7f584e..0000000000 --- a/packages/crypto/lib/validation/engine.js +++ /dev/null @@ -1,27 +0,0 @@ -const Joi = require('joi') -const extensions = require('./extensions') - -class Engine { - constructor() { - this.joi = Joi.extend(extensions) - } - - validate(attributes, rules, options) { - try { - return this.joi.validate( - attributes, - rules, - Object.assign( - { - convert: true, - }, - options, - ), - ) - } catch (error) { - return { value: null, error: error.stack } - } - } -} - -module.exports = new Engine() diff --git a/packages/crypto/lib/validation/extensions/address.js b/packages/crypto/lib/validation/extensions/address.js deleted file mode 100644 index feead0cdeb..0000000000 --- a/packages/crypto/lib/validation/extensions/address.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = joi => ({ - name: 'arkAddress', - base: joi - .string() - .alphanum() - .length(34), -}) diff --git a/packages/crypto/lib/validation/extensions/bignumber.js b/packages/crypto/lib/validation/extensions/bignumber.js deleted file mode 100644 index 85197ba5e1..0000000000 --- a/packages/crypto/lib/validation/extensions/bignumber.js +++ /dev/null @@ -1,76 +0,0 @@ -const Bignum = require('../../utils/bignum') - -module.exports = joi => ({ - name: 'bignumber', - base: joi.object().type(Bignum), - language: { - min: 'is less than minimum', - max: 'is greater than maximum', - only: 'is different from allowed value', - integer: 'is not an integer', - positive: 'is not positive', - }, - rules: [ - { - name: 'min', - params: { - q: joi.number().required(), - }, - validate(params, value, state, options) { - if (value.isLessThan(params.q)) { - return this.createError('bignumber.min', { v: value }, state, options); - } - - return value; - }, - }, - { - name: 'max', - params: { - q: joi.number().required(), - }, - validate(params, value, state, options) { - if (value.isGreaterThan(params.q)) { - return this.createError('bignumber.max', { v: value }, state, options); - } - - return value; - }, - }, - { - name: 'only', - params: { - q: joi.number().required(), - }, - validate(params, value, state, options) { - if (!value.isEqualTo(params.q)) { - return this.createError('bignumber.only', { v: value }, state, options); - } - - return value; - }, - }, - { - name: 'integer', - params: {}, - validate(_, value, state, options) { - if (!value.isInteger()) { - return this.createError('bignumber.integer', { v: value }, state, options); - } - - return value; - }, - }, - { - name: 'positive', - params: {}, - validate(_, value, state, options) { - if (!value.isPositive() || value.isZero()) { - return this.createError('bignumber.positive', { v: value }, state, options); - } - - return value; - }, - }, - ], -}) diff --git a/packages/crypto/lib/validation/extensions/block-id.js b/packages/crypto/lib/validation/extensions/block-id.js deleted file mode 100644 index 87615f9972..0000000000 --- a/packages/crypto/lib/validation/extensions/block-id.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = joi => ({ - name: 'arkBlockId', - base: joi.string().regex(/^[0-9]+$/, 'numbers'), -}) diff --git a/packages/crypto/lib/validation/extensions/block.js b/packages/crypto/lib/validation/extensions/block.js deleted file mode 100644 index 57d0f0cbb6..0000000000 --- a/packages/crypto/lib/validation/extensions/block.js +++ /dev/null @@ -1,63 +0,0 @@ -module.exports = joi => ({ - name: 'arkBlock', - base: joi.object().keys({ - id: joi.arkBlockId().required(), - idHex: joi.string().hex(), - version: joi - .number() - .integer() - .min(0), - timestamp: joi - .number() - .integer() - .min(0) - .required(), - previousBlock: joi.arkBlockId().required(), - previousBlockHex: joi.string().hex(), - height: joi - .number() - .integer() - .positive() - .required(), - numberOfTransactions: joi - .number() - .integer() - .only(joi.ref('transactions.length')), - totalAmount: joi.alternatives().try( - joi - .number() - .integer() - .min(0) - .required(), - joi - .string() - .regex(/[0-9]+/) - .required(), - ), - totalFee: joi - .number() - .integer() - .min(0) - .required(), - reward: joi - .number() - .integer() - .min(0) - .required(), - payloadLength: joi - .number() - .integer() - .min(0), - payloadHash: joi.string().hex(), - generatorPublicKey: joi - .string() - .hex() - .length(66) - .required(), - blockSignature: joi - .string() - .hex() - .required(), - transactions: joi.arkTransactions(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/index.js b/packages/crypto/lib/validation/extensions/index.js deleted file mode 100644 index ca1dc52e85..0000000000 --- a/packages/crypto/lib/validation/extensions/index.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = [ - require('./address'), - require('./bignumber'), - require('./public-key'), - require('./username'), - - require('./block-id'), - ...require('./transactions/index'), // individual transactions - require('./transactions'), - require('./block'), -] diff --git a/packages/crypto/lib/validation/extensions/public-key.js b/packages/crypto/lib/validation/extensions/public-key.js deleted file mode 100644 index 5dd66e4957..0000000000 --- a/packages/crypto/lib/validation/extensions/public-key.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = joi => ({ - name: 'arkPublicKey', - base: joi - .string() - .hex() - .length(66), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions.js b/packages/crypto/lib/validation/extensions/transactions.js deleted file mode 100644 index afdc9cae09..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions.js +++ /dev/null @@ -1,16 +0,0 @@ -module.exports = joi => ({ - name: 'arkTransactions', - base: joi - .array() - .items( - joi - .alternatives() - .try( - joi.arkTransfer(), - joi.arkSecondSignature(), - joi.arkDelegateRegistration(), - joi.arkVote(), - joi.arkMultiSignature(), - ), - ), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/base.js b/packages/crypto/lib/validation/extensions/transactions/base.js deleted file mode 100644 index 79c3c6734b..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/base.js +++ /dev/null @@ -1,65 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') - -module.exports = joi => - joi.object().keys({ - id: joi - .string() - .alphanum() - .required(), - blockid: joi.alternatives().try( - // TODO: remove in 2.1 - joi.arkBlockId(), - joi.number().unsafe(), - ), - version: joi - .number() - .integer() - .min(1) - .max(2) - .optional(), - timestamp: joi - .number() - .integer() - .min(0) - .required(), - amount: joi - .alternatives() - .try( - joi - .bignumber() - .integer() - .positive(), - joi - .number() - .integer() - .positive(), - ) - .required(), - fee: joi - .alternatives() - .try( - joi - .bignumber() - .integer() - .positive(), - joi - .number() - .integer() - .positive(), - ) - .required(), - senderId: joi.arkAddress(), // TODO: remove in 2.1 - recipientId: joi.arkAddress().required(), - senderPublicKey: joi.arkPublicKey().required(), - signature: joi - .string() - .alphanum() - .required(), - signatures: joi.array(), - secondSignature: joi.string().alphanum(), - signSignature: joi.string().alphanum(), // TODO: remove in 2.1 - confirmations: joi // TODO: remove in 2.1 - .number() - .integer() - .min(0), - }) diff --git a/packages/crypto/lib/validation/extensions/transactions/delegate-registration.js b/packages/crypto/lib/validation/extensions/transactions/delegate-registration.js deleted file mode 100644 index 60b5b157d9..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/delegate-registration.js +++ /dev/null @@ -1,27 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkDelegateRegistration', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.DELEGATE_REGISTRATION) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().only(0)) - .optional(), - asset: joi - .object({ - delegate: joi - .object({ - username: joi.arkUsername().required(), - publicKey: joi.arkPublicKey(), - }) - .required(), - }) - .required(), - recipientId: joi.empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/delegate-resignation.js b/packages/crypto/lib/validation/extensions/transactions/delegate-resignation.js deleted file mode 100644 index 10424080ed..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/delegate-resignation.js +++ /dev/null @@ -1,18 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkDelegateResignation', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.DELEGATE_RESIGNATION) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().valid(0)) - .optional(), - asset: joi.object().required(), - recipientId: joi.empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/index.js b/packages/crypto/lib/validation/extensions/transactions/index.js deleted file mode 100644 index 6a40f5b152..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/index.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = [ - require('./transfer'), - require('./second-signature'), - require('./delegate-registration'), - require('./vote'), - require('./multi-signature'), - require('./ipfs'), - require('./timelock-transfer'), - require('./multi-payment'), - require('./delegate-resignation'), -] diff --git a/packages/crypto/lib/validation/extensions/transactions/ipfs.js b/packages/crypto/lib/validation/extensions/transactions/ipfs.js deleted file mode 100644 index 0532698e06..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/ipfs.js +++ /dev/null @@ -1,18 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkIpfs', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.IPFS) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().valid(0)) - .optional(), - asset: joi.object().required(), - recipientId: joi.empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/multi-payment.js b/packages/crypto/lib/validation/extensions/transactions/multi-payment.js deleted file mode 100644 index b409b6fcb9..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/multi-payment.js +++ /dev/null @@ -1,14 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkMultiPayment', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.MULTI_PAYMENT) - .required(), - asset: joi.object().required(), - recipientId: joi.empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/multi-signature.js b/packages/crypto/lib/validation/extensions/transactions/multi-signature.js deleted file mode 100644 index e20885c3c5..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/multi-signature.js +++ /dev/null @@ -1,61 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkMultiSignature', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.MULTI_SIGNATURE) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().only(0)) - .optional(), - recipientId: joi.empty(), - signatures: joi - .array() - .length(joi.ref('asset.multisignature.keysgroup.length')) - .required(), - asset: joi - .object({ - multisignature: joi - .object({ - min: joi - .when(joi.ref('keysgroup.length'), { - is: joi.number().greater(16), - then: joi - .number() - .positive() - .max(16), - otherwise: joi - .number() - .positive() - .max(joi.ref('keysgroup.length')), - }) - .required(), - keysgroup: joi - .array() - .unique() - .min(2) - .items( - joi - .string() - .not(`+${transaction.senderPublicKey}`) - .length(67) - .regex(/^\+/) - .required(), - ) - .required(), - lifetime: joi - .number() - .integer() - .min(1) - .max(72) - .required(), - }) - .required(), - }) - .required(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/second-signature.js b/packages/crypto/lib/validation/extensions/transactions/second-signature.js deleted file mode 100644 index 78d250d1f9..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/second-signature.js +++ /dev/null @@ -1,27 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkSecondSignature', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.SECOND_SIGNATURE) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().only(0)) - .optional(), - secondSignature: joi.string().only(''), - asset: joi - .object({ - signature: joi - .object({ - publicKey: joi.arkPublicKey().required(), - }) - .required(), - }) - .required(), - recipientId: joi.empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/timelock-transfer.js b/packages/crypto/lib/validation/extensions/transactions/timelock-transfer.js deleted file mode 100644 index 224f2d1c5a..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/timelock-transfer.js +++ /dev/null @@ -1,18 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkTimelockTransfer', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.MULTI_PAYMENT) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().only(0)) - .optional(), - asset: joi.object().required(), - recipientId: joi.empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/transfer.js b/packages/crypto/lib/validation/extensions/transactions/transfer.js deleted file mode 100644 index 4e4a35164a..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/transfer.js +++ /dev/null @@ -1,26 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkTransfer', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.TRANSFER) - .required(), - expiration: joi - .number() - .integer() - .min(0), - vendorField: joi - .string() - .max(64, 'utf8') - .allow('', null) - .optional(), // TODO: remove in 2.1 - vendorFieldHex: joi - .string() - .max(64, 'hex') - .optional(), - asset: joi.object().empty(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/transactions/vote.js b/packages/crypto/lib/validation/extensions/transactions/vote.js deleted file mode 100644 index 4c05b496e0..0000000000 --- a/packages/crypto/lib/validation/extensions/transactions/vote.js +++ /dev/null @@ -1,34 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../constants') -const transaction = require('./base') - -module.exports = joi => ({ - name: 'arkVote', - base: transaction(joi).append({ - type: joi - .number() - .only(TRANSACTION_TYPES.VOTE) - .required(), - amount: joi - .alternatives() - .try(joi.bignumber().only(0), joi.number().only(0)) - .optional(), - asset: joi - .object({ - votes: joi - .array() - .items( - joi - .string() - .length(67) - .regex(/^(\+|-)[a-zA-Z0-9]+$/), - ) - .length(1) - .required(), - }) - .required(), - recipientId: joi - .arkAddress() - .allow(null) - .optional(), - }), -}) diff --git a/packages/crypto/lib/validation/extensions/username.js b/packages/crypto/lib/validation/extensions/username.js deleted file mode 100644 index 89ead49097..0000000000 --- a/packages/crypto/lib/validation/extensions/username.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = joi => ({ - name: 'arkUsername', - base: joi - .string() - .regex(/^[a-z0-9!@$&_.]+$/) - .min(1) - .max(20), -}) diff --git a/packages/crypto/lib/validation/index.js b/packages/crypto/lib/validation/index.js deleted file mode 100644 index 4247badce0..0000000000 --- a/packages/crypto/lib/validation/index.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - validator: require('./validator'), - transactionValidator: require('./validators/transaction'), -} diff --git a/packages/crypto/lib/validation/rules/address.js b/packages/crypto/lib/validation/rules/address.js deleted file mode 100644 index b0fed68c3f..0000000000 --- a/packages/crypto/lib/validation/rules/address.js +++ /dev/null @@ -1,12 +0,0 @@ -const engine = require('../engine') - -module.exports = attributes => { - const { error, value } = engine.validate(attributes, engine.joi.arkAddress()) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/index.js b/packages/crypto/lib/validation/rules/index.js deleted file mode 100644 index 9a03355bb9..0000000000 --- a/packages/crypto/lib/validation/rules/index.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - address: require('./address'), - publicKey: require('./public-key'), - username: require('./username'), -} diff --git a/packages/crypto/lib/validation/rules/models/transactions.js b/packages/crypto/lib/validation/rules/models/transactions.js deleted file mode 100644 index 392e8d7742..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions.js +++ /dev/null @@ -1,4 +0,0 @@ -exports.transfer = require('./transactions/transfer') -exports.signature = require('./transactions/second-signature') -exports.delegate = require('./transactions/delegate-registration') -exports.vote = require('./transactions/vote') diff --git a/packages/crypto/lib/validation/rules/models/transactions/delegate-registration.js b/packages/crypto/lib/validation/rules/models/transactions/delegate-registration.js deleted file mode 100644 index 8ea8dd2058..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/delegate-registration.js +++ /dev/null @@ -1,71 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.DELEGATE_REGISTRATION), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .valid(0) - .required(), - ), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - senderId: engine.joi.arkAddress(), - recipientId: engine.joi.empty(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi - .object({ - delegate: engine.joi - .object({ - username: engine.joi.arkUsername().required(), - publicKey: engine.joi.arkPublicKey(), - }) - .required(), - }) - .required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/delegate-resignation.js b/packages/crypto/lib/validation/rules/models/transactions/delegate-resignation.js deleted file mode 100644 index e49762acb5..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/delegate-resignation.js +++ /dev/null @@ -1,62 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.DELEGATE_RESIGNATION), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .min(0) - .required(), - ), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - senderId: engine.joi.arkAddress(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi.object().required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/ipfs.js b/packages/crypto/lib/validation/rules/models/transactions/ipfs.js deleted file mode 100644 index a499721c33..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/ipfs.js +++ /dev/null @@ -1,62 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.IPFS), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .min(0) - .required(), - ), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .min(0) - .required(), - ), - senderId: engine.joi.arkAddress(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi.object().required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/multi-payment.js b/packages/crypto/lib/validation/rules/models/transactions/multi-payment.js deleted file mode 100644 index 278ecdde2c..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/multi-payment.js +++ /dev/null @@ -1,62 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.MULTI_PAYMENT), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .min(0) - .required(), - ), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .min(0) - .required(), - ), - senderId: engine.joi.arkAddress(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi.object().required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/multi-signature.js b/packages/crypto/lib/validation/rules/models/transactions/multi-signature.js deleted file mode 100644 index fe0b436e2a..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/multi-signature.js +++ /dev/null @@ -1,103 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - let maxMinValue = 16 - let signaturesLength = 2 - if ( - transaction.asset && - transaction.asset.multisignature && - Array.isArray(transaction.asset.multisignature.keysgroup) - ) { - maxMinValue = transaction.asset.multisignature.keysgroup.length - signaturesLength = maxMinValue - } - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.MULTI_SIGNATURE), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi - .alternatives() - .try(engine.joi.bignumber(), engine.joi.number().valid(0)), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - senderId: engine.joi.arkAddress(), - recipientId: engine.joi.empty(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi - .array() - .length(signaturesLength) - .required(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi - .object({ - multisignature: engine.joi - .object({ - min: engine.joi - .number() - .integer() - .positive() - .max(Math.min(maxMinValue, 16)) - .required(), - keysgroup: engine.joi - .array() - .unique() - .min(2) - .items( - engine.joi - .string() - .not(`+${transaction.senderPublicKey}`) - .length(67) - .regex(/^\+/) - .required(), - ) - .required(), - lifetime: engine.joi - .number() - .integer() - .min(1) - .max(72) - .required(), - }) - .required(), - }) - .required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/second-signature.js b/packages/crypto/lib/validation/rules/models/transactions/second-signature.js deleted file mode 100644 index 69f1d82055..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/second-signature.js +++ /dev/null @@ -1,65 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.SECOND_SIGNATURE), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi - .alternatives() - .try(engine.joi.bignumber(), engine.joi.number().valid(0)), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - senderId: engine.joi.arkAddress(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.empty(), - asset: engine.joi - .object({ - signature: engine.joi - .object({ - publicKey: engine.joi.arkPublicKey().required(), - }) - .required(), - }) - .required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/timelock-transfer.js b/packages/crypto/lib/validation/rules/models/transactions/timelock-transfer.js deleted file mode 100644 index e1862fa3de..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/timelock-transfer.js +++ /dev/null @@ -1,52 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.TIMELOCK_TRANSFER), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi - .alternatives() - .try(engine.joi.bignumber(), engine.joi.number().integer()), - fee: engine.joi - .alternatives() - .try(engine.joi.bignumber(), engine.joi.number().integer()), - senderId: engine.joi.arkAddress(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi.object().required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/transfer.js b/packages/crypto/lib/validation/rules/models/transactions/transfer.js deleted file mode 100644 index 463c05de9c..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/transfer.js +++ /dev/null @@ -1,63 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.TRANSFER), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - senderId: engine.joi.arkAddress(), - recipientId: engine.joi.arkAddress().required(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - vendorField: engine.joi.string().max(64, 'utf8'), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/models/transactions/vote.js b/packages/crypto/lib/validation/rules/models/transactions/vote.js deleted file mode 100644 index ad28b6a3dc..0000000000 --- a/packages/crypto/lib/validation/rules/models/transactions/vote.js +++ /dev/null @@ -1,71 +0,0 @@ -const { TRANSACTION_TYPES } = require('../../../../constants') -const engine = require('../../../engine') - -module.exports = transaction => { - const { error, value } = engine.validate( - transaction, - engine.joi.object({ - id: engine.joi - .string() - .alphanum() - .required(), - blockid: engine.joi - .alternatives() - .try(engine.joi.arkBlockId(), engine.joi.number().unsafe()), - type: engine.joi.number().valid(TRANSACTION_TYPES.VOTE), - timestamp: engine.joi - .number() - .integer() - .min(0) - .required(), - amount: engine.joi - .alternatives() - .try(engine.joi.bignumber(), engine.joi.number().valid(0)), - fee: engine.joi.alternatives().try( - engine.joi.bignumber(), - engine.joi - .number() - .integer() - .positive() - .required(), - ), - senderId: engine.joi.arkAddress(), - recipientId: engine.joi.arkAddress().required(), - senderPublicKey: engine.joi.arkPublicKey().required(), - signature: engine.joi - .string() - .alphanum() - .required(), - signatures: engine.joi.array(), - secondSignature: engine.joi.string().alphanum(), - asset: engine.joi - .object({ - votes: engine.joi - .array() - .items( - engine.joi - .string() - .length(67) - .regex(/^(\+|-)[a-zA-Z0-9]+$/), - ) - .length(1) - .required(), - }) - .required(), - confirmations: engine.joi - .number() - .integer() - .min(0), - }), - { - allowUnknown: true, - }, - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/public-key.js b/packages/crypto/lib/validation/rules/public-key.js deleted file mode 100644 index a5522b003c..0000000000 --- a/packages/crypto/lib/validation/rules/public-key.js +++ /dev/null @@ -1,15 +0,0 @@ -const engine = require('../engine') - -module.exports = attributes => { - const { error, value } = engine.validate( - attributes, - engine.joi.arkPublicKey(), - ) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/rules/username.js b/packages/crypto/lib/validation/rules/username.js deleted file mode 100644 index 20d8720c4e..0000000000 --- a/packages/crypto/lib/validation/rules/username.js +++ /dev/null @@ -1,12 +0,0 @@ -const engine = require('../engine') - -module.exports = attributes => { - const { error, value } = engine.validate(attributes, engine.joi.arkUsername()) - - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } -} diff --git a/packages/crypto/lib/validation/validator.js b/packages/crypto/lib/validation/validator.js deleted file mode 100644 index 4994feb7ba..0000000000 --- a/packages/crypto/lib/validation/validator.js +++ /dev/null @@ -1,127 +0,0 @@ -const engine = require('./engine') - -class Validator { - /** - * Create a new validator instance. - */ - constructor() { - this.rules = require('./rules') - this.engine = engine - } - - /** - * Run the validator's rules against its data. - * @param {*} attributes - * @param {Object} rules - * @return {void|Boolean} - */ - async validate(attributes, rules) { - this.__reset() - - if (rules instanceof String) { - return this.__validateWithRule(attributes, rules) - } - - if (rules instanceof Function) { - return this.__validateWithFunction(attributes, rules) - } - - if (rules instanceof Object) { - return this.__validateWithJoi(attributes, rules) - } - - return false - } - - /** - * Determine if the data passes the validation rules. - * @return {Boolean} - */ - passes() { - return this.results.passes - } - - /** - * Determine if the data fails the validation rules. - * @return {Boolean} - */ - fails() { - return this.results.fails - } - - /** - * Get the validated data. - * @return {*} - */ - validated() { - return this.results.data - } - - /** - * Get the validation errors. - * @return {Array} - */ - errors() { - return this.results.errors - } - - /** - * Add a new rule to the validator. - * @return {void} - */ - extend(name, implementation) { - this.rules[name] = implementation - } - - /** - * Run the validator's rules against its data using a rule. - * @param {*} attributes - * @param {String} rule - * @return {void} - */ - __validateWithRule(attributes, rules) { - const validate = this.rules[rules] - - if (!rules) { - throw new Error('An invalid set of rules was provided.') - } - - this.results = validate(attributes) - } - - /** - * Run the validator's rules against its data using a function. - * @param {*} attributes - * @param {String} rule - * @return {void} - */ - __validateWithFunction(attributes, validate) { - this.results = validate(attributes) - } - - /** - * Run the validator's rules against its data using Joi. - * @param {*} attributes - * @param {String} rule - * @return {void} - */ - __validateWithJoi(attributes, rules) { - const { error, value } = this.engine.validate(attributes, rules) - - this.results = { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } - } - - /** - * Reset any previous results. - */ - __reset() { - this.results = null - } -} - -module.exports = new Validator() diff --git a/packages/crypto/lib/validation/validators/transaction.js b/packages/crypto/lib/validation/validators/transaction.js deleted file mode 100644 index b9a36e5847..0000000000 --- a/packages/crypto/lib/validation/validators/transaction.js +++ /dev/null @@ -1,27 +0,0 @@ -const engine = require('../engine') -const transactionExtensions = require('../extensions/transactions/index') - -class TransactionValidator { - constructor() { - this.rules = Object.keys(transactionExtensions).reduce((rules, type) => { - rules[type] = transactionExtensions[type](engine.joi).base - return rules - }, {}) - } - - validate(transaction) { - const { value, error } = engine.validate( - transaction, - this.rules[transaction.type], - { allowUnknown: true }, - ) - return { - data: value, - errors: error ? error.details : null, - passes: !error, - fails: error, - } - } -} - -module.exports = new TransactionValidator() diff --git a/packages/crypto/package.json b/packages/crypto/package.json index 07f15d2b62..cec9dd105f 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -1,57 +1,86 @@ { - "name": "@arkecosystem/crypto", - "description": "Crypto utilities for the Ark Blockchain", - "version": "0.2.6", - "contributors": [ - "François-Xavier Thoorens ", - "Brian Faust ", - "Alex Barnsley ", - "Lúcio Rubens ", - "Juan A. Martín ", - "Joshua Noack " - ], - "license": "MIT", - "main": "lib/index.js", - "browser": "dist/index.umd.js", - "module": "dist/index.cjs.js", - "files": [ - "lib", - "dist" - ], - "scripts": { - "prepublish": "yarn run lint && yarn run build", - "build": "rimraf dist && cross-env NODE_ENV=production webpack --config build/webpack.config.js", - "test": "cross-env ARK_ENV=test jest --runInBand --detectOpenHandles", - "test:coverage": "cross-env ARK_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.js|index.js)$' --runInBand --detectOpenHandles", - "test:debug": "cross-env ARK_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", - "test:watch": "jest --watch", - "test:watch:all": "jest --watchAll", - "lint": "eslint ./ --fix" - }, - "dependencies": { - "bignumber.js": "^8.0.1", - "bip32": "^1.0.2", - "bip38": "^2.0.2", - "bip39": "^2.5.0", - "bs58check": "^2.1.2", - "bytebuffer": "^5.0.1", - "create-hash": "^1.2.0", - "dayjs-ext": "^2.2.0", - "deepmerge": "^3.0.0", - "joi": "^14.3.0", - "lodash.camelcase": "^4.3.0", - "lodash.clonedeepwith": "^4.5.0", - "lodash.get": "^4.4.2", - "node-forge": "^0.7.6", - "otplib": "^10.0.1", - "pluralize": "^7.0.0", - "secp256k1": "^3.5.2", - "tiny-glob": "^0.2.3", - "webpack-merge": "^4.1.4", - "webpack-node-externals": "^1.7.2", - "wif": "^2.0.6" - }, - "publishConfig": { - "access": "public" - } + "name": "@arkecosystem/crypto", + "description": "Crypto utilities for the Ark Blockchain", + "version": "2.1.0", + "contributors": [ + "François-Xavier Thoorens ", + "Brian Faust ", + "Alex Barnsley ", + "Lúcio Rubens ", + "Juan A. Martín ", + "Joshua Noack " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "browser": "dist/index.umd.js", + "module": "dist/index.cjs.js", + "files": [ + "dist" + ], + "scripts": { + "prepublishOnly": "yarn test && yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && tsc", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "bundle": "rimraf dist && cross-env NODE_ENV=production webpack --config build/webpack.config.js", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "jest --watch", + "test:watch:all": "jest --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@types/bip32": "^1.0.0", + "@types/bip39": "^2.4.1", + "@types/bytebuffer": "^5.0.37", + "@types/create-hash": "^1.2.0", + "@types/deepmerge": "^2.2.0", + "@types/joi": "^14.0.1", + "@types/lodash.camelcase": "^4.3.4", + "@types/lodash.get": "^4.4.4", + "@types/lodash.set": "^4.3.4", + "@types/lodash.sumby": "^4.6.4", + "@types/node-forge": "^0.7.11", + "@types/otplib": "^7.0.0", + "@types/pluralize": "^0.0.29", + "@types/secp256k1": "^3.5.0", + "@types/wif": "^2.0.1", + "bignumber.js": "^8.0.1", + "bip32": "^1.0.2", + "bip39": "^2.5.0", + "bs58check": "^2.1.2", + "bytebuffer": "^5.0.1", + "create-hash": "^1.2.0", + "dayjs-ext": "^2.2.0", + "deepmerge": "^3.0.0", + "joi": "^14.3.0", + "lodash.camelcase": "^4.3.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.sumby": "^4.6.0", + "node-forge": "^0.7.6", + "otplib": "^10.0.1", + "pluralize": "^7.0.0", + "secp256k1": "^3.5.2", + "tiny-glob": "^0.2.6", + "ts-loader": "^5.3.1", + "webpack-merge": "^4.1.5", + "webpack-node-externals": "^1.7.2", + "wif": "^2.0.6" + }, + "publishConfig": { + "access": "public" + }, + "jest": { + "preset": "../../jest-preset.json" + }, + "devDependencies": { + "@types/webpack-merge": "^4.1.3", + "@types/webpack-node-externals": "^1.6.3" + } } diff --git a/packages/crypto/src/builder/index.ts b/packages/crypto/src/builder/index.ts new file mode 100644 index 0000000000..f9c82c989d --- /dev/null +++ b/packages/crypto/src/builder/index.ts @@ -0,0 +1,76 @@ +import { DelegateRegistrationBuilder } from "./transactions/delegate-registration"; +import { DelegateResignationBuilder } from "./transactions/delegate-resignation"; +import { IPFSBuilder } from "./transactions/ipfs"; +import { MultiPaymentBuilder } from "./transactions/multi-payment"; +import { MultiSignatureBuilder } from "./transactions/multi-signature"; +import { SecondSignatureBuilder } from "./transactions/second-signature"; +import { TimelockTransferBuilder } from "./transactions/timelock-transfer"; +import { TransferBuilder } from "./transactions/transfer"; +import { VoteBuilder } from "./transactions/vote"; + +export class TransactionBuilderFactory { + /** + * Create new transfer transaction type. + */ + public transfer(): TransferBuilder { + return new TransferBuilder(); + } + + /** + * Create new second signature transaction type. + */ + public secondSignature(): SecondSignatureBuilder { + return new SecondSignatureBuilder(); + } + + /** + * Create new delegate transaction type. + */ + public delegateRegistration(): DelegateRegistrationBuilder { + return new DelegateRegistrationBuilder(); + } + + /** + * Create new vote transaction type. + */ + public vote(): VoteBuilder { + return new VoteBuilder(); + } + + /** + * Create new multi-signature transaction type. + */ + public multiSignature(): MultiSignatureBuilder { + return new MultiSignatureBuilder(); + } + + /** + * Create new IPFS transaction type. + */ + public ipfs(): IPFSBuilder { + return new IPFSBuilder(); + } + + /** + * Create new timelock transfer transaction type. + */ + public timelockTransfer(): TimelockTransferBuilder { + return new TimelockTransferBuilder(); + } + + /** + * Create new multi-payment transaction type. + */ + public multiPayment(): MultiPaymentBuilder { + return new MultiPaymentBuilder(); + } + + /** + * Create new delegate resignation transaction type. + */ + public delegateResignation(): DelegateResignationBuilder { + return new DelegateResignationBuilder(); + } +} + +export const transactionBuilder = new TransactionBuilderFactory(); diff --git a/packages/crypto/src/builder/transactions/delegate-registration.ts b/packages/crypto/src/builder/transactions/delegate-registration.ts new file mode 100644 index 0000000000..3f86750902 --- /dev/null +++ b/packages/crypto/src/builder/transactions/delegate-registration.ts @@ -0,0 +1,38 @@ +import { TransactionTypes } from "../../constants"; +import { crypto } from "../../crypto"; +import { feeManager } from "../../managers"; +import { ITransactionAsset, ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class DelegateRegistrationBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.DelegateRegistration; + this.data.fee = feeManager.get(TransactionTypes.DelegateRegistration); + this.data.amount = 0; + this.data.recipientId = null; + this.data.senderPublicKey = null; + this.data.asset = { delegate: {} } as ITransactionAsset; + } + + /** + * Establish the delegate username on the asset. + */ + public usernameAsset(username: string): DelegateRegistrationBuilder { + this.data.asset.delegate.username = username; + return this; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.recipientId = this.data.recipientId; + struct.asset = this.data.asset; + return struct; + } + + protected instance(): DelegateRegistrationBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/delegate-resignation.ts b/packages/crypto/src/builder/transactions/delegate-resignation.ts new file mode 100644 index 0000000000..7aef76cdf2 --- /dev/null +++ b/packages/crypto/src/builder/transactions/delegate-resignation.ts @@ -0,0 +1,26 @@ +import { TransactionTypes } from "../../constants"; +import { feeManager } from "../../managers"; +import { ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class DelegateResignationBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.DelegateResignation; + this.data.fee = feeManager.get(TransactionTypes.DelegateResignation); + this.data.amount = 0; + this.data.asset = {}; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.asset = this.data.asset; + return struct; + } + + protected instance(): DelegateResignationBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/ipfs.ts b/packages/crypto/src/builder/transactions/ipfs.ts new file mode 100644 index 0000000000..6c52f36557 --- /dev/null +++ b/packages/crypto/src/builder/transactions/ipfs.ts @@ -0,0 +1,66 @@ +import { TransactionTypes } from "../../constants"; +import { feeManager } from "../../managers"; +import { ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class IPFSBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.Ipfs; + this.data.fee = feeManager.get(TransactionTypes.Ipfs); + this.data.amount = 0; + this.data.vendorFieldHex = null; + this.data.senderPublicKey = null; + this.data.asset = {}; + } + + /** + * Set the IPFS hash. + */ + public ipfsHash(ipfsHash: string): IPFSBuilder { + this.data.ipfsHash = ipfsHash; + return this; + } + + /** + * Set vendor field from hash. + * TODO: revise + */ + public vendorField(type: string): IPFSBuilder { + this.data.vendorFieldHex = Buffer.from(this.data.ipfsHash, type).toString("hex"); + + while (this.data.vendorFieldHex.length < 128) { + this.data.vendorFieldHex = `00${this.data.vendorFieldHex}`; + } + + // TODO is this right? when is vendorFieldHex.length is odd, + // it will add 1 more "0" than previous way + // const vendorFieldHex = Buffer.from(this.data.ipfsHash, type).toString('hex') + // this.data.vendorFieldHex = vendorFieldHex.padStart(128, '0') + + return this; + } + + public dag(dag: string): IPFSBuilder { + this.data.asset = { + ipfs: { + dag, + }, + }; + + return this; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.vendorFieldHex = this.data.vendorFieldHex; + struct.asset = this.data.asset; + return struct; + } + + protected instance(): IPFSBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/multi-payment.ts b/packages/crypto/src/builder/transactions/multi-payment.ts new file mode 100644 index 0000000000..46479a263c --- /dev/null +++ b/packages/crypto/src/builder/transactions/multi-payment.ts @@ -0,0 +1,52 @@ +import { TransactionTypes } from "../../constants"; +import { MaximumPaymentCountExceededError } from "../../errors"; +import { feeManager } from "../../managers"; +import { ITransactionData } from "../../models"; +import { Bignum } from "../../utils"; +import { TransactionBuilder } from "./transaction"; + +export class MultiPaymentBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.MultiPayment; + this.data.fee = feeManager.get(TransactionTypes.MultiPayment); + this.data.payments = {}; + this.data.vendorFieldHex = null; + this.data.asset = { + payments: [], + }; + this.data.amount = new Bignum(0); + } + + /** + * Add payment to the multipayment collection. + */ + public addPayment(recipientId: string, amount: number): MultiPaymentBuilder { + if (this.data.asset.payments.length >= 2258) { + throw new MaximumPaymentCountExceededError(this.data.asset.payments.length); + } + + this.data.asset.payments.push({ + amount: new Bignum(amount), + recipientId, + }); + this.data.amount = (this.data.amount as Bignum).plus(amount); + + return this; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.senderPublicKey = this.data.senderPublicKey; + struct.vendorFieldHex = this.data.vendorFieldHex; + struct.amount = this.data.amount; + struct.asset = this.data.asset; + + return struct; + } + + protected instance(): MultiPaymentBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/multi-signature.ts b/packages/crypto/src/builder/transactions/multi-signature.ts new file mode 100644 index 0000000000..61ca5bd3ea --- /dev/null +++ b/packages/crypto/src/builder/transactions/multi-signature.ts @@ -0,0 +1,42 @@ +import { TransactionTypes } from "../../constants"; +import { feeManager } from "../../managers"; +import { IMultiSignatureAsset, ITransactionAsset, ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class MultiSignatureBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.MultiSignature; + this.data.fee = 0; + this.data.amount = 0; + this.data.recipientId = null; + this.data.senderPublicKey = null; + this.data.asset = { multisignature: {} } as ITransactionAsset; + + this.signWithSenderAsRecipient = true; + } + + /** + * Establish the multi-signature on the asset and updates the fee. + */ + public multiSignatureAsset(multiSignature: IMultiSignatureAsset): MultiSignatureBuilder { + this.data.asset.multisignature = multiSignature; + this.data.fee = (multiSignature.keysgroup.length + 1) * feeManager.get(TransactionTypes.MultiSignature); + + return this; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.recipientId = this.data.recipientId; + struct.asset = this.data.asset; + + return struct; + } + + protected instance(): MultiSignatureBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/second-signature.ts b/packages/crypto/src/builder/transactions/second-signature.ts new file mode 100644 index 0000000000..e47f67a5db --- /dev/null +++ b/packages/crypto/src/builder/transactions/second-signature.ts @@ -0,0 +1,42 @@ +import { TransactionTypes } from "../../constants"; +import { crypto } from "../../crypto"; +import { feeManager } from "../../managers"; +import { ITransactionAsset, ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class SecondSignatureBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.SecondSignature; + this.data.fee = feeManager.get(TransactionTypes.SecondSignature); + this.data.amount = 0; + this.data.recipientId = null; + this.data.senderPublicKey = null; + this.data.asset = { signature: {} } as ITransactionAsset; + } + + /** + * Establish the signature on the asset, which is the one that would be that + * would be register on the blockchain, when creating a second passphrase. + */ + public signatureAsset(secondPassphrase: string): SecondSignatureBuilder { + this.data.asset.signature.publicKey = crypto.getKeys(secondPassphrase).publicKey; + return this; + } + + /** + * Overrides the inherited method to return the additional required by this. + */ + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.recipientId = this.data.recipientId; + struct.asset = this.data.asset; + return struct; + } + + protected instance(): SecondSignatureBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/timelock-transfer.ts b/packages/crypto/src/builder/transactions/timelock-transfer.ts new file mode 100644 index 0000000000..9e72b9ae51 --- /dev/null +++ b/packages/crypto/src/builder/transactions/timelock-transfer.ts @@ -0,0 +1,44 @@ +import { TransactionTypes } from "../../constants"; +import { feeManager } from "../../managers"; +import { ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class TimelockTransferBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.TimelockTransfer; + this.data.fee = feeManager.get(TransactionTypes.TimelockTransfer); + this.data.amount = 0; + this.data.recipientId = null; + this.data.senderPublicKey = null; + this.data.timelockType = 0x00; + this.data.timelock = null; + this.data.asset = {}; + } + + /** + * Set the timelock and the timelock type + */ + public timelock(timelock: number, timelockType: number): TimelockTransferBuilder { + this.data.timelock = timelock; + this.data.timelockType = timelockType; + return this; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.recipientId = this.data.recipientId; + struct.vendorFieldHex = this.data.vendorFieldHex; + struct.vendorField = this.data.vendorField; + struct.asset = this.data.asset; + struct.timelock = this.data.timelock; + struct.timelockType = this.data.timelockType; + return struct; + } + + protected instance(): TimelockTransferBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/transaction.ts b/packages/crypto/src/builder/transactions/transaction.ts new file mode 100644 index 0000000000..660bd7a15b --- /dev/null +++ b/packages/crypto/src/builder/transactions/transaction.ts @@ -0,0 +1,219 @@ +import { crypto, slots } from "../../crypto"; +import { MissingTransactionSignatureError } from "../../errors"; +import { configManager } from "../../managers"; +import { ITransactionData, Transaction } from "../../models"; +import { INetwork } from "../../networks"; +import { Bignum } from "../../utils"; + +export abstract class TransactionBuilder> { + public data: ITransactionData; + + protected signWithSenderAsRecipient: boolean = false; + + constructor() { + this.data = { + id: null, + timestamp: slots.getTime(), + version: 0x01, + } as ITransactionData; + } + + /** + * Build a new Transaction instance. + */ + public build(data: Partial = {}): Transaction { + return new Transaction({ ...this.data, ...data }); + } + + /** + * Set transaction version. + */ + public version(version: number): TBuilder { + this.data.version = version; + return this.instance(); + } + + /** + * Set transaction network. + */ + public network(network: number): TBuilder { + this.data.network = network; + return this.instance(); + } + + /** + * Set transaction fee. + */ + public fee(fee: Bignum | number | string): TBuilder { + if (fee !== null) { + this.data.fee = fee; + } + + return this.instance(); + } + + /** + * Set amount to transfer. + */ + public amount(amount: Bignum | number | string): TBuilder { + this.data.amount = amount; + return this.instance(); + } + + /** + * Set recipient id. + */ + public recipientId(recipientId: string): TBuilder { + this.data.recipientId = recipientId; + return this.instance(); + } + + /** + * Set sender public key. + */ + public senderPublicKey(publicKey: string): TBuilder { + this.data.senderPublicKey = publicKey; + return this.instance(); + } + + /** + * Set vendor field. + */ + public vendorField(vendorField: string): TBuilder { + if (vendorField && Buffer.from(vendorField).length <= 64) { + this.data.vendorField = vendorField; + } + + return this.instance(); + } + + /** + * Sign transaction using passphrase. + */ + public sign(passphrase: string): TBuilder { + const keys = crypto.getKeys(passphrase); + this.data.senderPublicKey = keys.publicKey; + + if (this.signWithSenderAsRecipient) { + const pubKeyHash = this.data.network || configManager.get("pubKeyHash"); + this.data.recipientId = crypto.getAddress(crypto.getKeys(passphrase).publicKey, pubKeyHash); + } + + this.data.signature = crypto.sign(this.getSigningObject(), keys); + + return this.instance(); + } + + /** + * Sign transaction using wif. + */ + public signWithWif(wif: string, networkWif?: number): TBuilder { + const keys = crypto.getKeysFromWIF(wif, { + wif: networkWif || configManager.get("wif"), + } as INetwork); + this.data.senderPublicKey = keys.publicKey; + + if (this.signWithSenderAsRecipient) { + const pubKeyHash = this.data.network || configManager.get("pubKeyHash"); + + this.data.recipientId = crypto.getAddress(keys.publicKey, pubKeyHash); + } + + this.data.signature = crypto.sign(this.getSigningObject(), keys); + + return this.instance(); + } + + /** + * Sign transaction with second passphrase. + */ + public secondSign(secondPassphrase: string): TBuilder { + if (secondPassphrase) { + const keys = crypto.getKeys(secondPassphrase); + // TODO sign or second? + this.data.signSignature = crypto.secondSign(this.getSigningObject(), keys); + } + + return this.instance(); + } + + /** + * Sign transaction with wif. + */ + public secondSignWithWif(wif: string, networkWif?: number): TBuilder { + if (wif) { + const keys = crypto.getKeysFromWIF(wif, { + wif: networkWif || configManager.get("wif"), + } as INetwork); + // TODO sign or second? + this.data.signSignature = crypto.secondSign(this.getSigningObject(), keys); + } + + return this.instance(); + } + + /** + * Sign transaction for multi-signature wallets. + */ + public multiSignatureSign(passphrase: string): TBuilder { + const keys = crypto.getKeys(passphrase); + if (!this.data.signatures) { + this.data.signatures = []; + } + this.data.signatures.push(crypto.sign(this.getSigningObject(), keys)); + + return this.instance(); + } + + /** + * Verify the transaction. + */ + public verify(): boolean { + return crypto.verify(this.data); + } + + /** + * Get structure of transaction + */ + public getStruct(): ITransactionData { + if (!this.data.senderPublicKey || !this.data.signature) { + throw new MissingTransactionSignatureError(); + } + + const struct = { + // hex: crypto.getBytes(this).toString('hex'), // v2 + id: crypto.getId(this.data).toString(), + signature: this.data.signature, + signSignature: this.data.signSignature, + timestamp: this.data.timestamp, + version: this.data.version, + type: this.data.type, + fee: this.data.fee, + senderPublicKey: this.data.senderPublicKey, + network: this.data.network, + } as ITransactionData; + + if (Array.isArray(this.data.signatures)) { + struct.signatures = this.data.signatures; + } + + return struct; + } + + protected abstract instance(): TBuilder; + + /** + * Get a valid object used to sign a transaction. + */ + private getSigningObject(): ITransactionData { + const data = Object.assign({}, this.data) as ITransactionData; + + Object.keys(data).forEach(key => { + if (["model", "network", "id"].includes(key)) { + delete data[key]; + } + }); + + return data; + } +} diff --git a/packages/crypto/src/builder/transactions/transfer.ts b/packages/crypto/src/builder/transactions/transfer.ts new file mode 100644 index 0000000000..97d3358c2d --- /dev/null +++ b/packages/crypto/src/builder/transactions/transfer.ts @@ -0,0 +1,31 @@ +import { TransactionTypes } from "../../constants"; +import { feeManager } from "../../managers"; +import { ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class TransferBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.Transfer; + this.data.fee = feeManager.get(TransactionTypes.Transfer); + this.data.amount = 0; + this.data.recipientId = null; + this.data.senderPublicKey = null; + this.data.expiration = 0; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.recipientId = this.data.recipientId; + struct.asset = this.data.asset; + struct.vendorField = this.data.vendorField; + // struct.vendorFieldHex = this.vendorFieldHex // v2 + return struct; + } + + protected instance(): TransferBuilder { + return this; + } +} diff --git a/packages/crypto/src/builder/transactions/vote.ts b/packages/crypto/src/builder/transactions/vote.ts new file mode 100644 index 0000000000..e41fcbd3da --- /dev/null +++ b/packages/crypto/src/builder/transactions/vote.ts @@ -0,0 +1,39 @@ +import { TransactionTypes } from "../../constants"; +import { feeManager } from "../../managers"; +import { ITransactionData } from "../../models"; +import { TransactionBuilder } from "./transaction"; + +export class VoteBuilder extends TransactionBuilder { + constructor() { + super(); + + this.data.type = TransactionTypes.Vote; + this.data.fee = feeManager.get(TransactionTypes.Vote); + this.data.amount = 0; + this.data.recipientId = null; + this.data.senderPublicKey = null; + this.data.asset = { votes: [] }; + + this.signWithSenderAsRecipient = true; + } + + /** + * Establish the votes on the asset. + */ + public votesAsset(votes: string[]): VoteBuilder { + this.data.asset.votes = votes; + return this; + } + + public getStruct(): ITransactionData { + const struct = super.getStruct(); + struct.amount = this.data.amount; + struct.recipientId = this.data.recipientId; + struct.asset = this.data.asset; + return struct; + } + + protected instance(): VoteBuilder { + return this; + } +} diff --git a/packages/crypto/src/client.ts b/packages/crypto/src/client.ts new file mode 100644 index 0000000000..05d6ac5b25 --- /dev/null +++ b/packages/crypto/src/client.ts @@ -0,0 +1,48 @@ +import { transactionBuilder } from "./builder"; +import { configManager } from "./managers"; +import { feeManager } from "./managers"; +import { NetworkManager } from "./managers"; + +export class Client { + /** + * @constructor + * @param {Object} config + */ + constructor(config?) { + this.setConfig(config || NetworkManager.findByName("devnet")); + } + + /** + * Set config for client. + * @param {Object} config + */ + public setConfig(config) { + configManager.setConfig(config); + } + + /** + * Get fee manager. + * @return {FeeManager} + */ + public getFeeManager() { + return feeManager; + } + + /** + * Get config manager. + * @return {ConfigManager} + */ + public getConfigManager() { + return configManager; + } + + /** + * Get transaction builder. + * @return {TransactionBuilder} + */ + public getBuilder() { + return transactionBuilder; + } +} + +export const client = new Client(); diff --git a/packages/crypto/src/constants.ts b/packages/crypto/src/constants.ts new file mode 100644 index 0000000000..c4592c881a --- /dev/null +++ b/packages/crypto/src/constants.ts @@ -0,0 +1,20 @@ +/** + * The Arktoshi base. + */ +export const ARKTOSHI: number = 1e8; + +/** + * Available transaction types. + */ + +export enum TransactionTypes { + Transfer = 0, + SecondSignature = 1, + DelegateRegistration = 2, + Vote = 3, + MultiSignature = 4, + Ipfs = 5, + TimelockTransfer = 6, + MultiPayment = 7, + DelegateResignation = 8, +} diff --git a/packages/crypto/src/crypto/bip38.ts b/packages/crypto/src/crypto/bip38.ts new file mode 100644 index 0000000000..1ab60cea15 --- /dev/null +++ b/packages/crypto/src/crypto/bip38.ts @@ -0,0 +1,239 @@ +// tslint:disable:no-bitwise + +/** + * Based on: https://github.com/bitcoinjs/bip38 @ 8e3a2cc6f7391782f3012129924a73bb632a3d4d + */ + +import assert from "assert"; +import aes from "browserify-aes"; +import bs58check from "bs58check"; +import xor from "buffer-xor/inplace"; +import crypto from "crypto"; +import secp256k1 from "secp256k1"; +import { crypto as arkCrypto, HashAlgorithms } from "../crypto"; +import { + Bip38CompressionError, + Bip38LengthError, + Bip38PrefixError, + Bip38TypeError, + PrivateKeyLengthError, +} from "../errors"; + +const SCRYPT_PARAMS = { + N: 16384, // specified by BIP38 + r: 8, + p: 8, +}; +const NULL = Buffer.alloc(0); + +export interface DecryptResult { + privateKey: Buffer; + compressed: boolean; +} + +export function encrypt(privateKey: Buffer, compressed: boolean, passphrase: string): string { + return bs58check.encode(encryptRaw(privateKey, compressed, passphrase)); +} + +export function decrypt(bip38: string, passphrase): DecryptResult { + return decryptRaw(bs58check.decode(bip38), passphrase); +} + +export function verify(bip38: string): boolean { + const decoded = bs58check.decodeUnsafe(bip38); + if (!decoded) { + return false; + } + + if (decoded.length !== 39) { + return false; + } + if (decoded.readUInt8(0) !== 0x01) { + return false; + } + + const type = decoded.readUInt8(1); + const flag = decoded.readUInt8(2); + + // encrypted WIF + if (type === 0x42) { + if (flag !== 0xc0 && flag !== 0xe0) { + return false; + } + + // EC mult + } else if (type === 0x43) { + if (flag & ~0x24) { + return false; + } + } else { + return false; + } + + return true; +} + +function encryptRaw(buffer: Buffer, compressed: boolean, passphrase: string): Buffer { + if (buffer.length !== 32) { + throw new PrivateKeyLengthError(32, buffer.length); + } + + const address = getAddressPrivate(buffer, compressed); + + const secret = Buffer.from(passphrase, "utf8"); + const salt = HashAlgorithms.hash256(address).slice(0, 4); + + const scryptBuf = crypto.scryptSync(secret, salt, 64, SCRYPT_PARAMS); + const derivedHalf1 = scryptBuf.slice(0, 32); + const derivedHalf2 = scryptBuf.slice(32, 64); + + const xorBuf = xor(derivedHalf1, buffer); + const cipher = aes.createCipheriv("aes-256-ecb", derivedHalf2, NULL); + cipher.setAutoPadding(false); + cipher.end(xorBuf); + + const cipherText = cipher.read(); + + // 0x01 | 0x42 | flagByte | salt (4) | cipherText (32) + const result = Buffer.allocUnsafe(7 + 32); + result.writeUInt8(0x01, 0); + result.writeUInt8(0x42, 1); + result.writeUInt8(compressed ? 0xe0 : 0xc0, 2); + salt.copy(result, 3); + cipherText.copy(result, 7); + + return result; +} + +// some of the techniques borrowed from: https://github.com/pointbiz/bitaddress.org +function decryptRaw(buffer: Buffer, passphrase: string): DecryptResult { + // 39 bytes: 2 bytes prefix, 37 bytes payload + if (buffer.length !== 39) { + throw new Bip38LengthError(39, buffer.length); + } + if (buffer.readUInt8(0) !== 0x01) { + throw new Bip38PrefixError(0x01, buffer.readUInt8(0)); + } + + // check if BIP38 EC multiply + const type = buffer.readUInt8(1); + if (type === 0x43) { + return decryptECMult(buffer, passphrase); + } + if (type !== 0x42) { + throw new Bip38TypeError(0x42, type); + } + + const flagByte = buffer.readUInt8(2); + const compressed = flagByte === 0xe0; + if (!compressed && flagByte !== 0xc0) { + throw new Bip38CompressionError(0xc0, flagByte); + } + + const salt = buffer.slice(3, 7); + const scryptBuf = crypto.scryptSync(passphrase, salt, 64, SCRYPT_PARAMS); + const derivedHalf1 = scryptBuf.slice(0, 32); + const derivedHalf2 = scryptBuf.slice(32, 64); + + const privKeyBuf = buffer.slice(7, 7 + 32); + const decipher = aes.createDecipheriv("aes-256-ecb", derivedHalf2, NULL); + decipher.setAutoPadding(false); + decipher.end(privKeyBuf); + + const plainText = decipher.read(); + const privateKey = xor(derivedHalf1, plainText); + + // verify salt matches address + const address = getAddressPrivate(privateKey, compressed); + + const checksum = HashAlgorithms.hash256(address).slice(0, 4); + assert.deepEqual(salt, checksum); + + return { + privateKey, + compressed, + }; +} + +function decryptECMult(buffer: Buffer, passphrase: string): DecryptResult { + buffer = buffer.slice(1); // FIXME: we can avoid this + + const flag = buffer.readUInt8(1); + + const compressed = (flag & 0x20) !== 0; + const hasLotSeq = (flag & 0x04) !== 0; + + assert.equal(flag & 0x24, flag, "Invalid private key."); + + const addressHash = buffer.slice(2, 6); + const ownerEntropy = buffer.slice(6, 14); + let ownerSalt; + + // 4 bytes ownerSalt if 4 bytes lot/sequence + if (hasLotSeq) { + ownerSalt = ownerEntropy.slice(0, 4); + + // else, 8 bytes ownerSalt + } else { + ownerSalt = ownerEntropy; + } + + const encryptedPart1 = buffer.slice(14, 22); // First 8 bytes + const encryptedPart2 = buffer.slice(22, 38); // 16 bytes + + const preFactor = crypto.scryptSync(passphrase, ownerSalt, 32, SCRYPT_PARAMS); + + let passFactor; + if (hasLotSeq) { + const hashTarget = Buffer.concat([preFactor, ownerEntropy]); + passFactor = HashAlgorithms.hash256(hashTarget); + } else { + passFactor = preFactor; + } + + const publicKey = getPublicKey(passFactor, true); + const seedBPass = crypto.scryptSync(publicKey, Buffer.concat([addressHash, ownerEntropy]), 64, { + N: 1024, + r: 1, + p: 1, + }); + const derivedHalf1 = seedBPass.slice(0, 32); + const derivedHalf2 = seedBPass.slice(32, 64); + + const decipher = aes.createDecipheriv("aes-256-ecb", derivedHalf2, Buffer.alloc(0)); + decipher.setAutoPadding(false); + decipher.end(encryptedPart2); + + const decryptedPart2 = decipher.read(); + const tmp = xor(decryptedPart2, derivedHalf1.slice(16, 32)); + const seedBPart2 = tmp.slice(8, 16); + + const decipher2 = aes.createDecipheriv("aes-256-ecb", derivedHalf2, Buffer.alloc(0)); + decipher2.setAutoPadding(false); + decipher2.write(encryptedPart1); // first 8 bytes + decipher2.end(tmp.slice(0, 8)); // last 8 bytes + + const seedBPart1 = xor(decipher2.read(), derivedHalf1.slice(0, 16)); + const seedB = Buffer.concat([seedBPart1, seedBPart2], 24); + const privateKey = secp256k1.privateKeyTweakMul(HashAlgorithms.hash256(seedB), passFactor); + + return { + privateKey, + compressed, + }; +} + +function getAddressPrivate(privateKey: Buffer, compressed: boolean): string { + const publicKey = getPublicKey(privateKey, compressed); + const buffer = HashAlgorithms.hash160(publicKey); + const payload = Buffer.alloc(21); + + payload.writeUInt8(0x00, 0); + buffer.copy(payload, 1); + + return bs58check.encode(payload); +} + +function getPublicKey(buffer: Buffer, compressed: boolean): Buffer { + return Buffer.from(arkCrypto.getKeysByPrivateKey(buffer, compressed).publicKey, "hex"); +} diff --git a/packages/crypto/src/crypto/crypto.ts b/packages/crypto/src/crypto/crypto.ts new file mode 100644 index 0000000000..52313eeab0 --- /dev/null +++ b/packages/crypto/src/crypto/crypto.ts @@ -0,0 +1,343 @@ +/* tslint:disable:no-shadowed-variable */ + +import bs58check from "bs58check"; +import ByteBuffer from "bytebuffer"; +import secp256k1 from "secp256k1"; + +import { TransactionVersionError } from "../errors"; +import { Address, KeyPair, Keys, PublicKey, WIF } from "../identities"; +import { configManager } from "../managers"; +import { feeManager } from "../managers"; +import { ITransactionData } from "../models"; +import { Bignum } from "../utils"; +import { HashAlgorithms } from "./hash-algorithms"; + +const { transactionIdFixTable } = configManager.getPreset("mainnet").exceptions; + +class Crypto { + /** + * Get transaction fee. + */ + public getFee(transaction: ITransactionData): number { + return feeManager.get(transaction.type); + } + + /** + * Get the byte representation of the transaction. + */ + public getBytes( + transaction: ITransactionData, + skipSignature: boolean = false, + skipSecondSignature: boolean = false, + ): Buffer { + if (transaction.version && transaction.version !== 1) { + throw new TransactionVersionError(transaction.version); + } + + let assetSize = 0; + let assetBytes = null; + + switch (transaction.type) { + case 1: { + // Signature + const { signature } = transaction.asset; + const bb = new ByteBuffer(33, true); + const publicKeyBuffer = Buffer.from(signature.publicKey, "hex"); + + for (const byte of publicKeyBuffer) { + bb.writeByte(byte); + } + + bb.flip(); + + assetBytes = new Uint8Array(bb.toArrayBuffer()); + assetSize = assetBytes.length; + break; + } + + case 2: { + // Delegate + assetBytes = Buffer.from(transaction.asset.delegate.username, "utf8"); + assetSize = assetBytes.length; + break; + } + + case 3: { + // Vote + if (transaction.asset.votes !== null) { + assetBytes = Buffer.from(transaction.asset.votes.join(""), "utf8"); + assetSize = assetBytes.length; + } + break; + } + + case 4: { + // Multi-Signature + const keysgroupBuffer = Buffer.from(transaction.asset.multisignature.keysgroup.join(""), "utf8"); + const bb = new ByteBuffer(1 + 1 + keysgroupBuffer.length, true); + + bb.writeByte(transaction.asset.multisignature.min); + bb.writeByte(transaction.asset.multisignature.lifetime); + + for (const byte of keysgroupBuffer) { + bb.writeByte(byte); + } + + bb.flip(); + + assetBytes = bb.toBuffer(); + assetSize = assetBytes.length; + break; + } + } + + const bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 21 + 64 + 64 + 64 + assetSize, true); + bb.writeByte(transaction.type); + bb.writeInt(transaction.timestamp); + + const senderPublicKeyBuffer = Buffer.from(transaction.senderPublicKey, "hex"); + for (const byte of senderPublicKeyBuffer) { + bb.writeByte(byte); + } + + // Apply fix for broken type 1 and 4 transactions, which were + // erroneously calculated with a recipient id. + const isBrokenTransaction = Object.values(transactionIdFixTable).includes(transaction.id); + const correctType = transaction.type !== 1 && transaction.type !== 4; + if (transaction.recipientId && (isBrokenTransaction || correctType)) { + const recipient = bs58check.decode(transaction.recipientId); + for (const byte of recipient) { + bb.writeByte(byte); + } + } else { + for (let i = 0; i < 21; i++) { + bb.writeByte(0); + } + } + + if (transaction.vendorFieldHex) { + const vf = Buffer.from(transaction.vendorFieldHex, "hex"); + const fillstart = vf.length; + for (let i = 0; i < fillstart; i++) { + bb.writeByte(vf[i]); + } + for (let i = fillstart; i < 64; i++) { + bb.writeByte(0); + } + } else if (transaction.vendorField) { + const vf = Buffer.from(transaction.vendorField); + const fillstart = vf.length; + for (let i = 0; i < fillstart; i++) { + bb.writeByte(vf[i]); + } + for (let i = fillstart; i < 64; i++) { + bb.writeByte(0); + } + } else { + for (let i = 0; i < 64; i++) { + bb.writeByte(0); + } + } + + bb.writeInt64(+new Bignum(transaction.amount).toFixed()); + bb.writeInt64(+new Bignum(transaction.fee).toFixed()); + + if (assetSize > 0) { + for (let i = 0; i < assetSize; i++) { + bb.writeByte(assetBytes[i]); + } + } + + if (!skipSignature && transaction.signature) { + const signatureBuffer = Buffer.from(transaction.signature, "hex"); + for (const byte of signatureBuffer) { + bb.writeByte(byte); + } + } + + if (!skipSecondSignature && transaction.signSignature) { + const signSignatureBuffer = Buffer.from(transaction.signSignature, "hex"); + for (const byte of signSignatureBuffer) { + bb.writeByte(byte); + } + } + + bb.flip(); + const arrayBuffer = new Uint8Array(bb.toArrayBuffer()); + const buffer = []; + + for (let i = 0; i < arrayBuffer.length; i++) { + buffer[i] = arrayBuffer[i]; + } + + return Buffer.from(buffer); + } + + /** + * Get transaction id. + */ + public getId(transaction: ITransactionData): string { + if (transaction.version && transaction.version !== 1) { + throw new TransactionVersionError(transaction.version); + } + + return this.getHash(transaction).toString("hex"); + } + + /** + * Get transaction hash. + */ + public getHash( + transaction: ITransactionData, + skipSignature: boolean = false, + skipSecondSignature: boolean = false, + ): Buffer { + if (transaction.version && transaction.version !== 1) { + throw new TransactionVersionError(transaction.version); + } + + const bytes = this.getBytes(transaction, skipSignature, skipSecondSignature); + return HashAlgorithms.sha256(bytes); + } + + /** + * Sign transaction. + */ + public sign(transaction: ITransactionData, keys: KeyPair): string { + let hash; + if (!transaction.version || transaction.version === 1) { + hash = this.getHash(transaction, true, true); + } else { + hash = this.getHash(transaction, false, false); + } + + const signature = this.signHash(hash, keys); + + if (!transaction.signature) { + transaction.signature = signature; + } + + return signature; + } + + /** + * Sign transaction with second signature. + */ + public secondSign(transaction: ITransactionData, keys: KeyPair): string { + const hash = this.getHash(transaction, false, true); + const signature = this.signHash(hash, keys); + + if (!transaction.secondSignature) { + transaction.secondSignature = signature; + } + + return signature; + } + + /** + * Sign a hash + */ + public signHash(hash: Buffer, keys: KeyPair): string { + const { signature } = secp256k1.sign(hash, Buffer.from(keys.privateKey, "hex")); + return secp256k1.signatureExport(signature).toString("hex"); + } + + /** + * Verify transaction on the network. + */ + public verify(transaction: ITransactionData): boolean { + if (transaction.version && transaction.version !== 1) { + // TODO: enable AIP11 when ready here + return false; + } + + if (!transaction.signature) { + return false; + } + + const hash = this.getHash(transaction, true, true); + return this.verifyHash(hash, transaction.signature, transaction.senderPublicKey); + } + + /** + * Verify second signature for transaction. + */ + public verifySecondSignature(transaction: ITransactionData, publicKey: string): boolean { + let hash; + let secondSignature; + if (transaction.version && transaction.version !== 1) { + hash = this.getHash(transaction); + secondSignature = transaction.secondSignature; + } else { + hash = this.getHash(transaction, false, true); + secondSignature = transaction.signSignature; + } + + if (!secondSignature) { + return false; + } + + return this.verifyHash(hash, secondSignature, publicKey); + } + + /** + * Verify the hash. + */ + public verifyHash(hash: Buffer, signature: Buffer | string, publicKey: Buffer | string): boolean { + signature = signature instanceof Buffer ? signature : Buffer.from(signature, "hex"); + publicKey = publicKey instanceof Buffer ? publicKey : Buffer.from(publicKey, "hex"); + return secp256k1.verify(hash, secp256k1.signatureImport(signature), publicKey); + } + + /** + * Get keys from secret. + */ + public getKeys(secret: string, compressed: boolean = true): KeyPair { + return Keys.fromPassphrase(secret, compressed); + } + + /** + * Get keys from a private key. + */ + public getKeysByPrivateKey(privateKey: Buffer | string, compressed: boolean = true): KeyPair { + privateKey = privateKey instanceof Buffer ? privateKey : Buffer.from(privateKey, "hex"); + return Keys.fromPrivateKey(privateKey, compressed); + } + + /** + * Get keys from WIF key. + */ + public getKeysFromWIF(wifKey: string, network?: { wif: number }): KeyPair { + return Keys.fromWIF(wifKey, network); + } + + /** + * Get WIF key from keys + */ + public keysToWIF(keys: KeyPair, network?: { wif: number }): string { + return WIF.fromKeys(keys, network); + } + + /** + * Get address from public key. + */ + public getAddress(publicKey: string, networkVersion?: number): string { + return Address.fromPublicKey(publicKey, networkVersion); + } + + /** + * Validate address. + */ + public validateAddress(address: string, networkVersion?: number): boolean { + return Address.validate(address, networkVersion); + } + + /** + * Validate public key. + */ + public validatePublicKey(address: string, networkVersion?: number): boolean { + return PublicKey.validate(address, networkVersion); + } +} + +export const crypto = new Crypto(); diff --git a/packages/crypto/src/crypto/hash-algorithms.ts b/packages/crypto/src/crypto/hash-algorithms.ts new file mode 100644 index 0000000000..80417db8ba --- /dev/null +++ b/packages/crypto/src/crypto/hash-algorithms.ts @@ -0,0 +1,46 @@ +import createHash from "create-hash"; + +export class HashAlgorithms { + /** + * Create a "ripemd160" buffer. + */ + public static ripemd160(buffer: Buffer | string): Buffer { + return createHash("rmd160") + .update(buffer) + .digest(); + } + + /** + * Create a "sha1" buffer. + */ + public static sha1(buffer: Buffer | string): Buffer { + return createHash("sha1") + .update(buffer) + .digest(); + } + + /** + * Create a "sha256" buffer. + * @param {Buffer} buffer + * @return {Buffer} + */ + public static sha256(buffer: Buffer | string): Buffer { + return createHash("sha256") + .update(buffer) + .digest(); + } + + /** + * Create a "hash160" buffer. + */ + public static hash160(buffer: Buffer | string): Buffer { + return this.ripemd160(this.sha256(buffer)); + } + + /** + * Create a "hash256" buffer. + */ + public static hash256(buffer: Buffer | string): Buffer { + return this.sha256(this.sha256(buffer)); + } +} diff --git a/packages/crypto/src/crypto/hdwallet.ts b/packages/crypto/src/crypto/hdwallet.ts new file mode 100644 index 0000000000..62344b6082 --- /dev/null +++ b/packages/crypto/src/crypto/hdwallet.ts @@ -0,0 +1,52 @@ +import bip32 from "bip32"; +import bip39 from "bip39"; +import { KeyPair } from "../identities/keys"; +import { configManager } from "../managers"; + +export class HDWallet { + public static readonly slip44 = 111; + + /** + * Get root node from the given mnemonic with an optional passphrase. + */ + public static fromMnemonic(mnemonic: string, passphrase?: string): bip32.BIP32 { + const seed = bip39.mnemonicToSeed(mnemonic, passphrase); + return bip32.fromSeed(seed, configManager.config); + } + + /** + * Get bip32 node from keys. + */ + public static fromKeys(keys: KeyPair, chainCode: Buffer): bip32.BIP32 { + if (!keys.compressed) { + throw new TypeError("BIP32 only allows compressed keys."); + } + + return bip32.fromPrivateKey(Buffer.from(keys.privateKey, "hex"), chainCode, configManager.config); + } + + /** + * Get key pair from the given node. + */ + public static getKeys(node: bip32.BIP32): KeyPair { + return { + publicKey: node.publicKey.toString("hex"), + privateKey: node.privateKey.toString("hex"), + compressed: true, + }; + } + + /** + * Derives a node from the coin type as specified by slip44. + */ + public static deriveSlip44(root: bip32.BIP32, hardened: boolean = true): bip32.BIP32 { + return root.derivePath(`m/44'/${this.slip44}${hardened ? "'" : ""}`); + } + + /** + * Derives a node from the network as specified by AIP20. + */ + public static deriveNetwork(root: bip32.BIP32): bip32.BIP32 { + return this.deriveSlip44(root).deriveHardened(configManager.config.aip20 || 1); + } +} diff --git a/packages/crypto/src/crypto/index.ts b/packages/crypto/src/crypto/index.ts new file mode 100644 index 0000000000..62bd0213dd --- /dev/null +++ b/packages/crypto/src/crypto/index.ts @@ -0,0 +1,8 @@ +import * as bip38 from "./bip38"; + +export { bip38 }; +export { crypto } from "./crypto"; +export { HashAlgorithms } from "./hash-algorithms"; +export { HDWallet } from "./hdwallet"; +export { Message } from "./message"; +export { slots } from "./slots"; diff --git a/packages/crypto/src/crypto/message.ts b/packages/crypto/src/crypto/message.ts new file mode 100644 index 0000000000..97d30148d3 --- /dev/null +++ b/packages/crypto/src/crypto/message.ts @@ -0,0 +1,53 @@ +import { configManager } from "../managers"; +import { INetwork } from "../networks"; +import { crypto } from "./crypto"; +import { HashAlgorithms } from "./hash-algorithms"; + +export interface IMessage { + readonly publicKey: string; + readonly signature: string; + readonly message: string; +} + +export class Message { + /** + * Sign the given message. + */ + public static sign(message: string, passphrase: string): IMessage { + const keys = crypto.getKeys(passphrase); + + return { + publicKey: keys.publicKey, + signature: crypto.signHash(this.createHash(message), keys), + message, + }; + } + + /** + * Sign the given message using a WIF. + */ + public static signWithWif(message: string, wif: string, network?: INetwork): IMessage { + if (!network) { + network = configManager.all(); + } + + const keys = crypto.getKeysFromWIF(wif, network); + + return { + publicKey: keys.publicKey, + signature: crypto.signHash(this.createHash(message), keys), + message, + }; + } + + /** + * Verify the given message. + */ + public static verify({ message, publicKey, signature }: IMessage): boolean { + return crypto.verifyHash(this.createHash(message), signature, publicKey); + } + + private static createHash(message: string): Buffer { + return HashAlgorithms.sha256(message); + } +} diff --git a/packages/crypto/src/crypto/slots.ts b/packages/crypto/src/crypto/slots.ts new file mode 100644 index 0000000000..86e2112c80 --- /dev/null +++ b/packages/crypto/src/crypto/slots.ts @@ -0,0 +1,127 @@ +import dayjs from "dayjs-ext"; +import { configManager } from "../managers"; + +class Slots { + public height: number; + /** + * Create a new Slot instance. + */ + constructor() { + this.resetHeight(); + } + + /** + * Get the height we are currently at. + */ + public getHeight(): number { + return this.height; + } + + /** + * Set the height we are currently at. + */ + public setHeight(height: number): void { + this.height = height; + } + + /** + * Reset the height to the initial value. + */ + public resetHeight(): void { + this.height = 1; + } + + /** + * Get epoch time relative to beginning epoch time. + */ + public getEpochTime(time?: number): number { + if (time === undefined) { + time = dayjs().valueOf(); + } + + const start = this.beginEpochTime().valueOf(); + + return Math.floor((time - start) / 1000); + } + + /** + * Get beginning epoch time. + */ + public beginEpochTime(): dayjs.Dayjs { + return dayjs(this.getMilestone("epoch")).utc(); + } + + /** + * Get epoch time relative to beginning epoch time. + */ + public getTime(time?: number): number { + return this.getEpochTime(time); + } + + /** + * Get real time from relative epoch time. + */ + public getRealTime(epochTime: number): number { + if (epochTime === undefined) { + epochTime = this.getTime(); + } + + const start = Math.floor(this.beginEpochTime().valueOf() / 1000) * 1000; + + return start + epochTime * 1000; + } + + /** + * Get the current slot number. + */ + public getSlotNumber(epochTime?: number): number { + if (epochTime === undefined) { + epochTime = this.getTime(); + } + + return Math.floor(epochTime / this.getMilestone("blocktime")); + } + + /** + * Get the current slot time. + */ + public getSlotTime(slot: number): number { + return slot * this.getMilestone("blocktime"); + } + + /** + * Get the next slot number. + */ + public getNextSlot(): number { + return this.getSlotNumber() + 1; + } + + /** + * Get the last slot number. + */ + public getLastSlot(nextSlot: number): number { + return nextSlot + this.getMilestone("activeDelegates"); + } + + /** + * Checks if forging is allowed + */ + public isForgingAllowed(epochTime?: number): boolean { + if (epochTime === undefined) { + epochTime = this.getTime(); + } + + const blockTime = this.getMilestone("blocktime"); + + return epochTime % blockTime < blockTime / 2; + } + + /** + * Get constant from height 1. + */ + private getMilestone(key: string): any { + return configManager.getMilestone(this.height)[key]; + } +} + +export const slots = new Slots(); diff --git a/packages/crypto/src/deserializers/block.ts b/packages/crypto/src/deserializers/block.ts new file mode 100644 index 0000000000..11d2965cca --- /dev/null +++ b/packages/crypto/src/deserializers/block.ts @@ -0,0 +1,76 @@ +import ByteBuffer from "bytebuffer"; +import { configManager } from "../managers"; +import { Transaction } from "../models"; +import { Block, IBlockData } from "../models/block"; +import { Bignum } from "../utils/bignum"; + +const { outlookTable } = configManager.getPreset("mainnet").exceptions; + +class BlockDeserializer { + public deserialize(serializedHex: string, headerOnly: boolean = false): IBlockData { + const block = {} as IBlockData; + const buf = ByteBuffer.fromHex(serializedHex, true); + + this.deserializeHeader(block, buf); + + headerOnly = headerOnly || buf.remaining() === 0; + if (!headerOnly) { + this.deserializeTransactions(block, buf); + } + + block.idHex = Block.getIdHex(block); + block.id = new Bignum(block.idHex, 16).toFixed(); + + if (outlookTable[block.id]) { + block.id = outlookTable[block.id]; + block.idHex = Block.toBytesHex(block.id); + } + + return block; + } + + private deserializeHeader(block: IBlockData, buf: ByteBuffer): void { + block.version = buf.readUint32(); + block.timestamp = buf.readUint32(); + block.height = buf.readUint32(); + block.previousBlockHex = buf.readBytes(8).toString("hex"); + block.previousBlock = new Bignum(block.previousBlockHex, 16).toFixed(); + block.numberOfTransactions = buf.readUint32(); + block.totalAmount = new Bignum(buf.readUint64().toString()); + block.totalFee = new Bignum(buf.readUint64().toString()); + block.reward = new Bignum(buf.readUint64().toString()); + block.payloadLength = buf.readUint32(); + block.payloadHash = buf.readBytes(32).toString("hex"); + block.generatorPublicKey = buf.readBytes(33).toString("hex"); + + const signatureLength = (): number => { + buf.mark(); + const lengthHex = buf + .skip(1) + .readBytes(1) + .toString("hex"); + buf.reset(); + + return parseInt(lengthHex, 16) + 2; + }; + + block.blockSignature = buf.readBytes(signatureLength()).toString("hex"); + } + + private deserializeTransactions(block: IBlockData, buf: ByteBuffer): any { + const transactionLengths = []; + + for (let i = 0; i < block.numberOfTransactions; i++) { + transactionLengths.push(buf.readUint32()); + } + + block.transactions = []; + transactionLengths.forEach(length => { + const serializedHex = buf.readBytes(length).toString("hex"); + const transaction = new Transaction(serializedHex); + block.transactions.push(transaction); + }); + } +} + +export const blockDeserializer = new BlockDeserializer(); diff --git a/packages/crypto/src/deserializers/index.ts b/packages/crypto/src/deserializers/index.ts new file mode 100644 index 0000000000..c18fc2b193 --- /dev/null +++ b/packages/crypto/src/deserializers/index.ts @@ -0,0 +1,2 @@ +export { transactionDeserializer as TransactionDeserializer } from "./transaction"; +export { blockDeserializer as BlockDeserializer } from "./block"; diff --git a/packages/crypto/src/deserializers/transaction.ts b/packages/crypto/src/deserializers/transaction.ts new file mode 100644 index 0000000000..7d64567998 --- /dev/null +++ b/packages/crypto/src/deserializers/transaction.ts @@ -0,0 +1,238 @@ +import bs58check from "bs58check"; +import ByteBuffer from "bytebuffer"; +import { TransactionTypes } from "../constants"; +import { crypto } from "../crypto"; +import { TransactionTypeError } from "../errors"; +import { configManager } from "../managers"; +import { Transaction } from "../models"; +import { IMultiSignatureAsset, ITransactionData } from "../models/transaction"; +import { Bignum } from "../utils/bignum"; + +const { transactionIdFixTable } = configManager.getPreset("mainnet").exceptions; + +// Reference: https://github.com/ArkEcosystem/AIPs/blob/master/AIPS/aip-11.md +class TransactionDeserializer { + public deserialize(serializedHex: string): ITransactionData { + const transaction = {} as ITransactionData; + const buf = ByteBuffer.fromHex(serializedHex, true); + + this.deserializeCommon(transaction, buf); + this.deserializeVendorField(transaction, buf); + this.deserializeType(transaction, buf); + this.deserializeSignatures(transaction, buf); + this.applyV1Compatibility(transaction); + + return transaction; + } + + private deserializeCommon(transaction: ITransactionData, buf: ByteBuffer): void { + buf.skip(1); // Skip 0xFF marker + transaction.version = buf.readUint8(); + transaction.network = buf.readUint8(); + transaction.type = buf.readUint8(); + transaction.timestamp = buf.readUint32(); + transaction.senderPublicKey = buf.readBytes(33).toString("hex"); // serializedHex.substring(16, 16 + 33 * 2); + transaction.fee = new Bignum(buf.readUint64().toString()); + transaction.amount = Bignum.ZERO; + } + + private deserializeVendorField(transaction: ITransactionData, buf: ByteBuffer): void { + if (!Transaction.canHaveVendorField(transaction.type)) { + buf.skip(1); + return; + } + + const vendorFieldLength = buf.readUint8(); + if (vendorFieldLength > 0) { + transaction.vendorFieldHex = buf.readBytes(vendorFieldLength).toString("hex"); + } + } + + private deserializeType(transaction: ITransactionData, buf: ByteBuffer): void { + if (transaction.type === TransactionTypes.Transfer) { + this.deserializeTransfer(transaction, buf); + } else if (transaction.type === TransactionTypes.SecondSignature) { + this.deserializeSecondSignature(transaction, buf); + } else if (transaction.type === TransactionTypes.DelegateRegistration) { + this.deserializeDelegateRegistration(transaction, buf); + } else if (transaction.type === TransactionTypes.Vote) { + this.deserializeVote(transaction, buf); + } else if (transaction.type === TransactionTypes.MultiSignature) { + this.deserializeMultiSignature(transaction, buf); + } else if (transaction.type === TransactionTypes.Ipfs) { + this.deserializeIpfs(transaction, buf); + } else if (transaction.type === TransactionTypes.TimelockTransfer) { + this.deserializeTimelockTransfer(transaction, buf); + } else if (transaction.type === TransactionTypes.MultiPayment) { + this.deserializeMultiPayment(transaction, buf); + } else if (transaction.type === TransactionTypes.DelegateResignation) { + this.deserializeDelegateResignation(transaction, buf); + } else { + throw new TransactionTypeError(transaction.type); + } + } + + private deserializeTransfer(transaction: ITransactionData, buf: ByteBuffer): void { + transaction.amount = new Bignum(buf.readUint64().toString()); + transaction.expiration = buf.readUint32(); + transaction.recipientId = bs58check.encode(buf.readBytes(21).toBuffer()); + } + + private deserializeSecondSignature(transaction: ITransactionData, buf: ByteBuffer): void { + transaction.asset = { + signature: { + publicKey: buf.readBytes(33).toString("hex"), + }, + }; + } + + private deserializeDelegateRegistration(transaction: ITransactionData, buf: ByteBuffer): void { + const usernamelength = buf.readUint8(); + + transaction.asset = { + delegate: { + username: buf.readString(usernamelength), + }, + }; + } + + private deserializeVote(transaction: ITransactionData, buf: ByteBuffer): void { + const votelength = buf.readUint8(); + transaction.asset = { votes: [] }; + + for (let i = 0; i < votelength; i++) { + let vote = buf.readBytes(34).toString("hex"); + vote = (vote[1] === "1" ? "+" : "-") + vote.slice(2); + transaction.asset.votes.push(vote); + } + } + + private deserializeMultiSignature(transaction: ITransactionData, buf: ByteBuffer): void { + transaction.asset = { multisignature: { keysgroup: [] } as IMultiSignatureAsset }; + transaction.asset.multisignature.min = buf.readUint8(); + + const num = buf.readUint8(); + transaction.asset.multisignature.lifetime = buf.readUint8(); + + for (let index = 0; index < num; index++) { + const key = buf.readBytes(33).toString("hex"); + transaction.asset.multisignature.keysgroup.push(key); + } + } + + private deserializeIpfs(transaction: ITransactionData, buf: ByteBuffer): void { + const dagLength = buf.readUint8(); + transaction.asset = { + ipfs: { + dag: buf.readBytes(dagLength).toString("hex"), + }, + }; + } + + private deserializeTimelockTransfer(transaction: ITransactionData, buf: ByteBuffer): void { + transaction.amount = new Bignum(buf.readUint64().toString()); + transaction.timelockType = buf.readUint8(); + transaction.timelock = buf.readUint64().toNumber(); + transaction.recipientId = bs58check.encode(buf.readBytes(21).toBuffer()); + } + + private deserializeMultiPayment(transaction: ITransactionData, buf: ByteBuffer): void { + const payments = []; + const total = buf.readUint32(); + + for (let j = 0; j < total; j++) { + const payment: any = {}; + payment.amount = new Bignum(buf.readUint64().toString()); + payment.recipientId = bs58check.encode(buf.readBytes(21).toBuffer()); + payments.push(payment); + } + + transaction.amount = payments.reduce((a, p) => a.plus(p.amount), Bignum.ZERO); + transaction.asset = { payments }; + } + + private deserializeDelegateResignation(transaction: ITransactionData, buf: ByteBuffer): void { + return; + } + + private deserializeSignatures(transaction: ITransactionData, buf: ByteBuffer) { + const currentSignatureLength = (): number => { + buf.mark(); + const lengthHex = buf + .skip(1) + .readBytes(1) + .toString("hex"); + buf.reset(); + + return parseInt(lengthHex, 16) + 2; + }; + + // Signature + if (buf.remaining()) { + const signatureLength = currentSignatureLength(); + transaction.signature = buf.readBytes(signatureLength).toString("hex"); + } + + const beginningMultiSignature = () => { + buf.mark(); + const marker = buf.readUint8(); + buf.reset(); + return marker === 255; + }; + + // Second Signature + if (buf.remaining() && !beginningMultiSignature()) { + const secondSignatureLength = currentSignatureLength(); + transaction.secondSignature = buf.readBytes(secondSignatureLength).toString("hex"); + } + + // Multi Signatures + if (buf.remaining() && beginningMultiSignature()) { + buf.skip(1); + transaction.signatures = []; + + while (buf.remaining()) { + const multiSignatureLength = currentSignatureLength(); + const multiSignature = buf.readBytes(multiSignatureLength).toString("hex"); + transaction.signatures.push(multiSignature); + } + } + } + + private applyV1Compatibility(transaction: ITransactionData): void { + if (transaction.version && transaction.version !== 1) { + return; + } + + if (transaction.secondSignature) { + transaction.signSignature = transaction.secondSignature; + } + + if (transaction.type === TransactionTypes.Vote) { + transaction.recipientId = crypto.getAddress(transaction.senderPublicKey, transaction.network); + } else if (transaction.type === TransactionTypes.MultiSignature) { + transaction.asset.multisignature.keysgroup = transaction.asset.multisignature.keysgroup.map(k => `+${k}`); + } + + if (transaction.vendorFieldHex) { + transaction.vendorField = Buffer.from(transaction.vendorFieldHex, "hex").toString("utf8"); + } + + if ( + transaction.type === TransactionTypes.SecondSignature || + transaction.type === TransactionTypes.MultiSignature + ) { + transaction.recipientId = crypto.getAddress(transaction.senderPublicKey, transaction.network); + } + + transaction.id = crypto.getId(transaction); + + // Apply fix for broken type 1 and 4 transactions, which were + // erroneously calculated with a recipient id. + if (transactionIdFixTable[transaction.id]) { + transaction.id = transactionIdFixTable[transaction.id]; + } + } +} + +export const transactionDeserializer = new TransactionDeserializer(); diff --git a/packages/crypto/src/errors.ts b/packages/crypto/src/errors.ts new file mode 100644 index 0000000000..0ac47992ee --- /dev/null +++ b/packages/crypto/src/errors.ts @@ -0,0 +1,85 @@ +// tslint:disable:max-classes-per-file + +export class CryptoError extends Error { + constructor(message: string) { + super(message); + + Object.defineProperty(this, "message", { + enumerable: false, + value: message, + }); + + Object.defineProperty(this, "name", { + enumerable: false, + value: this.constructor.name, + }); + + Error.captureStackTrace(this, this.constructor); + } +} + +export class Bip38CompressionError extends CryptoError { + constructor(expected: string | number, given: string | number) { + super(`Expected flag to be ${expected}, but got ${given}.`); + } +} + +export class Bip38LengthError extends CryptoError { + constructor(expected: string | number, given: string | number) { + super(`Expected length to be ${expected}, but got ${given}.`); + } +} + +export class Bip38PrefixError extends CryptoError { + constructor(expected: string | number, given: string | number) { + super(`Expected prefix to be ${expected}, but got ${given}.`); + } +} + +export class Bip38TypeError extends CryptoError { + constructor(expected: string | number, given: string | number) { + super(`Expected type to be ${expected}, but got ${given}.`); + } +} + +export class NetworkVersionError extends CryptoError { + constructor(expected: string | number, given: string | number) { + super(`Expected version to be ${expected}, but got ${given}.`); + } +} + +export class PrivateKeyLengthError extends CryptoError { + constructor(expected: string | number, given: string | number) { + super(`Expected length to be ${expected}, but got ${given}.`); + } +} + +export class PublicKeyError extends CryptoError { + constructor(given: string) { + super(`Expected ${given} to be a valid public key.`); + } +} + +export class TransactionTypeError extends CryptoError { + constructor(given: string) { + super(`Type ${given} not supported.`); + } +} + +export class TransactionVersionError extends CryptoError { + constructor(given: number) { + super(`Version ${given} not supported.`); + } +} + +export class MaximumPaymentCountExceededError extends CryptoError { + constructor(given: number) { + super(`Expected a maximum of 2258 payments, but got ${given}.`); + } +} + +export class MissingTransactionSignatureError extends CryptoError { + constructor() { + super(`Expected the transaction to be signed.`); + } +} diff --git a/packages/crypto/src/handlers/transactions/delegate-registration.ts b/packages/crypto/src/handlers/transactions/delegate-registration.ts new file mode 100644 index 0000000000..1b63937696 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/delegate-registration.ts @@ -0,0 +1,35 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class DelegateRegistrationHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + if (!super.canApply(wallet, transaction, errors)) { + return false; + } + + const username = transaction.asset.delegate.username; + // TODO: Checking whether the username is a lowercase version of itself seems silly. Why can't we mutate it to lowercase + const canApply = !wallet.username && username && username === username.toLowerCase(); + if (!canApply) { + errors.push("Wallet already has a registered username"); + } + return canApply; + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + wallet.username = transaction.asset.delegate.username; + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + wallet.username = null; + } +} diff --git a/packages/crypto/src/handlers/transactions/delegate-resignation.ts b/packages/crypto/src/handlers/transactions/delegate-resignation.ts new file mode 100644 index 0000000000..01daeda39e --- /dev/null +++ b/packages/crypto/src/handlers/transactions/delegate-resignation.ts @@ -0,0 +1,33 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class DelegateResignationHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + if (!super.canApply(wallet, transaction, errors)) { + return false; + } + + const canApply = !!wallet.username; + if (!canApply) { + errors.push("Wallet has not registered a username"); + } + return canApply; + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + // + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + // + } +} diff --git a/packages/crypto/src/handlers/transactions/handler.ts b/packages/crypto/src/handlers/transactions/handler.ts new file mode 100644 index 0000000000..18dc8f6e48 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/handler.ts @@ -0,0 +1,111 @@ +import { crypto } from "../../crypto"; +import { configManager } from "../../managers"; +import { ITransactionData, Wallet } from "../../models"; +import { transactionValidator } from "../../validation"; + +export abstract class Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + const validationResult = transactionValidator.validate(transaction); + + if (validationResult.fails) { + errors.push(validationResult.fails.message); + return false; + } + + if (wallet.multisignature) { + return false; + } + + if ( + wallet.balance + .minus(transaction.amount) + .minus(transaction.fee) + .isLessThan(0) + ) { + errors.push("Insufficient balance in the wallet"); + return false; + } + + if (!(transaction.senderPublicKey.toLowerCase() === wallet.publicKey.toLowerCase())) { + errors.push('wallet "publicKey" does not match transaction "senderPublicKey"'); + return false; + } + + if (!wallet.secondPublicKey && (transaction.secondSignature || transaction.signSignature)) { + // Accept invalid second signature fields prior the applied patch. + if (configManager.getMilestone().ignoreInvalidSecondSignatureField) { + return true; + } + + errors.push("Invalid second-signature field"); + return false; + } + + // TODO: this can blow up if 2nd phrase and other transactions are in the wrong order + if (wallet.secondPublicKey && !crypto.verifySecondSignature(transaction, wallet.secondPublicKey)) { + errors.push("Failed to verify second-signature"); + return false; + } + + return true; + } + + /** + * Associate this wallet as the sender of a transaction. + */ + public applyTransactionToSender(wallet: Wallet, transaction: ITransactionData): void { + if ( + transaction.senderPublicKey.toLowerCase() === wallet.publicKey.toLowerCase() || + crypto.getAddress(transaction.senderPublicKey) === wallet.address + ) { + wallet.balance = wallet.balance.minus(transaction.amount).minus(transaction.fee); + + this.apply(wallet, transaction); + + wallet.dirty = true; + } + } + + /** + * Remove this wallet as the sender of a transaction. + */ + public revertTransactionForSender(wallet: Wallet, transaction: ITransactionData): void { + if ( + transaction.senderPublicKey.toLowerCase() === wallet.publicKey.toLowerCase() || + crypto.getAddress(transaction.senderPublicKey) === wallet.address + ) { + wallet.balance = wallet.balance.plus(transaction.amount).plus(transaction.fee); + + this.revert(wallet, transaction); + + wallet.dirty = true; + } + } + + /** + * Add transaction balance to this wallet. + */ + public applyTransactionToRecipient(wallet: Wallet, transaction: ITransactionData): void { + if (transaction.recipientId === wallet.address) { + wallet.balance = wallet.balance.plus(transaction.amount); + wallet.dirty = true; + } + } + + /** + * Remove transaction balance from this wallet. + */ + public revertTransactionForRecipient(wallet: Wallet, transaction: ITransactionData): void { + if (transaction.recipientId === wallet.address) { + wallet.balance = wallet.balance.minus(transaction.amount); + wallet.dirty = true; + } + } + + protected abstract apply(wallet: Wallet, transaction: ITransactionData): void; + + protected abstract revert(wallet: Wallet, transaction: ITransactionData): void; +} diff --git a/packages/crypto/src/handlers/transactions/index.ts b/packages/crypto/src/handlers/transactions/index.ts new file mode 100644 index 0000000000..e316bc00ea --- /dev/null +++ b/packages/crypto/src/handlers/transactions/index.ts @@ -0,0 +1,71 @@ +import { TransactionTypes } from "../../constants"; +import { ITransactionData, Wallet } from "../../models"; +import { DelegateRegistrationHandler } from "./delegate-registration"; +import { DelegateResignationHandler } from "./delegate-resignation"; +import { Handler } from "./handler"; +import { IpfsHandler } from "./ipfs"; +import { MultiPaymentHandler } from "./multi-payment"; +import { MultiSignatureHandler } from "./multi-signature"; +import { SecondSignatureHandler } from "./second-signature"; +import { TimelockTransferHandler } from "./timelock-transfer"; +import { TransferHandler } from "./transfer"; +import { VoteHandler } from "./vote"; + +class TransactionHandler { + public handlers: { [x in TransactionTypes]: typeof Handler }; + + constructor() { + this.handlers = { + [TransactionTypes.Transfer]: TransferHandler, + [TransactionTypes.SecondSignature]: SecondSignatureHandler, + [TransactionTypes.DelegateRegistration]: DelegateRegistrationHandler, + [TransactionTypes.Vote]: VoteHandler, + [TransactionTypes.MultiSignature]: MultiSignatureHandler, + [TransactionTypes.Ipfs]: IpfsHandler, + [TransactionTypes.TimelockTransfer]: TimelockTransferHandler, + [TransactionTypes.MultiPayment]: MultiPaymentHandler, + [TransactionTypes.DelegateResignation]: DelegateResignationHandler, + }; + } + + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + return this.getHandler(transaction).canApply(wallet, transaction, errors); + } + + /** + * Associate this wallet as the sender of a transaction. + */ + public applyTransactionToSender(wallet: Wallet, transaction: ITransactionData): void { + this.getHandler(transaction).applyTransactionToSender(wallet, transaction); + } + + /** + * Add transaction balance to this wallet. + */ + public applyTransactionToRecipient(wallet: Wallet, transaction: ITransactionData): void { + this.getHandler(transaction).applyTransactionToRecipient(wallet, transaction); + } + + /** + * Remove this wallet as the sender of a transaction. + */ + public revertTransactionForSender(wallet: Wallet, transaction: ITransactionData): void { + this.getHandler(transaction).revertTransactionForSender(wallet, transaction); + } + + /** + * Remove transaction balance from this wallet. + */ + public revertTransactionForRecipient(wallet: Wallet, transaction: ITransactionData): void { + this.getHandler(transaction).revertTransactionForRecipient(wallet, transaction); + } + + private getHandler(transaction: ITransactionData): Handler { + return new (this.handlers[transaction.type] as any)(); + } +} + +export const transactionHandler = new TransactionHandler(); diff --git a/packages/crypto/src/handlers/transactions/ipfs.ts b/packages/crypto/src/handlers/transactions/ipfs.ts new file mode 100644 index 0000000000..7b58dd0655 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/ipfs.ts @@ -0,0 +1,25 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class IpfsHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + return super.canApply(wallet, transaction, errors); + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + // + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + // + } +} diff --git a/packages/crypto/src/handlers/transactions/multi-payment.ts b/packages/crypto/src/handlers/transactions/multi-payment.ts new file mode 100644 index 0000000000..caff8e8932 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/multi-payment.ts @@ -0,0 +1,42 @@ +import sumBy from "lodash/sumBy"; +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class MultiPaymentHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + if (!super.canApply(wallet, transaction, errors)) { + return false; + } + + const amount = sumBy(transaction.asset.payments, (payment: any) => payment.amount.toFixed()); + + if ( + wallet.balance + .minus(amount) + .minus(transaction.fee) + .isLessThan(0) + ) { + errors.push("Insufficient balance in the wallet to transfer all payments"); + return false; + } + + return true; + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + // + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + // + } +} diff --git a/packages/crypto/src/handlers/transactions/multi-signature.ts b/packages/crypto/src/handlers/transactions/multi-signature.ts new file mode 100644 index 0000000000..92689863b6 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/multi-signature.ts @@ -0,0 +1,51 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class MultiSignatureHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + if (!super.canApply(wallet, transaction, errors)) { + return false; + } + + if (wallet.multisignature) { + errors.push("Wallet is already a multi-signature wallet"); + return false; + } + + const keysgroup = transaction.asset.multisignature.keysgroup; + + if (keysgroup.length < transaction.asset.multisignature.min) { + errors.push("Specified key count does not meet minimum key count"); + return false; + } + + if (keysgroup.length !== transaction.signatures.length) { + errors.push("Specified key count does not equal signature count"); + return false; + } + + if (!wallet.verifySignatures(transaction, transaction.asset.multisignature)) { + errors.push("Failed to verify multi-signatures"); + return false; + } + + return true; + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + wallet.multisignature = transaction.asset.multisignature; + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + wallet.multisignature = null; + } +} diff --git a/packages/crypto/src/handlers/transactions/second-signature.ts b/packages/crypto/src/handlers/transactions/second-signature.ts new file mode 100644 index 0000000000..ac1b8418fe --- /dev/null +++ b/packages/crypto/src/handlers/transactions/second-signature.ts @@ -0,0 +1,34 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class SecondSignatureHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + if (wallet.secondPublicKey) { + errors.push("Wallet already has a second signature"); + return false; + } + + if (!super.canApply(wallet, transaction, errors)) { + return false; + } + + return true; + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + wallet.secondPublicKey = transaction.asset.signature.publicKey; + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + delete wallet.secondPublicKey; + } +} diff --git a/packages/crypto/src/handlers/transactions/timelock-transfer.ts b/packages/crypto/src/handlers/transactions/timelock-transfer.ts new file mode 100644 index 0000000000..0ac4f4c8a4 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/timelock-transfer.ts @@ -0,0 +1,25 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class TimelockTransferHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + return super.canApply(wallet, transaction, errors); + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + // + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + // + } +} diff --git a/packages/crypto/src/handlers/transactions/transfer.ts b/packages/crypto/src/handlers/transactions/transfer.ts new file mode 100644 index 0000000000..de649e93b3 --- /dev/null +++ b/packages/crypto/src/handlers/transactions/transfer.ts @@ -0,0 +1,25 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class TransferHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + return super.canApply(wallet, transaction, errors); + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + // + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + // + } +} diff --git a/packages/crypto/src/handlers/transactions/vote.ts b/packages/crypto/src/handlers/transactions/vote.ts new file mode 100644 index 0000000000..9660394e3f --- /dev/null +++ b/packages/crypto/src/handlers/transactions/vote.ts @@ -0,0 +1,60 @@ +import { ITransactionData, Wallet } from "../../models"; +import { Handler } from "./handler"; + +export class VoteHandler extends Handler { + /** + * Check if the transaction can be applied to the wallet. + */ + public canApply(wallet: Wallet, transaction: ITransactionData, errors: string[]): boolean { + if (!super.canApply(wallet, transaction, errors)) { + return false; + } + + const vote = transaction.asset.votes[0]; + if (vote.startsWith("-") && (!wallet.vote || wallet.vote !== vote.slice(1))) { + if (!wallet.vote) { + errors.push("Wallet has not voted yet"); + } else { + errors.push("The unvote public key does not match the currently voted one"); + } + + return false; + } + + if (vote.startsWith("+") && wallet.vote) { + errors.push("Wallet has already voted"); + return false; + } + return true; + } + + /** + * Apply the transaction to the wallet. + */ + protected apply(wallet: Wallet, transaction: ITransactionData): void { + const vote = transaction.asset.votes[0]; + + if (vote.startsWith("+")) { + wallet.vote = vote.slice(1); + } + + if (vote.startsWith("-")) { + wallet.vote = null; + } + } + + /** + * Revert the transaction from the wallet. + */ + protected revert(wallet: Wallet, transaction: ITransactionData): void { + const vote = transaction.asset.votes[0]; + + if (vote.startsWith("+")) { + wallet.vote = null; + } + + if (vote.startsWith("-")) { + wallet.vote = vote.slice(1); + } + } +} diff --git a/packages/crypto/src/identities/address.ts b/packages/crypto/src/identities/address.ts new file mode 100644 index 0000000000..ffbbbf4187 --- /dev/null +++ b/packages/crypto/src/identities/address.ts @@ -0,0 +1,47 @@ +import bs58check from "bs58check"; +import { HashAlgorithms } from "../crypto"; +import { PublicKeyError } from "../errors"; +import { configManager } from "../managers"; +import { PublicKey } from "./public-key"; + +export class Address { + public static fromPassphrase(passphrase, networkVersion?: number): string { + return Address.fromPublicKey(PublicKey.fromPassphrase(passphrase), networkVersion); + } + + public static fromPublicKey(publicKey, networkVersion?: number): string { + const pubKeyRegex = /^[0-9A-Fa-f]{66}$/; + if (!pubKeyRegex.test(publicKey)) { + throw new PublicKeyError(publicKey); + } + + if (!networkVersion) { + networkVersion = configManager.get("pubKeyHash"); + } + + const buffer = HashAlgorithms.ripemd160(Buffer.from(publicKey, "hex")); + const payload = Buffer.alloc(21); + + payload.writeUInt8(networkVersion, 0); + buffer.copy(payload, 1); + + return bs58check.encode(payload); + } + + public static fromPrivateKey(privateKey, networkVersion?: number): string { + return Address.fromPublicKey(privateKey.publicKey, networkVersion); + } + + public static validate(address, networkVersion?: number): boolean { + if (!networkVersion) { + networkVersion = configManager.get("pubKeyHash"); + } + + try { + const decode = bs58check.decode(address); + return decode[0] === networkVersion; + } catch (e) { + return false; + } + } +} diff --git a/packages/crypto/src/identities/index.ts b/packages/crypto/src/identities/index.ts new file mode 100644 index 0000000000..177d36e8f8 --- /dev/null +++ b/packages/crypto/src/identities/index.ts @@ -0,0 +1,5 @@ +export { Address } from "./address"; +export { Keys, KeyPair } from "./keys"; +export { PrivateKey } from "./private-key"; +export { PublicKey } from "./public-key"; +export { WIF } from "./wif"; diff --git a/packages/crypto/src/identities/keys.ts b/packages/crypto/src/identities/keys.ts new file mode 100644 index 0000000000..5c685c8d68 --- /dev/null +++ b/packages/crypto/src/identities/keys.ts @@ -0,0 +1,58 @@ +import secp256k1 from "secp256k1"; +import wif from "wif"; + +import { HashAlgorithms } from "../crypto"; +import { NetworkVersionError } from "../errors"; +import { configManager } from "../managers"; +import { INetwork } from "../networks"; + +export interface KeyPair { + publicKey: string; + privateKey: string; + compressed: boolean; +} + +export class Keys { + public static fromPassphrase(passphrase: string, compressed: boolean = true): KeyPair { + const privateKey = HashAlgorithms.sha256(Buffer.from(passphrase, "utf8")); + return Keys.fromPrivateKey(privateKey, compressed); + } + + public static fromPrivateKey(privateKey: Buffer | string, compressed: boolean = true): KeyPair { + privateKey = privateKey instanceof Buffer ? privateKey : Buffer.from(privateKey, "hex"); + + const publicKey = secp256k1.publicKeyCreate(privateKey, compressed); + const keyPair = { + publicKey: publicKey.toString("hex"), + privateKey: privateKey.toString("hex"), + compressed, + }; + + return keyPair; + } + + public static fromWIF(wifKey: string, network?: { wif: number }): KeyPair { + if (!network) { + network = configManager.all(); + } + + // @ts-ignore + const decoded = wif.decode(wifKey); + const version = decoded.version; + + if (version !== network.wif) { + throw new NetworkVersionError(network.wif, version); + } + + const privateKey = decoded.privateKey; + const publicKey = secp256k1.publicKeyCreate(privateKey, decoded.compressed); + + const keyPair = { + publicKey: publicKey.toString("hex"), + privateKey: privateKey.toString("hex"), + compressed: decoded.compressed, + }; + + return keyPair; + } +} diff --git a/packages/crypto/src/identities/private-key.ts b/packages/crypto/src/identities/private-key.ts new file mode 100644 index 0000000000..590d846002 --- /dev/null +++ b/packages/crypto/src/identities/private-key.ts @@ -0,0 +1,12 @@ +import { INetwork } from "../networks"; +import { Keys } from "./keys"; + +export class PrivateKey { + public static fromPassphrase(passphrase: string): string { + return Keys.fromPassphrase(passphrase).privateKey; + } + + public static fromWIF(wif: string, network?: INetwork): string { + return Keys.fromWIF(wif, network).privateKey; + } +} diff --git a/packages/crypto/src/identities/public-key.ts b/packages/crypto/src/identities/public-key.ts new file mode 100644 index 0000000000..883e258e1e --- /dev/null +++ b/packages/crypto/src/identities/public-key.ts @@ -0,0 +1,26 @@ +import { configManager } from "../managers"; +import { INetwork } from "../networks"; +import { Address } from "./address"; +import { Keys } from "./keys"; + +export class PublicKey { + public static fromPassphrase(passphrase: string): string { + return Keys.fromPassphrase(passphrase).publicKey; + } + + public static fromWIF(wif: string, network?: INetwork): string { + return Keys.fromWIF(wif, network).publicKey; + } + + public static validate(publicKey: string, networkVersion?: number): boolean { + if (!networkVersion) { + networkVersion = configManager.get("pubKeyHash"); + } + + try { + return Address.fromPublicKey(publicKey, networkVersion).length === 34; + } catch (e) { + return false; + } + } +} diff --git a/packages/crypto/src/identities/wif.ts b/packages/crypto/src/identities/wif.ts new file mode 100644 index 0000000000..2097d09a9c --- /dev/null +++ b/packages/crypto/src/identities/wif.ts @@ -0,0 +1,24 @@ +import wif from "wif"; +import { configManager } from "../managers"; +import { INetwork } from "../networks"; +import { KeyPair, Keys } from "./keys"; + +export class WIF { + public static fromPassphrase(passphrase: string, network?: INetwork): string { + const keys = Keys.fromPassphrase(passphrase); + + if (!network) { + network = configManager.all(); + } + + return wif.encode(network.wif, Buffer.from(keys.privateKey, "hex"), keys.compressed); + } + + public static fromKeys(keys: KeyPair, network?: { wif: number }): string { + if (!network) { + network = configManager.all(); + } + + return wif.encode(network.wif, Buffer.from(keys.privateKey, "hex"), keys.compressed); + } +} diff --git a/packages/crypto/src/index.ts b/packages/crypto/src/index.ts new file mode 100644 index 0000000000..38790911c1 --- /dev/null +++ b/packages/crypto/src/index.ts @@ -0,0 +1,13 @@ +export * from "./builder"; + +import * as constants from "./constants"; +import * as models from "./models"; + +export * from "./identities"; +export * from "./managers"; +export * from "./utils"; +export * from "./validation"; +export * from "./crypto"; +export * from "./client"; + +export { models, constants }; diff --git a/packages/crypto/src/managers/config.ts b/packages/crypto/src/managers/config.ts new file mode 100644 index 0000000000..5e6592b186 --- /dev/null +++ b/packages/crypto/src/managers/config.ts @@ -0,0 +1,155 @@ +import deepmerge from "deepmerge"; +import camelCase from "lodash/camelCase"; +import get from "lodash/get"; +import set from "lodash/set"; +import { feeManager } from "./fee"; + +import { TransactionTypes } from "../constants"; +import * as networks from "../networks"; + +interface IMilestone { + index: number; + data: { [key: string]: any }; +} + +export type NetworkName = keyof typeof networks; + +export class ConfigManager { + public config: any; + public milestone: IMilestone; + public milestones: any; + private height: number; + + /** + * @constructor + */ + constructor() { + this.setConfig(networks.devnet); + } + + /** + * Set config data. + * @param {Object} config + */ + public setConfig(config: any) { + this.config = {}; + + // Map the config.network values to the root + for (const [key, value] of Object.entries(config.network)) { + this.config[key] = value; + } + + this.config.exceptions = config.exceptions; + this.config.milestones = config.milestones; + this.config.genesisBlock = config.genesisBlock; + + this.buildConstants(); + this.buildFees(); + } + + /** + * Set the configuration based on a preset. + */ + public setFromPreset(network: NetworkName) { + this.setConfig(this.getPreset(network)); + } + + /** + * Get the configuration for a preset. + */ + public getPreset(network: NetworkName) { + return networks[network.toLowerCase()]; + } + + /** + * Get all config data. + */ + public all() { + return this.config; + } + + /** + * Set individual config value. + */ + public set(key: string, value: any) { + set(this.config, key, value); + } + + /** + * Get specific config value. + */ + public get(key): T { + return get(this.config, key) as T; + } + + /** + * Set config manager height. + */ + public setHeight(value: number): void { + this.height = value; + } + + /** + * Get config manager height. + */ + public getHeight(): number { + return this.height; + } + + /** + * Get all config constants based on height. + */ + public getMilestone(height?: number): { [key: string]: any } { + if (!height && this.height) { + height = this.height; + } + + if (!height) { + height = 1; + } + + while ( + this.milestone.index < this.milestones.length - 1 && + height >= this.milestones[this.milestone.index + 1].height + ) { + this.milestone.index++; + this.milestone.data = this.milestones[this.milestone.index]; + } + + while (height < this.milestones[this.milestone.index].height) { + this.milestone.index--; + this.milestone.data = this.milestones[this.milestone.index]; + } + + return this.milestone.data; + } + + /** + * Build constant data based on active heights. + */ + private buildConstants(): void { + this.milestones = this.config.milestones.sort((a, b) => a.height - b.height); + this.milestone = { + index: 0, + data: this.milestones[0], + }; + + let lastMerged = 0; + + while (lastMerged < this.milestones.length - 1) { + this.milestones[lastMerged + 1] = deepmerge(this.milestones[lastMerged], this.milestones[lastMerged + 1]); + lastMerged++; + } + } + + /** + * Build fees from config constants. + */ + private buildFees(): void { + for (const type of Object.keys(TransactionTypes)) { + feeManager.set(TransactionTypes[type], this.getMilestone().fees.staticFees[camelCase(type)]); + } + } +} + +export const configManager = new ConfigManager(); diff --git a/packages/crypto/src/managers/fee.ts b/packages/crypto/src/managers/fee.ts new file mode 100644 index 0000000000..e836b2038b --- /dev/null +++ b/packages/crypto/src/managers/fee.ts @@ -0,0 +1,33 @@ +import { TransactionTypes } from "../constants"; +import { ITransactionData } from "../models"; + +export class FeeManager { + public fees: { [key in TransactionTypes]?: number } = {}; + + /** + * Set fee value based on type. + */ + public set(type: TransactionTypes, value: number) { + this.fees[type] = value; + } + + /** + * Get fee value based on type. + */ + public get(type: TransactionTypes): number { + return this.fees[type]; + } + + /** + * Get fee value based on type. + */ + public getForTransaction(transaction: ITransactionData): number { + if (transaction.type === TransactionTypes.MultiSignature) { + return this.fees[transaction.type] * (transaction.asset.multisignature.keysgroup.length + 1); + } + + return this.fees[transaction.type]; + } +} + +export const feeManager = new FeeManager(); diff --git a/packages/crypto/src/managers/index.ts b/packages/crypto/src/managers/index.ts new file mode 100644 index 0000000000..7345bebea4 --- /dev/null +++ b/packages/crypto/src/managers/index.ts @@ -0,0 +1,5 @@ +import { configManager } from "./config"; +import { feeManager } from "./fee"; +import { NetworkManager } from "./network"; + +export { configManager, feeManager, NetworkManager }; diff --git a/packages/crypto/src/managers/network.ts b/packages/crypto/src/managers/network.ts new file mode 100644 index 0000000000..4ae8d54b56 --- /dev/null +++ b/packages/crypto/src/managers/network.ts @@ -0,0 +1,19 @@ +import get from "lodash/get"; +import * as networks from "../networks"; +import { NetworkName } from "./config"; + +export class NetworkManager { + /** + * Get all network types. + */ + public static getAll(): any { + return networks; + } + + /** + * Find network by name. + */ + public static findByName(name: NetworkName): any { + return get(networks, name.toLowerCase()); + } +} diff --git a/packages/crypto/src/models/block.ts b/packages/crypto/src/models/block.ts new file mode 100644 index 0000000000..ad79ee636f --- /dev/null +++ b/packages/crypto/src/models/block.ts @@ -0,0 +1,349 @@ +import { createHash } from "crypto"; +import pluralize from "pluralize"; +import { crypto, slots } from "../crypto"; +import { BlockDeserializer } from "../deserializers"; +import { configManager } from "../managers/config"; +import { BlockSerializer } from "../serializers"; +import { Bignum } from "../utils"; +import { ITransactionData, Transaction } from "./transaction"; + +export interface BlockVerification { + verified: boolean; + errors: string[]; +} + +export interface IBlock { + data: IBlockData; +} + +export interface IBlockData { + id?: string; + idHex?: string; + + timestamp: number; + version: number; + height: number; + previousBlockHex?: string; + previousBlock: string; + numberOfTransactions: number; + totalAmount: Bignum | number | string; + totalFee: Bignum | number | string; + reward: Bignum | number | string; + payloadLength: number; + payloadHash: string; + generatorPublicKey: string; + + blockSignature?: string; + serialized?: string; + transactions?: ITransactionData[]; +} + +/** + * TODO copy some parts to ArkDocs + * @classdesc This model holds the block data, its verification and serialization + * + * A Block model stores on the db: + * - id + * - version (version of the block: could be used for changing how they are forged) + * - timestamp (related to the genesis block) + * - previousBlock (id of the previous block) + * - height + * - numberOfTransactions + * - totalAmount (in arktoshi) + * - totalFee (in arktoshi) + * - reward (in arktoshi) + * - payloadHash (hash of the transactions) + * - payloadLength (total length in bytes of the IDs of the transactions) + * - generatorPublicKey (public key of the delegate that forged this block) + * - blockSignature + * + * The `transactions` are stored too, but in a different table. + * + * These data is exposed through the `data` attributed as a plain object and + * serialized through the `serialized` attribute. + * + * In the future the IDs could be changed to use the hexadecimal version of them, + * which would be more efficient for performance, disk usage and bandwidth reasons. + * That is why there are some attributes, such as `idHex` and `previousBlockHex`. + */ + +export class Block implements IBlock { + /** + * Create block from data. + */ + public static create(data, keys): Block { + data.generatorPublicKey = keys.publicKey; + + const payloadHash: Buffer = Block.serialize(data, false); + const hash = createHash("sha256") + .update(payloadHash) + .digest(); + + data.blockSignature = crypto.signHash(hash, keys); + data.id = Block.getId(data); + + return new Block(data); + } + + /** + * Deserialize block from hex string. + */ + public static deserialize(hexString, headerOnly = false): IBlockData { + return BlockDeserializer.deserialize(hexString, headerOnly); + } + + /** + * Serialize the given block including transactions. + */ + public static serializeFull(block: IBlockData) { + return BlockSerializer.serializeFull(block); + } + + /** + * Serialize the given block without transactions. + */ + public static serialize(block: IBlockData, includeSignature: boolean = true) { + return BlockSerializer.serialize(block, includeSignature); + } + + public static getIdHex(data): string { + const payloadHash: any = Block.serialize(data); + const hash = createHash("sha256") + .update(payloadHash) + .digest(); + const temp = Buffer.alloc(8); + + for (let i = 0; i < 8; i++) { + temp[i] = hash[7 - i]; + } + return temp.toString("hex"); + } + + public static toBytesHex(data): string { + const temp = data ? new Bignum(data).toString(16) : ""; + return "0".repeat(16 - temp.length) + temp; + } + + /** + * Get block id from already serialized buffer + */ + public static getIdFromSerialized(serializedBuffer: Buffer): string { + const hash = createHash("sha256") + .update(serializedBuffer) + .digest(); + const temp = Buffer.alloc(8); + + for (let i = 0; i < 8; i++) { + temp[i] = hash[7 - i]; + } + return new Bignum(temp.toString("hex"), 16).toFixed(); + } + + public static getId(data): string { + const idHex = Block.getIdHex(data); + return new Bignum(idHex, 16).toFixed(); + } + + public serialized: string; + public data: IBlockData; + public transactions: Transaction[]; + public verification: BlockVerification; + + constructor(data: IBlockData | string) { + if (typeof data === "string") { + data = Block.deserialize(data); + } + + this.serialized = Block.serializeFull(data).toString("hex"); + this.data = Block.deserialize(this.serialized); + + // TODO genesis block calculated id is wrong for some reason + if (data.height === 1) { + this.applyGenesisBlockFix(data); + } + + // fix on real timestamp, this is overloading transaction + // timestamp with block timestamp for storage only + // also add sequence to keep database sequence + const { transactions } = this.data; + this.transactions = transactions + ? transactions.map((transaction, index) => { + transaction.blockId = this.data.id; + transaction.timestamp = this.data.timestamp; + transaction.sequence = index; + return transaction as Transaction; + }) + : []; + + delete this.data.transactions; + + this.verification = this.verify(); + + // order of transactions messed up in mainnet V1 + // TODO: move this to network constants exception using block ids + if ( + this.transactions && + this.data.numberOfTransactions === 2 && + (this.data.height === 3084276 || this.data.height === 34420) + ) { + const temp = this.transactions[0]; + this.transactions[0] = this.transactions[1]; + this.transactions[1] = temp; + } + } + + /** + * Return block as string. + */ + public toString(): string { + return `${this.data.id}, height: ${this.data.height.toLocaleString()}, ${pluralize( + "transaction", + this.data.numberOfTransactions, + true, + )}, verified: ${this.verification.verified}, errors: ${this.verification.errors}`; + } + + /** + * Get header from block. + */ + public getHeader(): IBlockData { + const header = Object.assign({}, this.data); + delete header.transactions; + return header; + } + + /** + * Verify signature associated with this block. + */ + public verifySignature(): boolean { + const bytes: any = Block.serialize(this.data, false); + const hash = createHash("sha256") + .update(bytes) + .digest(); + + return crypto.verifyHash(hash, this.data.blockSignature, this.data.generatorPublicKey); + } + + public toJson(): any { + const blockData = Object.assign({}, this.data) as IBlockData; + ["reward", "totalAmount", "totalFee"].forEach((key: string) => { + blockData[key] = +(blockData[key] as Bignum).toFixed(); + }); + + blockData.transactions = this.transactions.map(transaction => transaction.toJson()); + return blockData; + } + + /** + * Verify this block. + */ + private verify(): BlockVerification { + const block = this.data; + const result: BlockVerification = { + verified: false, + errors: [], + }; + + try { + const constants = configManager.getMilestone(block.height); + + if (block.height !== 1) { + if (!block.previousBlock) { + result.errors.push("Invalid previous block"); + } + } + + if (!(block.reward as Bignum).isEqualTo(constants.reward)) { + result.errors.push(["Invalid block reward:", block.reward, "expected:", constants.reward].join(" ")); + } + + const valid = this.verifySignature(); + + if (!valid) { + result.errors.push("Failed to verify block signature"); + } + + if (block.version !== constants.block.version) { + result.errors.push("Invalid block version"); + } + + if (slots.getSlotNumber(block.timestamp) > slots.getSlotNumber()) { + result.errors.push("Invalid block timestamp"); + } + + // Disabling to allow orphanedBlocks? + // if(previousBlock){ + // const lastBlockSlotNumber = slots.getSlotNumber(previousBlock.timestamp) + // if(blockSlotNumber < lastBlockSlotNumber) { + // result.errors.push('block timestamp is smaller than previous block timestamp') + // } + // } + + let size = 0; + const payloadHash = createHash("sha256"); + const invalidTransactions = this.transactions.filter(tx => !tx.verified); + if (invalidTransactions.length > 0) { + result.errors.push("One or more transactions are not verified:"); + invalidTransactions.forEach(tx => result.errors.push(`=> ${tx.serialized}`)); + } + + if (this.transactions.length !== block.numberOfTransactions) { + result.errors.push("Invalid number of transactions"); + } + + if (this.transactions.length > constants.block.maxTransactions) { + if (block.height > 1) { + result.errors.push("Transactions length is too high"); + } + } + + // Checking if transactions of the block adds up to block values. + const appliedTransactions = {}; + let totalAmount = Bignum.ZERO; + let totalFee = Bignum.ZERO; + this.transactions.forEach(transaction => { + const bytes = Buffer.from(transaction.data.id, "hex"); + + if (appliedTransactions[transaction.data.id]) { + result.errors.push(`Encountered duplicate transaction: ${transaction.data.id}`); + } + + appliedTransactions[transaction.data.id] = transaction.data; + + totalAmount = totalAmount.plus(transaction.data.amount); + totalFee = totalFee.plus(transaction.data.fee); + size += bytes.length; + + payloadHash.update(bytes); + }); + + if (!totalAmount.isEqualTo(block.totalAmount)) { + result.errors.push("Invalid total amount"); + } + + if (!totalFee.isEqualTo(block.totalFee)) { + result.errors.push("Invalid total fee"); + } + + if (size > constants.block.maxPayload) { + result.errors.push("Payload is too large"); + } + + if (payloadHash.digest().toString("hex") !== block.payloadHash) { + result.errors.push("Invalid payload hash"); + } + } catch (error) { + result.errors.push(error); + } + + result.verified = result.errors.length === 0; + + return result; + } + + private applyGenesisBlockFix(data: IBlockData): void { + this.data.id = data.id; + this.data.idHex = Block.toBytesHex(this.data.id); + delete this.data.previousBlock; + } +} diff --git a/packages/crypto/src/models/delegate.ts b/packages/crypto/src/models/delegate.ts new file mode 100644 index 0000000000..51bc696ceb --- /dev/null +++ b/packages/crypto/src/models/delegate.ts @@ -0,0 +1,168 @@ +import { createHash } from "crypto"; +import forge from "node-forge"; +import { authenticator } from "otplib"; +import wif from "wif"; +import * as bip38 from "../crypto/bip38"; +import { Bignum } from "../utils"; + +import { crypto } from "../crypto/crypto"; +import { KeyPair } from "../identities"; +import { INetwork } from "../networks"; +import { sortTransactions } from "../utils"; +import { Block, IBlockData } from "./block"; +import { Transaction } from "./transaction"; + +export class Delegate { + /** + * BIP38 encrypt passphrase. + */ + public static encryptPassphrase(passphrase: string, network: INetwork, password: string): string { + const keys = crypto.getKeys(passphrase); + // @ts-ignore + const decoded = wif.decode(crypto.keysToWIF(keys, network)); + + return bip38.encrypt(decoded.privateKey, decoded.compressed, password); + } + + /** + * BIP38 decrypt passphrase keys. + */ + public static decryptPassphrase(passphrase: string, network: INetwork, password?: string): KeyPair { + const decryptedWif = bip38.decrypt(passphrase, password); + const wifKey = wif.encode(network.wif, decryptedWif.privateKey, decryptedWif.compressed); + + return crypto.getKeysFromWIF(wifKey, network); + } + + public network: INetwork; + public keySize: number; + public iterations: number; + public keys: KeyPair; + public publicKey: string; + public address: string; + public otpSecret: string; + public bip38: boolean = false; + public otp: string; + public encryptedKeys: string; + + constructor(passphrase: string, network: INetwork, password?: string) { + this.network = network; + this.keySize = 32; // AES-256 + this.iterations = 5000; + + if (bip38.verify(passphrase)) { + try { + this.keys = Delegate.decryptPassphrase(passphrase, network, password); + this.publicKey = this.keys.publicKey; + this.address = crypto.getAddress(this.keys.publicKey, network.pubKeyHash); + this.otpSecret = authenticator.generateSecret(); + this.bip38 = true; + this.encryptKeysWithOtp(); + } catch (error) { + this.publicKey = null; + this.keys = null; + this.address = null; + } + } else { + this.keys = crypto.getKeys(passphrase); + this.publicKey = this.keys.publicKey; + this.address = crypto.getAddress(this.publicKey, network.pubKeyHash); + } + } + + /** + * Encrypt keys with one time password - used to store encrypted in memory. + */ + public encryptKeysWithOtp(): void { + this.otp = authenticator.generate(this.otpSecret); + const wifKey = crypto.keysToWIF(this.keys, this.network); + this.encryptedKeys = this.encryptData(wifKey, this.otp); + this.keys = null; + } + + /** + * Decrypt keys with one time password. + */ + public decryptKeysWithOtp(): void { + const wifKey = this.decryptData(this.encryptedKeys, this.otp); + this.keys = crypto.getKeysFromWIF(wifKey, this.network); + this.otp = null; + this.encryptedKeys = null; + } + + /** + * Forge block - we consider transactions are signed, verified and unique. + */ + public forge(transactions: Transaction[], options: any): Block | null { + if (!options.version && (this.encryptedKeys || !this.bip38)) { + const transactionData = { + amount: Bignum.ZERO, + fee: Bignum.ZERO, + sha256: createHash("sha256"), + }; + + const sortedTransactions = sortTransactions(transactions); + sortedTransactions.forEach(transaction => { + transactionData.amount = transactionData.amount.plus(transaction.amount); + transactionData.fee = transactionData.fee.plus(transaction.fee); + transactionData.sha256.update(Buffer.from(transaction.id, "hex")); + }); + + const data: IBlockData = { + version: 0, + generatorPublicKey: this.publicKey, + timestamp: options.timestamp, + previousBlock: options.previousBlock.id, + previousBlockHex: options.previousBlock.idHex, + height: options.previousBlock.height + 1, + numberOfTransactions: sortedTransactions.length, + totalAmount: transactionData.amount, + totalFee: transactionData.fee, + reward: options.reward, + payloadLength: 32 * sortedTransactions.length, + payloadHash: transactionData.sha256.digest().toString("hex"), + transactions: sortedTransactions, + }; + + if (this.bip38) { + this.decryptKeysWithOtp(); + } + + const block = Block.create(data, this.keys); + + if (this.bip38) { + this.encryptKeysWithOtp(); + } + + return block; + } + + return null; + } + + /** + * Perform OTP encryption. + */ + private encryptData(content: string, password: string): string { + const derivedKey = forge.pkcs5.pbkdf2(password, this.otpSecret, this.iterations, this.keySize); + const cipher = forge.cipher.createCipher("AES-CBC", derivedKey); + cipher.start({ iv: forge.util.decode64(this.otp) }); + cipher.update(forge.util.createBuffer(content)); + cipher.finish(); + + return forge.util.encode64(cipher.output.getBytes()); + } + + /** + * Perform OTP decryption. + */ + private decryptData(cipherText: string, password: string): string { + const derivedKey = forge.pkcs5.pbkdf2(password, this.otpSecret, this.iterations, this.keySize); + const decipher = forge.cipher.createDecipher("AES-CBC", derivedKey); + decipher.start({ iv: forge.util.decode64(this.otp) }); + decipher.update(forge.util.createBuffer(forge.util.decode64(cipherText))); + decipher.finish(); + + return decipher.output.toString(); + } +} diff --git a/packages/crypto/src/models/index.ts b/packages/crypto/src/models/index.ts new file mode 100644 index 0000000000..0609cc9af5 --- /dev/null +++ b/packages/crypto/src/models/index.ts @@ -0,0 +1,4 @@ +export * from "./block"; +export * from "./transaction"; +export * from "./delegate"; +export * from "./wallet"; diff --git a/packages/crypto/src/models/transaction.ts b/packages/crypto/src/models/transaction.ts new file mode 100644 index 0000000000..2f36ca9475 --- /dev/null +++ b/packages/crypto/src/models/transaction.ts @@ -0,0 +1,177 @@ +import { TransactionTypes } from "../constants"; +import { crypto } from "../crypto"; +import { TransactionDeserializer } from "../deserializers"; +import { TransactionSerializer } from "../serializers"; +import { Bignum, isException } from "../utils"; + +export interface ITransactionAsset { + signature?: { + publicKey: string; + }; + delegate?: { + username: string; + publicKey?: string; + }; + votes?: string[]; + multisignature?: IMultiSignatureAsset; + ipfs?: { + dag: string; + }; + payments?: any; +} + +export interface IMultiSignatureAsset { + min: number; + keysgroup: string[]; + lifetime: number; +} + +export interface ITransactionData { + version?: number; + network?: number; + + type: TransactionTypes; + timestamp: number; + senderPublicKey: string; + + fee: Bignum | number | string; + amount: Bignum | number | string; + + expiration?: number; + recipientId?: string; + + asset?: ITransactionAsset; + vendorField?: string; + vendorFieldHex?: string; + + id?: string; + signature?: string; + secondSignature?: string; + signSignature?: string; + signatures?: string[]; + + blockId?: string; + sequence?: number; + + timelock?: any; + timelockType?: number; + + ipfsHash?: string; + payments?: { [key: string]: any }; +} + +/** + * TODO copy some parts to ArkDocs + * @classdesc This model holds the transaction data and its serialization + * + * A Transaction stores on the db: + * - id + * - version (version of the transaction generation process, ie: serialization) + * - blockId (id of the block that contains the transaction) + * - timestamp (related to the genesis block) + * - senderPublicKey (public key of the sender) + * - recipientId (address of the recipient) + * - type + * - vendorFieldHex (hexadecimal version of the vendorField) + * - amount (in arktoshi) + * - fee (in arktoshi) + * - serialized + * + * Apart, the Model includes other fields: + * - signature + * - secondSignature + * - vendorField + * + * - assets + * - network + */ + +export class Transaction implements ITransactionData { + public static serialize(transaction: ITransactionData): Buffer { + return TransactionSerializer.serialize(transaction); + } + + public static deserialize(hexString: string): ITransactionData { + return TransactionDeserializer.deserialize(hexString); + } + + public static canHaveVendorField(type: number): boolean { + return [TransactionTypes.Transfer, TransactionTypes.TimelockTransfer].includes(type); + } + + public data: ITransactionData; + public serialized: string; + public verified: boolean; + + // TODO: remove all duplicated data properties from Transaction class + public id: string; + public version: number; + public network: number; + public type: TransactionTypes; + public timestamp: number; + public senderPublicKey: string; + public fee: Bignum; + public amount: Bignum; + public expiration?: number; + public recipientId?: string; + public asset?: any; + public vendorField?: string; + public vendorFieldHex?: string; + public signature: string; + public secondSignature?: string; + public signSignature?: string; + public signatures?: string[]; + public blockId?: string; + public sequence?: number; + public timelock?: any; + public timelockType?: number; + + constructor(data: string | ITransactionData) { + if (typeof data === "string") { + this.serialized = data; + } else { + this.serialized = Transaction.serialize(data).toString("hex"); + } + + this.data = Transaction.deserialize(this.serialized); + this.verified = (this.data.type < 4 && crypto.verify(this.data)) || isException(this.data); + + // TODO: remove this + [ + "id", + "sequence", + "version", + "timestamp", + "senderPublicKey", + "recipientId", + "type", + "vendorField", + "vendorFieldHex", + "amount", + "fee", + "blockId", + "signature", + "signatures", + "secondSignature", + "signSignature", + "asset", + "expiration", + "timelock", + "timelockType", + ].forEach(key => { + this[key] = this.data[key]; + }, this); + } + + public verify(): boolean { + return this.verified; + } + + public toJson(): any { + const data = Object.assign({}, this.data); + data.amount = +(data.amount as Bignum).toFixed(); + data.fee = +(data.fee as Bignum).toFixed(); + + return data; + } +} diff --git a/packages/crypto/src/models/wallet.ts b/packages/crypto/src/models/wallet.ts new file mode 100644 index 0000000000..e29beb795e --- /dev/null +++ b/packages/crypto/src/models/wallet.ts @@ -0,0 +1,288 @@ +import { TransactionTypes } from "../constants"; +import { crypto } from "../crypto/crypto"; +import { transactionHandler } from "../handlers/transactions"; +import { Bignum, formatArktoshi } from "../utils"; +import { IBlockData } from "./block"; +import { IMultiSignatureAsset, ITransactionData } from "./transaction"; + +/** + * TODO copy some parts to ArkDocs + * @classdesc This class holds the wallet data, verifies it and applies the + * transaction and blocks to it + * + * Wallet attributes that are stored on the db: + * - address + * - publicKey + * - secondPublicKey + * - balance + * - vote + * - username (name, if the wallet is a delegate) + * - voteBalance (number of votes if the wallet is a delegate) + * - producedBlocks + * - missedBlocks + * + * This other attributes are not stored on the db: + * - multisignature + * - lastBlock (last block applied or `null``) + * - dirty + */ +export class Wallet { + public address: string; + public publicKey: string | null; + public secondPublicKey: string | null; + public balance: Bignum; + public vote: string; + public voted: boolean; + public username: string | null; + public lastBlock: any; + public voteBalance: Bignum; + public multisignature?: IMultiSignatureAsset; + public dirty: boolean; + public producedBlocks: number; + public missedBlocks: number; + public forgedFees: Bignum; + public forgedRewards: Bignum; + + constructor(address: string) { + this.address = address; + this.publicKey = null; + this.secondPublicKey = null; + this.balance = Bignum.ZERO; + this.vote = null; + this.voted = false; + this.username = null; + this.lastBlock = null; + this.voteBalance = Bignum.ZERO; + this.multisignature = null; + this.dirty = true; + this.producedBlocks = 0; + this.missedBlocks = 0; + this.forgedFees = Bignum.ZERO; + this.forgedRewards = Bignum.ZERO; + } + + /** + * Check if can apply a transaction to the wallet. + */ + public canApply(transaction: ITransactionData, errors: any[]): boolean { + return transactionHandler.canApply(this, transaction, errors); + } + + /** + * Associate this wallet as the sender of a transaction. + */ + public applyTransactionToSender(transaction: ITransactionData): void { + return transactionHandler.applyTransactionToSender(this, transaction); + } + + /** + * Remove this wallet as the sender of a transaction. + */ + public revertTransactionForSender(transaction: ITransactionData): void { + return transactionHandler.revertTransactionForSender(this, transaction); + } + + /** + * Add transaction balance to this wallet. + */ + public applyTransactionToRecipient(transaction: ITransactionData): void { + return transactionHandler.applyTransactionToRecipient(this, transaction); + } + + /** + * Remove transaction balance from this wallet. + */ + public revertTransactionForRecipient(transaction: ITransactionData): void { + return transactionHandler.revertTransactionForRecipient(this, transaction); + } + + /** + * Add block data to this wallet. + */ + public applyBlock(block: IBlockData): boolean { + this.dirty = true; + + if ( + block.generatorPublicKey === this.publicKey || + crypto.getAddress(block.generatorPublicKey) === this.address + ) { + this.balance = this.balance.plus(block.reward).plus(block.totalFee); + + // update stats + this.producedBlocks++; + this.forgedFees = this.forgedFees.plus(block.totalFee); + this.forgedRewards = this.forgedRewards.plus(block.reward); + this.lastBlock = block; + return true; + } + + return false; + } + + /** + * Remove block data from this wallet. + */ + public revertBlock(block: IBlockData): boolean { + if ( + block.generatorPublicKey === this.publicKey || + crypto.getAddress(block.generatorPublicKey) === this.address + ) { + this.dirty = true; + this.balance = this.balance.minus(block.reward).minus(block.totalFee); + + // update stats + this.forgedFees = this.forgedFees.minus(block.totalFee); + this.forgedRewards = this.forgedRewards.minus(block.reward); + this.producedBlocks--; + + // TODO: get it back from database? + this.lastBlock = null; + return true; + } + + return false; + } + + /** + * Verify multi-signatures for the wallet. + */ + public verifySignatures(transaction: ITransactionData, multisignature: IMultiSignatureAsset): boolean { + if (!transaction.signatures || transaction.signatures.length < multisignature.min) { + return false; + } + + const keysgroup = multisignature.keysgroup.map(publicKey => + publicKey.startsWith("+") ? publicKey.slice(1) : publicKey, + ); + const signatures = Object.values(transaction.signatures); + + let valid = 0; + for (const publicKey of keysgroup) { + const signature = this.verifyTransactionSignatures(transaction, signatures, publicKey); + if (signature) { + signatures.splice(signatures.indexOf(signature), 1); + valid++; + if (valid === multisignature.min) { + return true; + } + } + } + + return false; + } + + /** + * Audit the specified transaction. + */ + public auditApply(transaction: ITransactionData): any[] { + const audit = []; + + if (this.multisignature) { + audit.push({ + Mutisignature: this.verifySignatures(transaction, this.multisignature), + }); + } else { + audit.push({ + "Remaining amount": +this.balance + .minus(transaction.amount) + .minus(transaction.fee) + .toFixed(), + }); + audit.push({ "Signature validation": crypto.verify(transaction) }); + // TODO: this can blow up if 2nd phrase and other transactions are in the wrong order + if (this.secondPublicKey) { + audit.push({ + "Second Signature Verification": crypto.verifySecondSignature(transaction, this.secondPublicKey), + }); + } + } + + if (transaction.type === TransactionTypes.Transfer) { + audit.push({ Transfer: true }); + } + + if (transaction.type === TransactionTypes.SecondSignature) { + audit.push({ "Second public key": this.secondPublicKey }); + } + + if (transaction.type === TransactionTypes.DelegateRegistration) { + const username = transaction.asset.delegate.username; + audit.push({ "Current username": this.username }); + audit.push({ "New username": username }); + } + + if (transaction.type === TransactionTypes.Vote) { + audit.push({ "Current vote": this.vote }); + audit.push({ "New vote": transaction.asset.votes[0] }); + } + + if (transaction.type === TransactionTypes.MultiSignature) { + const keysgroup = transaction.asset.multisignature.keysgroup; + audit.push({ "Multisignature not yet registered": !this.multisignature }); + audit.push({ + "Multisignature enough keys": keysgroup.length >= transaction.asset.multisignature.min, + }); + audit.push({ + "Multisignature all keys signed": keysgroup.length === transaction.signatures.length, + }); + audit.push({ + "Multisignature verification": this.verifySignatures(transaction, transaction.asset.multisignature), + }); + } + + if (transaction.type === TransactionTypes.Ipfs) { + audit.push({ IPFS: true }); + } + + if (transaction.type === TransactionTypes.TimelockTransfer) { + audit.push({ Timelock: true }); + } + + if (transaction.type === TransactionTypes.MultiPayment) { + const amount = transaction.asset.payments.reduce((a, p) => a.plus(p.amount), Bignum.ZERO); + audit.push({ "Multipayment remaining amount": amount }); + } + + if (transaction.type === TransactionTypes.DelegateResignation) { + audit.push({ "Resignate Delegate": this.username }); + } + + if (!Object.values(TransactionTypes).includes(transaction.type)) { + audit.push({ "Unknown Type": true }); + } + + return audit; + } + + /** + * Get formatted wallet address and balance as string. + */ + public toString(): string { + return `${this.address} (${formatArktoshi(this.balance)})`; + } + + /** + * Goes through signatures to check if public key matches. Can also remove valid signatures. + */ + private verifyTransactionSignatures( + transaction: ITransactionData, + signatures: string[], + publicKey: string, + ): string | null { + for (const signature of signatures) { + if (this.verify(transaction, signature, publicKey)) { + return signature; + } + } + + return null; + } + + /** + * Verify the wallet. + */ + private verify(transaction: ITransactionData, signature: string, publicKey: string): boolean { + const hash = crypto.getHash(transaction, true, true); + return crypto.verifyHash(hash, signature, publicKey); + } +} diff --git a/packages/crypto/src/networks/devnet/exceptions.json b/packages/crypto/src/networks/devnet/exceptions.json new file mode 100644 index 0000000000..3cce29d8a9 --- /dev/null +++ b/packages/crypto/src/networks/devnet/exceptions.json @@ -0,0 +1,350 @@ +{ + "blocks": [ + "15895730198424359628", + "14746174532446639362", + "15249141324902969334", + "12360802297474246584", + "2565729258675312304", + "12614646598841308905", + "8274406339991077743", + "1661383348822169561", + "15467742607784975524", + "3665174254391236833", + "18033869253067308940", + "9121030900295704150", + "4296553458016414976", + "6837659293375391985", + "16540521480028827748", + "1485997193168364918", + "14159698257459587584", + "7561147498738550191", + "6247200360319694668", + "7363268091423233950", + "8738693892321921533", + "9014317427571908796", + "15519361274991733193", + "14013227271822852495", + "12603272471546364995", + "1944108005996955253", + "8469356042757089608", + "3433946900869474802", + "11257633501887013743", + "2997965849869498353", + "9196430932294555781", + "6730395143580220680", + "5806654366498055250", + "13290912469992409149", + "9502002558776276513", + "330791153715252718", + "12084096509112875921", + "7079194814443264009", + "15946707936026547597", + "1641736062116508620", + "5245034769798442586", + "4073147595542846301", + "11129434526540201266", + "15355810214343508168", + "834201289153220685", + "4785149476172130294", + "9808224912335721998", + "11229968119222422821", + "6766557974469507237", + "2066948671330348076", + "13308773643111727094", + "15649739201370841265", + "17287484123727410951", + "1739406121453748889", + "16969775483726255451", + "5174570296595098048", + "10957882104586895269", + "16222316251056394079", + "11019993339496601918", + "7648775833276915174", + "5947225658884952613", + "17256370470460685782", + "5681801935518263609", + "6853934810393582972", + "621694479387726255", + "649083198759873217", + "4052333663180604671", + "5348794590580429562", + "7723209448992965570", + "15836524583901486981", + "12478859533758330380", + "13701809340863213986", + "4296553458016414976", + "6837659293375391985", + "16540521480028827748", + "1485997193168364918", + "14159698257459587584", + "6247200360319694668", + "7363268091423233950", + "9014317427571908796", + "15519361274991733193", + "12603272471546364995", + "1944108005996955253", + "8469356042757089608", + "2997965849869498353", + "9196430932294555781", + "5806654366498055250", + "13290912469992409149", + "9502002558776276513", + "330791153715252718", + "7079194814443264009", + "15946707936026547597", + "1641736062116508620", + "5245034769798442586", + "2565729258675312304", + "12614646598841308905", + "8274406339991077743", + "1661383348822169561", + "15467742607784975524", + "3665174254391236833", + "17417028847837598792", + "14220651316552198137", + "13101468344291730322", + "6671890826474701031" + ], + "transactions": [ + "76bd168e57a4431a64617c4e7864df1e0be89831eabaa230e37643efae2def6f", + "90d06cb306dcc33faba59545e03d91ee83b0409e66a45ffe6a9e3b1049a0c521", + "0f7a3e8036fbaae7c76f7615b09b8c4f1337e96d87042d86e03cc5ab9b6ed745", + "23733214f347970a34ccd772f89396056309744688bd6dbc35129e1f12d46d2f", + "e30d7290ca9cab570aa72bf0365dde39b8d75fe65a4e804844e5708a021f8ab4", + "03d3902bb30f71c29151f8b85ff478be1dc5264785c8c84550b6b59339dc03c9", + "7a00dc347a50186bc0d789a7899f1a4dcbc1e5d5adbb5359df238cd2b9363b41", + "bbe266eac2bbc505b40e74ae6d1960d7c76d3ca8d4b942b6046f0c5f750ff9f4", + "cb63ee14068a8d2987c90ecb12998653161cd8748af7790c75592647260d3266", + "d184752c1546c366866013aa00a2a0f9b40463656072334fc302ff783ff4ee98", + "f8122e3d8b7ad31c58ed3254196b16c23249b8372f06de42191c43bfcf39849d", + "0529fd0fe210f128b8090685093c8c6d6a1a0809e9f174a2917c8f80c090ec69", + "6fa076d2c25f0ab89b71c9cf4694c36202a67efb65ffdfc58624ea80f4009bf6", + "3b3099b15dab0cbda88a454ecede7cc24cc8dc08901bc0be7337538feae0e2e9", + "e3ec42eae6134e74a1ed858e8f7e2e65aab66dc2561ee6f2affc1f4742ab2970", + "d4aa08cefeba30724e2c5e08880585b9912d7fbf383aeab6b7fbee9525bf32f2", + "618621ca07903b74a205bee148c34cb1b6384aca0db1fab2bbdd82866cfe2309", + "56425c88a7be0ae35a99fd74f7934adc0b7abb00f51eb5a2dadc7bb564f05b9a", + "13f331dd16ca3ca8533262b554396ca0e99ff1f68ea6052c9dea7504d8701e4b", + "1855d6aea924d93582e92b81208901532928d282f150e496e501d044ecbe7136", + "3632a80ff935e14da59baced343f113900d52bdf6070ff19af352332172ced44", + "363bfbb7ae14858ab4a0f0049259b72d785657785d52363d6a9aaedd9bfc7488", + "4114409365b496d4751a4424dce769125eac6dbd87091287a98f16f85204930d", + "4659270deac2c6b0030a2db6a78c681ea629421438c7b73b963504483830e6e2", + "523be7376cb099367d805eb1a4ee83b3ae1645ab04c03b3ebd3277ec5b967810", + "52441513709b67c68a8a150ee1f54d2e33afe0a34f0197ecf2a616fd98b74b73", + "574f3579f2bd2bbf4f60ca1edb96dde23a88827fece9e4098c8e9740d13301e0", + "9890ba52dc8ae3386e06dd4a5c98152a249528bbbfb3af5c5a328e9a9a74e0fd", + "38fa93e15190fbb648a98b63dca816abf2d9b405f4695ad9b7e5088a4543463a", + "e29718a6b12666d2dc52b9cf99295b93cdf4eef057ad9bf4f47fd57654fe9f20", + "6bf6a0c115bc062f872828d0310e6826caf20cee6bdc5f4196d00b1b2bb8a745", + "6d949f412c183b47fe697600f903a7740d3b44b258a4a5da8af056587bc6eca0", + "81440f6f528463fbfb37884f42d8f2bd3ef94ead662eee0e62fae4b063217620", + "89f64e9c09b5509f006fbb9239349e97caf5513f591e110e44dd44a18078636f", + "8d6ab35a4625c91316e549ebef9085e5e4cbb7be85cf9146500086bac3a0ed8b", + "9e6ff0848991e3b88eb28845884e6c79459564e73e835219760592d35dc8a2a3", + "bc3f66e88a7bf2682adf54e00d938b93f5631521ea4a0c861e8c9dca38b84e16", + "c4f9333bfdbb7d495873ca51de29375b3b9dbf46e700c220b4bb4536ee69f793", + "d5b21daf82695a13b6a332c35836c42d167337315dbddf4699c68ef23466c31d", + "d8ebd64ad471d91ee2a12fee9c986e43a13cfef836f168515d107ee8dea58d5b", + "dd25b474f12cb920b1b1349125d83c0ccfce7dea27f0e4d06baab46ebada458d", + "ddcbee2daf18a52177c04473d91d5e63329ab3dccda76ed6eb3b41f640e67694", + "de11387163af72a28cbad86960d462887c3fd3285ccac84cec8aaecd1c84c678", + "f929ad9669b0020a37a17f6bd334f4649d43755968cdba7ad0d15cd2e4b8d9db", + "ff1f8fa07cc528fa68d87f1d4ff6c3ae8df9d7365f5eef077b2146446f71192f", + "8809592c1097b679702c2750fbc9307559bc1d554175d807ccce40942c0560d4", + "99a6e5c8624fd7642c6128ba287bb0c9ec2da1e0e8dcaed423df13bfcd0522b5", + "a9fb683405a8664e1d0fbffa605ffa3b15db99f2913cdd4bfb0408448c22a058", + "ace1e3a0de725e8a944611a6bb3dda6c309c26c6f415b4749d084c6421f38d30", + "fddfa2b8c797b66f247a7c69245568e5e93c0a6baaac467cf5247d464c803f7a", + "5088d9dd9c1aa0cd4fc5682a0db737b8fd81348424f0a796d81fe781dcc20062", + "6b5e0f73ebd9677afe42cd83d271e00ef41813e734a9e4d312e07726ce53a4b8", + "6c87d87bb867e0419812f832e07c06fe1fdba941a9adca02197c8d61246d61ac", + "0cbc773f731a02430ba7feb8b2a3af90e294cb1370f97e06c65a5e343a72986b", + "53580b0571bae7fdb77fe2470f38f00f2ae15c6a5ace19d837decbd893f2f6ba", + "f7338be76d01ae40f9cc1993390644191da6ec0f84b10c6df6f52026650556c1", + "03a740dadd99686b916bf298b90c829eca00d25112fdadaef444ff41d2fabda3", + "1b2b43d0a91ab424df5aadec60bac7044c82f5c3c13c9ec588f00a5cd50f3b2e", + "c1d1560bf2a44f2a59bfa2244d303ce1ebaa0cc434a409c9c159ec0365eaed4a", + "ed9a0a81cfa79e0b5af947780fb249d46c7669c4386c83e2a2ed4cfa9f576c76", + "f9c42c2401c69df14bed689c4b42ee3e0ef92b0f20eb2f6777620548b7ab95ad", + "2d2b1a34684d56066ef8681b2b520dea3cfca36b70674fa828ed95972a5b6aa0", + "3eebeb0d6eb1877c7b9611920b16414e98cfe718ec62294a63c3f70668183354", + "1a37cd89a0bc6f257f37431ce93494dc63476102a079b611f05c79f33224ce1e", + "e979723514200e6d69663f8cc6ed671fa418fa69c66c53cdf6494e4db8fbd574", + "60ec3ff66908b4ee077a22fe00ee276241cec475de576e0fd8c35fd4635eccbd", + "24f407b8761b4a0fc54bbcc2e070c811f7dc4c6f9240ed715a8a95924acfa345", + "66d2f629bda689e61aa83375d81c7374244c22d203efe8f1fc70a71fb4439487", + "97b73c30314f7c5fad0314f34d0d218376186267c67ba636b106dab4b55789b3", + "9a2ba4134745d1182e96dfb016385c31f260545bf1d9382e4e757112305e55a7", + "734988e30e42d124a7b3dea66d060ab567dcb8b65d806c32d8607fbc67bad292", + "e354b6c4891980135cc8201f257d1b052d46fc33d6555b633c59bd33f6b281cb", + "3945e67bb5e864d2dd206293f1e778fa2181db5f81c2efc0a89e8fe53e2a2e7c", + "15b1f1ffb2394e17d373c8573e39ebc395dfe98ae2b0793e6ad0d80fa2b12d13", + "4490ef5d8e4001c8687e7e394fd80e163d63b45fc45f655e8a085eef70102a3a", + "c7229a92f4d875112098934900d745a42b37e2888b2c86ee451bcb4cfb5e0c1f", + "c6377ddedd7bcf3b0025d3d00f3dd1d44c94e2baca2b1c1381f70a02a5e6e0da", + "fdd724dacef7442578efe93e841386c3410f44d7a5eb9bd0aec4c59964386f7d", + "ff9956d76b6b1f56cec744bd7f6fa6b408fe846ec278458a2591a43b1b8239fb", + "fe7ba0d0713821dfc43ca5024cfa430829eefacae2daf83f08375b552b578bd2", + "08b55a8e470a018881e27d65fc56fbdd5d562d0f6e1ae753ee49a6d9e7a02bad", + "1472b726972a477b4a20af71a185fe4159012d1c3bf523ca0b3936fa86d7f06a", + "3464941177cb161dbebd477abac7598fef15b47dfc1d99ac212b488b86c00bc8", + "761ac9b796de81737e3f140f831890a5eb7a86a25ed1a6b5bd48d58959f6baf3", + "2fb6f7fdc049f21a14bffa984fcf0e4d6cda6d30d549209e21b4d8ef9ff56e7a", + "7afc8b68390e07585d1289da18a4d89e9ed6a91abd21aa365636ab9e054c2089", + "9e9f37dd168ddd2fef9783b927e32194ec0e2735c8a42bfab3584030bac47c0b", + "0d37689318e83197de4472927d08a651ced33fae48a5595e033db4b7816fccf0", + "13cc5a4906bfc47342282eff7c8c5d6e2d824a3ba7b417365f07e473be8d5586", + "1d9cad8cfe1c0ccc3c49befcf3dbea1aeeb329b03279f900938ff4574e729d17", + "56b78c8935e2bcd301f3be154ea05a3bff9b6fedd59827eb052269d8a052d32f", + "ca82c604121d02677f6a2c5aaf7b0387a395955f0a3ca2cc9d4df86ddec51151", + "ca8a6575eb9aac88abd838bf76d3859244598f30c4e2ad68383c39d350363944", + "c2d9fc71c8e3ee3edbbc4827f7cd4b37e19d21756801ee67c2832f19ef50f403", + "89b622c919168b5aa3bc8018459d45aabdd3b4dcda5a867fa9f2a7eb875e2f8e", + "69ce928210329b22320d923d756fa61c986b8e710f23d41547bac851c5283b30", + "4fc69b18868a8e055421278c04a713d9e249903fbcc888df688b423d3b5de3e2", + "7b8a5d8ceb73764400fdb11e7f2e018975f5c8a5c569b5bd7345a01f8a76ac8e", + "3b2a22bc2fdc46233d51ad9e92ed15323ad399c1e7f7da20cee4f47c45240881", + "d97a0a8f8db3d9c75ded3e0c472d821c106c31548bba90c4f9df3f2b0f8c0db0", + "9da6e6f92938b5dde9e1836952dba31c3e501380a38d70c39242e38d82e9acf7", + "7175ca33fffa0a5327119d493e67c5185d56649a580aa95366dec2468ae01c32", + "9b76f5cf7bb2c248965e9b8608774370ed3fab3124621036368ef142cc5688e1", + "e516a8301f254877c65daa491f7cfd995833f528c2c2af7df70c83c41933e35b", + "f637c84b6a249181bc010df54d6271ea65996c528885c7ef6ea0198039df3854", + "fde663f1c01256855568c80d967ffd73c93a3096db0943ef8801929a0bf3fe7e", + "ffc9fa27a918e3bae19aeda9b6e236602a029249701862b3e919642784279e03", + "b1df50fcf4eea46c32ac98628d8b8b2a4249c7906b866e5331f6bdaad02af823", + "27eb23f7e828cf4b5402fd941ffad69f6b4236fa12831738094070aa812fc632", + "340c1207a356b6c00b8d35cca5b5977a322ccbae2cb9c70c7ad8f5c9999ec581", + "fb38cb3f4e42da4c948fa13ef1a3ade1ab4830b51eb751432ae8f989a4035df0", + "890ad9c2b67cf29acb2a8e0d8495887ccdb681c3828c0d49dfb3da341f810546", + "d66d7ca4a3b7ac2eff15bcc98e96b3aefc7ea18c088a13ac1cac97408cd15402", + "4bb2dea1d7b44889e369853421293d812b18cc3328dc52d368156b17e8825f54", + "23dd658fb3e50fac70af521c50b43f95d5194d0d27dd149d170a4771141d4370", + "4f555646ae62f6ac458b00f36d570e935c3b9b83116636e01292ffd4d1761c74", + "aa93866398c6a9e065cbafa1db5e12b40510c3da299d97d14c56d12c4f44d42b", + "72cea8d4bde0d262747a4627a478ec8afd40ebe5b5c38a3e6ae157c1d73d766b", + "c5f9e74cff064b7bcbc9b2b59199b835f5abf0a7d5d1f77cc5e01b29142d9210", + "57976122fc03bca4a5438a12e667507b95d543f959b99161146b3851c1591f3d", + "ce6e4b199059b9f2b8baaa6475a7e8002f412598be0666943c170d3f48ec3042", + "f2d0e7cf9cda469eb510092df957b58287f5b3c46dd596fc0398905eb1bbb1e8", + "fd866d74827a4d468f97260e05700f8f4a317ae98d0c1e0c307f707c21960aab", + "3a0f3747086ed148b3af107ec5e02fbea4c3036b568b313de45c5d13f9348bd2", + "d9ba72dd34f95d003a3b177dc7656244cf2693d72337463dc16eb6ff1d8f679f", + "276ef67f4c04365e81d22a55bfcd542eb54d8e178359c7084253de0941de22c9", + "591b307b57e1f924ccfda0e44aa626f3055bfc330897b12aca5fc99d004ef377", + "2f7f4453bb28befa7e3ad8a241a72455ed16b94f8d65ead817cdf06e5d320542", + "b4fa3a85e757e4c07f01380c8a684d380353d9810707699cd59b791f836c6571", + "ea23f249ed30ef8d4b12dcb967cb1ab728bf283f7180f13ed2e1a4e392293754", + "eccd803527df3ce48f4be72e5e3a6f278d4ea257ab984857c152448bd8e4b3de", + "fc9a5dc83e58c3594ebcbccdf76740f41d66329fe89dd5203cc23bf37eff2db4", + "487a87fab87abaf2ef38fa802051d107d60bc257b5be62ead2a6e0fe97bbeb95", + "9c93a90c3a81891d483b22d5ca132398f6acc6dc1095d3ae70c7cc9ddfd3028a", + "d21115e0d477d54e56349c2789914f561a02ef26d25a95ed4041927d9bce6158", + "34ce6311cfa5205d452db2b0e7f74717fad33119ebd71071194273dd09e28aa6", + "df898fb403c3487776c0afcd93c71755947924d093e55bdcc645cd7736235543", + "c5b34b3acb869e509b0bb1634c37c466f05361de269e1cf432a661908de9bb17", + "e321699c6d3829309e2052d2fa73facd8285dcbcdaca90503daa6a4e10488649", + "d140e1425b21cfacd07ece474784b935f170670ea7d44c07e61b7f1559c33ec1", + "02d81bc62bf59e2861aa0e1078a0fa67b44ed10d20c15bf610d2c60ee6d87d30", + "f8440d2fe36a2496281192a6f2017c3e0b1143dffc1299c5717f75c30baf8b82", + "5f028b575a7f0b0f417e3b3642b8608515cb3e7aa05db77b956a69b4bfbd981e", + "605cb7e40c8ef9bfd34f7ece54fc68a915c589691d8e19105d909061643b39fa", + "223e866865d9b65a44b6f320dd2f5422ec209c73fb302f2b08bce1d79bd05a6e", + "e78e533751d7a5745cd30247ee58392a3811b6546d291727e9887d6d55352864", + "088202b75537ba3e7456903780c2c91cf72c58e8e9856e3cc6d5c465fed00724", + "cb3dee266d937c04453d6ff7002d9948e5be09bedd480076e73aba4d85a1a39f", + "f1e17f743a17d85b1d2567ac748dba7cbe1d5dfa3707e1beb8862a56fcdab821", + "f27bc6a1baf3a75b4877f7a1cff333bede90f394195ee9a8b7098ae7e7e38067", + "f5ec7a17a63210d7e1ea28ba45168a9a88f6c9f35485d265b4dc98c0a5ad5927", + "f5f17b136c8955668fc75c98953b6b903065cdba9e905e6f3b94af225689aeea", + "386d6d2933965ffcb33c5f6dcf886e5df8a4bcc79e97c3a85dda83f3b1d5e3df", + "42001376bff16d35118fc05a271d0795a6d30b2fd7e9458974a1c2683a87c00a", + "64cd978b06ea47ccd4ed727e451e285b148cbc850be89edf2b9b4825c798886a", + "9be031bdf2f988e083808d50daed3e86b7c7c7b758e03e45de365cfc5cdad98b", + "0efc0a63612582eba21394b53491658d45dc64b230e25ec8f1aab4df6e273006", + "3bc7ed0ae39c641e02ab6591e1957a99755db6702724d2824d45491f33dd1a40", + "430063c50d13e4b386a8577340b48a9f4e7620b09445420e9b640cdf49feb72a", + "6c4578227054d105f28c3b3b622203ae3bf5156e326a3d2cc9b4c4e835ea8112", + "7fa78167690601ad2dd159d819aca03dce84cbd4cda1ac4d7ff981ef6f7d69e8", + "88d4bb35378e34fc45e3caa8fdeb2205699361f8e2aa488e0e98be5b7ba870ed", + "a478e34f5cb0e313d7f70e051bf549e5e3ff7e69ac93d5c4d156ba055fe447b5", + "caed90e15dcb174044f519930f10801c073b36f05b3179c75bc7f36160a70079", + "ec48a8a8b24e441695b24ba2d4f6cd964d24a6bb7ab6ce037252a0a302ff0f03", + "f172d80d6db5e5080abee7718c73a8d61892a96196c3d886aad9cb8644da808d", + "ff172882257b8f162f16a9a670b27039e23809b76a80842f70dd1febb00732b9", + "9d6926f2649eb3e4157be68fc931a4487b5eb498a61397d8a4bdf81416db8a31", + "ac4bc7ba22b020c8ac3bec48dced41361e63fb1604f1848a800ab20c379619f8", + "86e6e88147fac70815bb41498becb00ba631c23465838901e4ddc1b5db158ec4", + "d5e42d7e6cdee582655901fe187513db6caaa4722c659256a642a5fdbe5061cf", + "000f3c8a706ecedb17416431061eb9a15d925a328edd4c0478481673ca385005", + "16e1bb38bdbe7128bd85816e4933bce9a356d4b77868b647b17a3b4624263e30", + "a06928b3e6493b223524826a54ca70885fdbc65cfd712e756dd8168e523d9519", + "dc39a407d04ea4c14b0603d3402b5111d17d90ab18229bf2c8f7395c88d10155", + "c73f441649bdbe652fbd05c37e81696e6991c2b129161fd90223c56fa9c7965e", + "dd984076dba89d32d5c3a47e3fd53b04d2c4263f20781d2dd3e7c68c0be2d997", + "1ab767240d156568db3a836167097c81599514a3e84440081fd2eecdbd3349b8", + "c70c8eacd8d1d9da965b2d9836e7b9ed47ee67e9b75ecf20e8b4581aed13a328", + "23226558116e9fef05e4d257afa4c6bbf01f73bb0c1589ae7cec2eebd92cc491", + "9671f9586b38f1dc1a6b715c2c5e4e4f30e2247b541b4e91f8f716e8a0f6c9e4", + "daf91acfb23f6b3a950df087d80774ebb74fe194e01e048e974a643093ae656d", + "81a7f9c699cb9b9123eeccd81de1cae1b620033f337b367590c204628f392cdc", + "4ee69f95f7eeac9d4c3768905f33172cf7651726b90effa867d16365af440874", + "9f99233049815668ef9a20fa14e912f42d6a16dc276b6e5fb560afea1c2406fe", + "53572b544b88b1876a15ef324f02ba4a594fb341ee1c88a7d904b2658ba73789", + "46f5570912d57c022e7abcd9dbda9478531c4f1539b1eb2603ba009bf2c2fc83", + "a341122217619a4c34542a573481087bb07c41125be77af4aa56b6a13d4e43bc", + "73df531d66828721c8440b0a008a4de62b331693a01a12706ea624f9d187eace", + "37b97b5300332288f518a0e46582a989a88d75ceb3f5795fb70f95a8db88fa64", + + "fc8248f9d5d054d09961e2c27d9b6dd87dd7461f654f72ddce8547954f7e8761", + "0bd5e40ef75d8a76882386eac6569ef4e04488ea1b74f1f3947c0219d25788c4", + "a82bd2475912be3aa5f85833e6b2b4973df2eae80447dc70e0f35efb149c7844", + "c7778b52f808b29d7f8de036a7686e2e4bf8cbeda360ed432a26c089efbeb31b", + "9ec8e3f9b6f097c65e292bc6218e53656cc21bf2b4b27e3b716b921a93f08a0a", + "a90ff245459a7066d7132a193b864c40fa4eb2597100847780c8029364a7fcdd", + "e299b1783031142cf4f228b844d6c00d3045962d50910b8fa995f15781562e6c", + "3c9951cd7cba4ec48eb04d49a2e811798f30d8d5330ffc901ed7d59c712ae87e", + "7c20c3e55d771a9603397fcef5c32547fbf99b37aa6030836cd1bc405bc183c4", + "65cfe144d953b9141312f294fff70067e2ca27515449e37297c4b87b24956898", + "5c34237e466740aa62a6d2f060c839b737094c4c8cfc92343ebb8229ce45f51e", + "5835af622801fb643601b350c23966042af115dfe4ab34c6e0b33200bf62b9bb", + "9a3a919b2b9dde7ff91c531f928f4141ff22afc1a9185b98c1a8075f57025a29", + "1f203b08c54729503b536fa75faa0c127f99befa40e7a73a7c4da7d7f0955f2d", + "2b58f2ff1d421bfd063b87d3ebf2118e2e8b4bdcbedc7d888efaff3e13e3c26e", + "a1d2d2318a4fcbb3145e3972c1a6165f19914f5829a4f248bc083af5055c5acc", + "8854cb9d82913c46d738a829e8ac1271ebb8541973ca72659fbd9dd5c410a18a", + "af94b014f96ca01a04df6e22aa256fa3f13fa6eb0f55de536d2e4f226bbcd75c", + "e6ef65acaae9650d4b0693595141fd7adac13608fd0f078de4a57d11ca315102", + "0c9d308bdc166d94b85d4f2ff700e38545776e2bf933a44fbc7b023c50060298", + "0ef6356cd9a36e92847b05121dfea3513f1ba1ee4352ff3d3f700f66808b322b", + "9c8c5c2945060938c10c7f866688f384f89b18b42ea92190f4637f5de5d0dc7b", + "515c760e0833cafd0c43d3b243cda756cd83a1be6a59785166b911229785c7f0", + "e35691e6bbd4f9b79b936110ffa513001fa45bd00442fb7b7cf6b82411ba8ce4", + "386b8f59783d9b84370534c62354edc230f49dedf7bce380e43c02f7372eead4", + "b89777887c2ffc97076e69c6dada7a26d08e9d69aa56c49ac6b0266efe841622", + "76c6f9406d1306bb482d9229bcc523f9c1c5c6388a59565ce3b19e0e1f26e9df", + "32aa60577531c190e6a29d28f434367c84c2f0a62eceba5c5483a3983639d51a", + + "035d32c0eba6e76bc248a202aa23d501d35ea5f1ce113ac4f496f1274e03df54", + "fb945ec58fb74f597eab09952cf0a0b54ff35941767571a2edfabffd5545ce2b", + "a47427dc0605bc933d989b00cb92b0d036915592d36a7348b3277084e3aa8ec0", + "fe5043a086d9b27b45c4d9411013c822e9ecc7a77b6f506a58900e18c6623bd0", + "9b7f70815fa01c0b5baa42f96e00a8519562648d06d52ea6e7687291ce17a4a7", + "0376a9b2d6a13b69d9084b3312e8370b604931c0890daeba234ebcd40ce0914c", + "9c785f4feb5de0380969b4ad82a25049d4e4cbc1e57698c80a4845f0734d928b", + "37e1680dad0fd00fe1bd17633a0908699b35ac42928f198c65be812b640fa735", + "224d769ac8515e50c1a2a7f62fcd7be60ee54fb7230288c79d35d0c49924efa8", + "b43c2940feb8794c95266051e80cdfa5e3f68f9891753a1411b74baef3531bca", + "7bdaddc8802d157c747e0021eaedda99aea98f5193db6b8a7d77d13fbebe8582", + "f87c7fa28e4420a8395c77d803ab7d46a71fbe6fac3fbd955e483e2e81bf881c", + "af41b49f9865fc3f99795557f4a54a35895ea64141146c7b9d569c6455332255", + "20b7557aabfa5ae2916ee0c62aa9622436ff91d48de3c05bec3345d018932359", + "ec86c0951a797a42b9e86262d705ad22bd6ee9e2ee93e99aa4ee25be2302f0b8", + "74e103631915eb8d240e3e49e6cdbb991e90c4e63f5170e3c9d80fcfac83f06b", + "60e7fc3094f4d45dbd105d589a5bcc156eda7ce85b412833ad1814c285fcf447", + "0b92d7e3e8601cd3e098d9aef3644d6f961a82a309fffe0f35fc55defc234554", + "169ae1ec30b70da7312a5c26a20b224298426517ab531fedbac251f0ee0c66a7", + "941a5b54b2e0086d972b9c9fbbb7b35b4f09ffff215350db7c7d936106e71592" + ] +} diff --git a/packages/crypto/src/networks/devnet/genesisBlock.json b/packages/crypto/src/networks/devnet/genesisBlock.json new file mode 100644 index 0000000000..846c0216ac --- /dev/null +++ b/packages/crypto/src/networks/devnet/genesisBlock.json @@ -0,0 +1,896 @@ +{ + "version": 0, + "totalAmount": 12500000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867", + "timestamp": 0, + "numberOfTransactions": 52, + "payloadLength": 11395, + "previousBlock": null, + "generatorPublicKey": "03d3fdad9c5b25bf8880e6b519eb3611a5c0b31adebc8455f0e096175b28321aff", + "transactions": [ + { + "type": 0, + "amount": 12500000000000000, + "fee": 0, + "recipientId": "D6Z26L69gdk9qYmTv5uzk3uGepigtHY4ax", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0208e6835a8f020cfad439c059b89addc1ce21f8cab0af6e6957e22d3720bff8a4", + "signature": "304402203a3f0f80aad4e0561ae975f241f72a074245f1205d676d290d6e5630ed4c027502207b31fee68e64007c380a4b6baccd4db9b496daef5f7894676586e1347ac30a3b", + "id": "3e3817fd0c35bc36674f3874c2953fa3e35877cbcdb44a08bdc6083dbd39d572", + "senderId": "DLK7ts2DpkbeBjFamuFtHLoDAq5upDhCmf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02511f16ffb7b7e9afc12f04f317a11d9644e4be9eb5a5f64673946ad0f6336f34", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "02511f16ffb7b7e9afc12f04f317a11d9644e4be9eb5a5f64673946ad0f6336f34" + } + }, + "signature": "304402205f6acbc1b91787b97a02ce8dd2f511ec8ab8786a9e3ba058173a94e80a1b4d49022044ec8f8e7a14dbb661a3d9803484d220a5038488b99befb43b6a22a5b7c499d4", + "id": "cbff39a30fea596d0cea50c78cfbb23a6d8546ef1487abe7d5023ae949357832", + "senderId": "DL6wmfnA2acPLpBjKS4zPGsSwxkTtGANsK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03697abb61ee85e020a35a1d2701112e7e16477ac9d2eb2e8900a27995edc917a2", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "03697abb61ee85e020a35a1d2701112e7e16477ac9d2eb2e8900a27995edc917a2" + } + }, + "signature": "304402204cff61e3c4f0aa15a31f4a84611b7ab555a2b15ebe7012b6cfc99f711842277e022040dd69a0e6ba7e1b8be9d7dc7df64f0f23bf119f01e9babedba3851d65ba3263", + "id": "bebaa71c139fb53d5453833ac9c8f1491bcbf96be4d10ce5ea1bcf5e7f86e07d", + "senderId": "DMCAKuFyjRhZreFtgaBjV43Qtb3EzVUfVz" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "027e2269d8a770343223bedc49bab31b3c52fb4c1df6627153e6374ac23e2d878b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "027e2269d8a770343223bedc49bab31b3c52fb4c1df6627153e6374ac23e2d878b" + } + }, + "signature": "304402200f86542051d29cf0c9a2dcaffa1729b7d59e85c9c01a19d00b5cf2d8193c160a02201841255da9bf8fecc6c78b868c7ebb3b450372a4fffeda0b31bdecf344fb096f", + "id": "44a36f5fcc58f2091a25c1346edf2a0a707e5d58c89be2f814f1c658fda7c559", + "senderId": "D5pVkhZbSb4UNXvfmF6j7zdau8yGxfKwSv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03858d4d3b77c7c227f6fe3e18b5807aa476828cb712663dcd79df87e439cc07c5", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "03858d4d3b77c7c227f6fe3e18b5807aa476828cb712663dcd79df87e439cc07c5" + } + }, + "signature": "3045022100cdea6cbd578c8380c5ffc4635af5c9cf9a06b8ce8eeeb3a03a93936c60f0b67a022027a8efce7700d9e4192f50091cb5d72b8c18ca867df92d1d9cd40dc3475da4a2", + "id": "e12ade6f3832d1aced7ddb25bb63e06efdc1f99e3eb16c501f7cb790b107a09e", + "senderId": "DAjqLwuWd4t4rEDZ6xpk7Fcyndv4Qcr5WZ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "023918d30ff448ec897e12b77ccd529835c78aee07db1682639320c253cc21a1c7", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "023918d30ff448ec897e12b77ccd529835c78aee07db1682639320c253cc21a1c7" + } + }, + "signature": "304402207b6770015a921fd6ed1d0e0faa06ede9af7e66925a9dd2ddd1553e48179cadf9022044ecaee06f0661abccdff90be1157c5410fbb42ed8e67db413201c61740d7e39", + "id": "2c5969f690dc7d8e0c6720405e172b4ae06ce186f7ba99972bbe7dd587b3b319", + "senderId": "DSMxEhoudwLYVt1jtHDu1dtisa2gS7LeCW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03b12f99375c3b0e4f5f5c7ea74e723f0b84a6f169b47d9105ed2a179f30c82df2", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "03b12f99375c3b0e4f5f5c7ea74e723f0b84a6f169b47d9105ed2a179f30c82df2" + } + }, + "signature": "3045022100899b81a7fd3683fe1cb60e8ea070dda891ed22e9ce386538d1f57a79f801e90e02201592f75d7d1dfb15da63c27d190d915fca192e9d69013af7d8087fcc7662a13e", + "id": "5191f5fce08b980f0004f654f0af58eba43a112c25eac53e29778239c7c2b8ee", + "senderId": "DNv1iScT2DJBWzpJd1AFYkTx1xkAZ9XVJk" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c3d1ae1b8fe831218f78cf09d864e60818ebdba4aacc74ecc2bcf2734aadf5ea", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "02c3d1ae1b8fe831218f78cf09d864e60818ebdba4aacc74ecc2bcf2734aadf5ea" + } + }, + "signature": "3045022100b71c3d87f13aafa9e4e7ecb0293daa2f40ac703a8678f58c3a3c04c57845d04802206b0c9e48051b23380c158abc441372f4074b1fb76040fef0a008fd9a9a1a948b", + "id": "269b9bf296615f4cca0812f3d15ef8f0afec02b123088e46008ce4a353def912", + "senderId": "DEHtM61jVo4uJWP23B6mGrb6p9batXCHZs" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02be5acbe305bc5382ebd7998f3f42744c793245f37a645916771ff123fb7b4ef0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "02be5acbe305bc5382ebd7998f3f42744c793245f37a645916771ff123fb7b4ef0" + } + }, + "signature": "304502210094b62a07ee25d1058f5eb2c71d82acf55f62b787cb017e821eb22885f16c81bd022074d2f06ba34771e2fdb65b5d46a8ad69d05e3e2500549c8c9dd978503b8ebf2a", + "id": "588c80d5df11abff1247672f875a9c44c36b45e742187b9071f1a9e04a1ec3b0", + "senderId": "DKXY49bVxzzr3QhLpoYWPvdKcBrKfchE9h" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02e9ef70986ab6de9dbd5e1f430018bb8dea671d30c1e34af5146a48f2b73d551d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "02e9ef70986ab6de9dbd5e1f430018bb8dea671d30c1e34af5146a48f2b73d551d" + } + }, + "signature": "3044022038243ef0738d6edaf8313fd51bc0f1482a055880e1a37e0d28f13e7287d3f5f802206e3cc68b90437b8b879a83c490aac772d89c8f15c28011b7409545c9e934d922", + "id": "a11ff1c1ffae49f6cc08f1b36606519f215f3ddaac5dca616f3e0858f33205e3", + "senderId": "DQEAsYqgNQ92wqmH6b1WGwsJ9JWhJtMTbQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "024c149adb250db9d314462e7e3628c8a63d263812d85306a530c3ee1f5ba31618", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "024c149adb250db9d314462e7e3628c8a63d263812d85306a530c3ee1f5ba31618" + } + }, + "signature": "3044022001e9b6ab03157a9ab0b3a882e27576bb496b0a749f2b4c9f6bfb77ae3a18d72502206f4bc9e9f4375dae548c9d480b4b149af35a3625b1aea1e9298494dcd292b935", + "id": "e5034eeb44b0762a884d346a0d8806e0776bf7194973d978e07a2247d457b6c0", + "senderId": "DM38Tc7HUZ5T41swShV2ELVtvjghVgHWLw" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022eedf9f1cdae0cfaae635fe415b6a8f1912bc89bc3880ec41135d62cbbebd3d3", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "022eedf9f1cdae0cfaae635fe415b6a8f1912bc89bc3880ec41135d62cbbebd3d3" + } + }, + "signature": "30450221009bff8a22cdad663c21f587e828cf774c3b399a66fbe9a4161f22a932f0a389bb022045b1ee28e49cc95d280e4d07bc044459381c8d7dfbaed94e1bac2e22ece22f67", + "id": "831e9c84af1e61afb18fc7561681558009b51206e920495b2b708e9f65f261cd", + "senderId": "DReUcXWdCz2QLKzHM9NdZQE7fAwAyPwAmd" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02bc4cfee3716fcf191caa51c7bd2205a796b504b9ad5461864681cf1b33912003", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "02bc4cfee3716fcf191caa51c7bd2205a796b504b9ad5461864681cf1b33912003" + } + }, + "signature": "304402203ef34aac4fb45505b2e869e1a1f075a1967bd7f7fdb95362b33bc30862007517022028f65b2409c64c0832f1874f5e7e6173b107bff4f3737fc41c2042cf1fc9370a", + "id": "5d7cbc26b116c9daefe94965b5a64e34fd6a7e42de3619a71d0ba5976976c966", + "senderId": "DLnysb6HbtTcNpff87P5f47qVpFxYAqUSY" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03f3512aa9717b2ca83d371ed3bb2d3ff922969f3ceef92f65c060afa2bc2244fb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "03f3512aa9717b2ca83d371ed3bb2d3ff922969f3ceef92f65c060afa2bc2244fb" + } + }, + "signature": "30440220763f33a056e01ee9749e60a17722138dee67b88cc1a54099e30939787ca6890c0220606b4e77d517b9e2d3f2fcf92176f081f3ce35c76ea4dc838bfe5f9122635bc8", + "id": "8ad18dfd85c61f3fbbabf8f3a0363cee55966c2cad784f96406aaeeb8d87e8ac", + "senderId": "DRqv1yELdrVc3z4ViTe9ueWiPm8Dbs5ZV2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02e311d97f92dc879860ec0d63b344239f17149ed1700b279b5ef52d2baaa0226f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "02e311d97f92dc879860ec0d63b344239f17149ed1700b279b5ef52d2baaa0226f" + } + }, + "signature": "304402207f764b224b3baa195c4385ed608ff2ae4516f00a0d26c370fbe8d2da246f31fb02205be484657a0550bbcf334fe20cca1e5d4b709d7288d497fa6f8ebc9dc08e4d91", + "id": "75604d72872f730d7c38b9d73c916e4a532408ea0074850a581f4b28bd62acdf", + "senderId": "D8vwEEvKgMPVvvK2Zwzyb5uHzRadurCcKq" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c57b6a3eb7d01ade51f95c8ae4e8ebeb7ca7b8422ab0fb2a236de5d1a5bc6a1b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "03c57b6a3eb7d01ade51f95c8ae4e8ebeb7ca7b8422ab0fb2a236de5d1a5bc6a1b" + } + }, + "signature": "304402203fabd59dfcfdf9a2789b08b3fa44685f0327e314af34c61a70ae0a857a54cb7302207a87e1d17b1526a16dad101bac823dd6c3075b9a65ea0045e9b60ffa0dba51f8", + "id": "d52d2fa7a2dde4e8f76e9a4d87c9b85100390e85e502f7f53760b062e7c58b2b", + "senderId": "DHg1jYVS23D6GP7RuhckuJsYAr6crH6c3Z" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "020aac4ec02d47d306b394b79d3351c56c1253cd67fe2c1a38ceba59b896d584d1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "020aac4ec02d47d306b394b79d3351c56c1253cd67fe2c1a38ceba59b896d584d1" + } + }, + "signature": "3044022036d24450a81aa6343f4e7911301f8fa1e8a7a4d439ad140035f5033841da95a302206fa9f1721e1fe5985dce006270ae5e25f4bf128b8d007350caa7041252a68356", + "id": "d11fcfd463a40465ed61d8d624f0a4141c7a66629d0fe36d40d1b181abfe7673", + "senderId": "DRgF3PvzeGWndQjET7dZsSmnrc6uAy23ES" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ff046250cc01d4ccb74fcf6ff9b6977f5a67539cd9c461f9d41436677f4035b7", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "02ff046250cc01d4ccb74fcf6ff9b6977f5a67539cd9c461f9d41436677f4035b7" + } + }, + "signature": "30440220347cba116760fab827b31ebb2169679cfdfbe6b5c104ea74a2e4e3878bb064e3022015a44c54db1909777d2d4023716de8cc30484b0659d9776400ce9d7ccb84041b", + "id": "4933cee8da17f173db0dbbd4746dde532dbadd50060dda1d571e2ff918593a54", + "senderId": "D9RmfFg3xPMdxwo4AsNA1xbBoM2eY2quBM" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d6ba3d1132d7a74bbe9da3923168aad70146b5646c6d6505bb7f500b211b332c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "03d6ba3d1132d7a74bbe9da3923168aad70146b5646c6d6505bb7f500b211b332c" + } + }, + "signature": "30450221009aacd21661b435f58c40969cbab10600c504fd5d705ee174cb9177e36f893149022049870b13972d9eaa6a3a4335d892ab2f0620c231ce90d0a9cffeaf40f9a3093f", + "id": "8cb3517ff945dad8652d439c6c4db47a040aa41359897f45d6089ad325f4e239", + "senderId": "DFjnYr6USjvtaz9VZv7agd144W77WUiWtN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03ef692bb144c368b4844ceca3ffd30fb8c82b97b5b40220473e9009925637e9f9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "03ef692bb144c368b4844ceca3ffd30fb8c82b97b5b40220473e9009925637e9f9" + } + }, + "signature": "304402203ea9c88ba6bbe508e1753b77593be7213b4fe9833ea5b09168a4c85575b5b1a702205cde0fb97c4786d9a87c3afdc3a1ea0557bcb00530f33aaeda67fc544f39e3ef", + "id": "1bc38d97669d9bab1b4f59927d1f752732ac400c80cc3e2972a59c4dc1f59423", + "senderId": "DHaJZBGNrWLzbWbVTc5TnHMJXKeT6comq9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02aabcdb8511f55b6a28593979b726ef55b1f5bbf16a83205c2e2bfc9d8c2909e3", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "02aabcdb8511f55b6a28593979b726ef55b1f5bbf16a83205c2e2bfc9d8c2909e3" + } + }, + "signature": "3045022100ff5a0dca3050e01b69688c27ee08b9954b9424354fd1ba5d9f100d982d938fc402203159fecb536b4a4dcbb262e37df5649f1497ac2a5e58f54f9f2a1a5b2fb981d0", + "id": "8232a75bf0a7e2133276dc9f66c969ab8710105b5128c293d8a51efe5328f99b", + "senderId": "DT1ftBZEuQm49xzqfq1MiniwvLkFtr42KK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03b1e1d0b5e1dbe57109d78119785e8c5985a47e282a048bdf6e8b1fe61b7c5c13", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "03b1e1d0b5e1dbe57109d78119785e8c5985a47e282a048bdf6e8b1fe61b7c5c13" + } + }, + "signature": "3045022100f041225c982e862436317172c593d016e6f1f6d1f5c732d3ba1806a5956015ea022079bac9a025de182905dd19e151f8414051227387e42067089c5017371585fa91", + "id": "c13a52286d075cd6ab637047dd7512514352fe25d32fc721d8cb867fd620454c", + "senderId": "DG1hN8QzbjsMdQhCjpHDQoc7BgBt7WBRum" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030efa0d981385ad10d237588ed67eee4a245ca552673b7f137cc2e9c9dc7143d9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "030efa0d981385ad10d237588ed67eee4a245ca552673b7f137cc2e9c9dc7143d9" + } + }, + "signature": "30440220478da5abd03f51d3a1b2313027624ad38a3af2e832fe4998fc70524a2032d14002200f4ded96178002713f05ef0bd80b2f5a96182ee5f9b2a4508c663c2c6169aa82", + "id": "bc9ab5bde4513df5c7ea88d2b625e2b9dc33681d3b6ac8c2e620d92032f35588", + "senderId": "DSQhC4JyfWaxsZKzF9Kfq5knVifCZA5jLK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0335fec08c867b80e3b71545be195e1b9876b2040d5393fc177b6edca78bde8e42", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "0335fec08c867b80e3b71545be195e1b9876b2040d5393fc177b6edca78bde8e42" + } + }, + "signature": "3044022001a806c7d5a40553fb13a1fde597f53a1904229d3c2ffba8bf9c86c10f436170022058403f447c5d3aa98af0e4fd111c541e33e5d9f271093fa8860ff131ed6eb6a1", + "id": "f585f7fc3b709c91128b1c2c09fb6b8d43ceb6220b85e5e1257715de748eb6d7", + "senderId": "DNKFUB9u37jWrePHMXhe3qWde69JFe3HzZ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02a5522a8cb7bc98f8adb024f92d8455918e18d3280e427e7d73d40a03a1698b96", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "02a5522a8cb7bc98f8adb024f92d8455918e18d3280e427e7d73d40a03a1698b96" + } + }, + "signature": "30440220116373ef6690b97809bf2063ff3a617a5ed8ac351978ad2ffe8b2424c8f8727a02206c64f816b524f388c878fbc51a0e1229fe8e0cc774964711581055fe576965d1", + "id": "b07658aea5a70cdb8cb9001625979681d49a8b1a6989e64a5098b959a6f1f76a", + "senderId": "DFUAdEcBMCPfBq3ZjM1DViAp7oLL486zEB" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02951227bb3bc5309aeb96460dbdf945746012810bb4020f35c20feae4eea7e5d4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "02951227bb3bc5309aeb96460dbdf945746012810bb4020f35c20feae4eea7e5d4" + } + }, + "signature": "3045022100fdf80150b0cc8956bfc2cbed753a52343b9b95c858620c3e59aed44630114a9e022041a34e074ae8e36ddd7ec352837395733f93eab9a77f3e4635c6739c54d22048", + "id": "f60145575d1dfaaee2d8c7927c30bae6ba7c82c4381ffef374b7a09388998052", + "senderId": "D7JJ4ZfkJDwDCwuwzhtbCFapBUCWU3HHGP" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0259d9ca7922c277b0e7407a88703bbb98f5da43a335b0eefa6c4642f072acfe79", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "0259d9ca7922c277b0e7407a88703bbb98f5da43a335b0eefa6c4642f072acfe79" + } + }, + "signature": "3045022100e90fd1d6bc5572317f97de693c8186c550c81e5d2ab9314dc56572984d72967102206fcb2d63ef004a9356048ec44a083886f7ff73af5fde973da1275ef694244938", + "id": "7d1409e3032e05f3571439148ed8914ec9d7cd11943bab4170823049dbcda9ba", + "senderId": "DCZFtPKqK5iz294jyhrXPXKGRwQpGru6o5" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "027f997ab2d8874c8f06d12ef78d39e309ed887a1141d2ba0f50aec8abffaffcde", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "027f997ab2d8874c8f06d12ef78d39e309ed887a1141d2ba0f50aec8abffaffcde" + } + }, + "signature": "304402203aada267711da498f7c6e3714d51ae89c0f4dffdcca85740eaca3994cb36b08602206b7c29fbfdae516c5cc677b8932f9cb2112c7f231abfdd040148ddd6cbe930ed", + "id": "056716dada5916f4f8e108a49a93d9b5c4d2da5773e9d47ec81509624a46e638", + "senderId": "DK52aX793nEMRpDvRVB2euBgGXiCpRfmwQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02fdcb7706624aba3c050c19bdd05f74a0c746614462584dfbba3705810ba2d69c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "02fdcb7706624aba3c050c19bdd05f74a0c746614462584dfbba3705810ba2d69c" + } + }, + "signature": "304502210086fe27f8a2527269b59728ddce8cefb41c4b11efb49cedadafbf364ff972beba02204df3a83777933f4cb0941748e148a3f0d2b03b5e1be93ff429b1108ba1791c04", + "id": "583e706ee292251a32526a647576a4467162ebe4dd61fdf4e9bb878083745c7d", + "senderId": "DP8LLTXWNZfafDbetQEWm5uc8YYpNz1wFF" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "0212a11582a28f178b906edbf8e8c447ce1ba86041ee731e595ae4d39ef034c2ad" + } + }, + "signature": "3044022065af2653051345aef10cebf5f98210f228af00768b094eb0f82a5a0765180ca202203c87461e7e40f17273be4638f604b01cae72c47ffe8815917d0dae6f6195709b", + "id": "4041e3a8e3760e40e7b3d0269935b7894df156dbdd08092a9dfab32a00dc0572", + "senderId": "DQUtJHQToFo9Kbgm6R9afGamvXptDzc2mi" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "027b320c5429334ecf846122492d12b898a756bf1347aa61f7bf1dcd706315a9fb" + } + }, + "signature": "30450221009c0682b562cb4405cea7e522d7b98b628af4b33c1bfbea354d67351a0d3a066402204e1c060c533c8a8896dbfd5e6219e2a0e7d96c9d40cbbb23bbdf8bc6c0450bb8", + "id": "ffc28e2cddf4494fa4bdebd90e5b8fa41253c1faa02a95eca0d7ba6bd04ca616", + "senderId": "DJmvhhiQFSrEQCq9FUxvcLcpcBjx7K3yLt" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ff842d25fc8eec9e1382e6468188b3fd130ab6246240fc97958ce83d6d147eaf", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "02ff842d25fc8eec9e1382e6468188b3fd130ab6246240fc97958ce83d6d147eaf" + } + }, + "signature": "304402206f3e7f51cea90b6180dee3dbb80a0e7fa5f06ab6266b73ae8f1cc05de077247a022017d5aa06a94eee0c3be3764fb644c02db8a0367010e726c646e75df3e41aecaf", + "id": "062b54b6fc5abdc8e7d57a98c9a8f2d0ddfd42c98c640a6242c9af10fe792d68", + "senderId": "DRDyyHNmfF2vijmA3kmMp2JvmBVMLJTTBr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a2933", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "03a3c6fd74a23fbe1e02f08d9c626ebb255b48de7ba8c283ee27c9303be81a2933" + } + }, + "signature": "3045022100e35cca00771ea7b1dfb141681aebfc30a698976203b2429b21d296980f278b6b02203bef1d5571dc1109fbf7eb956e411bce5d11ae06557d232a6fb7c5e8055a489b", + "id": "d1955dd9e4ebafd3c33fc2752ddeebfb5066c0e9c06275e13b3904d2db4f59ae", + "senderId": "DTKn3J3pMjtPLJxZtjvqR5T6DefPzjgGdf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03b906102928cf97c6ddeb59cefb0e1e02105a22ab1acc3b4906214a16d494db0a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "03b906102928cf97c6ddeb59cefb0e1e02105a22ab1acc3b4906214a16d494db0a" + } + }, + "signature": "3045022100901a161168ef69bc7b35610809e892d0aafe2b275f2f03752fe46d54301bd16502203b143cb62708efd18ee11cf5405a05023935ffc867c39b78397dced75aa7b0ab", + "id": "a9a246807ec148536909e633b7422dc90dc8ffe858b3146e0b3d870100ebc488", + "senderId": "DA4RAKRngGDfgEA7L6nQjYVEKcZPgi8EdP" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034ecce71a7690b1c314f71789fb32373afb79fef870fa6ea2843be4c54812b0e5", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "034ecce71a7690b1c314f71789fb32373afb79fef870fa6ea2843be4c54812b0e5" + } + }, + "signature": "304402200e5bc71301bef7a018e9715f18cb705d59253ecb9ba90dca9846272f70168b6602206d5cd8f334883480502e565b1602daa4c6168f631d449faae098e773305386f2", + "id": "e15317b81533925b0c39b50feb18b865f7122621a33266f606787a2a6e97bad7", + "senderId": "DLqV4kuQMq1j9nEFoWKEF9jPQQdTh7CNWx" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03f89a2f86fe683dbbe4856a43c4c326fb2938ae2647fd334ad33261c7c2dee265", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "03f89a2f86fe683dbbe4856a43c4c326fb2938ae2647fd334ad33261c7c2dee265" + } + }, + "signature": "30440220310fa69482a0cea90eb033c258b1f6f012799f76fbe17abccc36c55fb33b00200220287647844189029eed0f7b30e28759d346e6cd212cf1ed20bbb3457e066f8234", + "id": "b6f546a0017599ca64784ee5fba9fa524fe861ab1a9e54249d3a87c94477f5bd", + "senderId": "DMjBzD8BP4WwzpZ8yMVGHkr1z5jsA2A5Ze" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0349e7e2afb470994a8323e9623a6dab227c69d5f09f1a59991fd92880123ffe75", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "0349e7e2afb470994a8323e9623a6dab227c69d5f09f1a59991fd92880123ffe75" + } + }, + "signature": "3045022100b38c62db607d567ab0e9180dabc42662ad02b1ad86ec962af54085403bb4405702203b8b95a3941e8a825102a55af6c374364efb95f807cb146d318c2255379c132b", + "id": "1df66d5b41a604ffa298cac7a60a3cf69f8c5fb9f6e8da67cd6f8e6fcf6807c0", + "senderId": "DJic2dfPgxaGJeme1tQPpLuvkJMKo6PDfP" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02237692aa37cba4fe67d557973ed610ea175ea44d3f5cf4c3ce8ca7553ca1de17", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "02237692aa37cba4fe67d557973ed610ea175ea44d3f5cf4c3ce8ca7553ca1de17" + } + }, + "signature": "3044022022d6b16ab19faf9d9a86bf52e6cbe3568f2c3be19169657b49cce412b2287d4c02200ba4ec759b7d1e2fdd268111fea5ea49d54f2c5d9c04723e4b4fa64025362dfe", + "id": "3fbf00f4d88c94d736bbff3ef4722183c9b2993a0afc2b992a590b2cf93c5d64", + "senderId": "DQFLoHAkjrY7eqJrdPu2NdS8KgLQJSnP6Y" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ac8d84d81648154f79ba64fbf64cd6ee60f385b2aa52e8eb72bc1374bf55a68c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "02ac8d84d81648154f79ba64fbf64cd6ee60f385b2aa52e8eb72bc1374bf55a68c" + } + }, + "signature": "304402205097c9e4a3e3020f13753bd9749cf7263e8aed0165582f449da057a4f46d14310220699527199ff99af5ab3a81cc13d8100ae8dc85c84ddecbb2211daa7ccaff9361", + "id": "637aa19839b81f70c84ebc0e1611e18a260a55fa55af363873f7c2401a8ac18d", + "senderId": "DDU4aLrxw9VYJzrMTYtRAyDM9fKsqciiYd" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "026a423b3323de175dd82788c7eab57850c6a37ea6a470308ebadd7007baf8ceb3", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "026a423b3323de175dd82788c7eab57850c6a37ea6a470308ebadd7007baf8ceb3" + } + }, + "signature": "304502210099fb7d275d0e4a42febbec6279f853df99b6d4423df38df484511772e8af7e44022011933c855a3253eb1c68ae7d6c6daef095a93fc55942b77cb502f070d1559bee", + "id": "ef351a3237490b7b09d74f37340aaa8a589adee9ded564974ad230f7ca0e0aa9", + "senderId": "D8xN3Nsa3KfC3H68Ek9xnkfdSwzv8Kkh3q" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02637b15aa50fa95018609a6d7b52b025de807a41b79b164626cee87dd6f61a662", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "02637b15aa50fa95018609a6d7b52b025de807a41b79b164626cee87dd6f61a662" + } + }, + "signature": "3045022100923763d4651f207103a4c8c5d7cb40cc9423b66d03dc155b320fda25b6c24c860220088a9c96d50a8123856be4762284f0390dc0775f05a1bf40c54fe034c74c6cfb", + "id": "d1c6a20ba8d77777b0d3f48bd0c0c0b1dbca53c602a98645ed103ffb7a09d816", + "senderId": "DF2hqXQjZvzowY6wLxgwMeH638Cgfd9pGB" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e5359516", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "0346e1a1b5cb0720e863e8cf8a91dc8ed827e09fb4a4812f9f4d51f606e5359516" + } + }, + "signature": "304402202e2550948f1a31becfa76f7fc1698313cd597148ce9530fec24a03bc8b3b28d302203b75f4b837b83602acb796baebd684d8807140a445277728987325ca1d4f32a7", + "id": "0240a4a290d3973ebd34eee830567a6c8250229f5f3d67cfa7400787bfc5f158", + "senderId": "D5L5zXgvqtg7qoGimt5vYhFuf5Ued6iWVr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021b0f58eca7f123428a8647ffe0644a9454c510f066d3864c27d8c7ad8f5a8aa4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "021b0f58eca7f123428a8647ffe0644a9454c510f066d3864c27d8c7ad8f5a8aa4" + } + }, + "signature": "3045022100bd702d74bc7a476bdb8b2eeac513fa57a2a06f7a3726933c0edc13413c1fdeb90220014682b24f98f2ad79fd53913ef4e691c4dafb5e00535c1c220529bcaa6e0998", + "id": "4b4c0d5999f70244ff66b14e77bb6f1ae3279458e784222f9b8a81b3915111ca", + "senderId": "DNpPXpZrRSKsk9aiM2p4g5PiKFHxEFCtqb" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0335238d54c6ee73d79769d4ed43888d9b18e38c3594e656bb5a5544649d4f5ffe", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "0335238d54c6ee73d79769d4ed43888d9b18e38c3594e656bb5a5544649d4f5ffe" + } + }, + "signature": "3045022100d7dc0aceb038ca7a910b9337cc8fb325c9523874b4bb79d8491a6d5d413391c1022008c6f2b0bc8ade96163d577241437c6e1a376d36525a3739596cba2077abd1ed", + "id": "dd002a3a4f56a33c33cde898966887009adcf53f4cb84093d6a5670ed43d6248", + "senderId": "DLL6884s6tbNX2Lwbbsi5FNonm8GEvQ6J7" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022e13de675e14a409ce636706c76d42857c673d8dc0dda4e5bfceffdbf86e13c9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "022e13de675e14a409ce636706c76d42857c673d8dc0dda4e5bfceffdbf86e13c9" + } + }, + "signature": "3045022100a57a10ee4e79394818345347cf3a4f6c9e4f039b4fb5586da040ff34a6b96cff022078416a630aa400f9c988f8c43d4a5484d14bce536d91f3a6dede075896d32842", + "id": "c8b11bc8306047f9b368c510dbb6027a7c9b4d6df3c5505079ac334446437fd9", + "senderId": "D7u9gSS3KsykEoRys7DxsRNwHjpYoG8mqS" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "029a20963b506afabb2bd805830a46cef8d59218cd88c0dca9d2a0158045b1c3e0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "029a20963b506afabb2bd805830a46cef8d59218cd88c0dca9d2a0158045b1c3e0" + } + }, + "signature": "30450221009ee57a3ff9a8db841853411ad4068065aed82d35bed8a89f80995e70848f78dc02206a6628a2a8347a91aebcb78e1d575c0a1f8754103909a8a225ce3ac834a84830", + "id": "4f5a69ef4ee644cee1e5f8c0fffba9156d26a3c27ff88531a5c3bc53d77cde41", + "senderId": "DGKgCQ1srb8HZyr47RqQqMvGZ4cDyr4eMo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03f08761f99892996c6771761955ec41ee6cdffadd43171228f5f28f8c76423b3d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "03f08761f99892996c6771761955ec41ee6cdffadd43171228f5f28f8c76423b3d" + } + }, + "signature": "3045022100d65c0d76bf792c61a8c93070cdcfcc56330f1ffb37185774d04f77115c1acbc70220092dfa82e91e4b7f47a2a6d4ee57965d80ddc82fca9758250b22c4271b5ca303", + "id": "e36de13f1ef1c5749f697a04943279d8b398c5758eb30b7c8c685d45cd2da4ac", + "senderId": "DPv1wzB2xJtJQH5PaCMnw1V9mUBugzP8yQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03ea97a59522c4cb4bb3420fc94555f6223813d9817dd421bf533b390a7ea140db", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "03ea97a59522c4cb4bb3420fc94555f6223813d9817dd421bf533b390a7ea140db" + } + }, + "signature": "304402200a378960653a3d2f8b7959828cc6591a7e345bccd78bf4467e727841504f6dd302205129a7d4ff5a695ea4c05881824540753d09343ea591f8744d408917ad81465c", + "id": "c1da6ef5cb25b778cd8401127ee15b22e2e95cd37fe6da489e8ab35dd8138eb4", + "senderId": "DMDtE1ueKfVYHs2Kmxb2XepLnzvSM47fwh" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "027710de44279aaa41f8e75d5ae61a085020c414db2dbea02bbec0e0b1f27bcd22", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "027710de44279aaa41f8e75d5ae61a085020c414db2dbea02bbec0e0b1f27bcd22" + } + }, + "signature": "3045022100dc408846105dd8d3849aa24b5f8ed40836f17b1c5c0d8c82977fb278f8bd9d3702200b6c097658fb5b1030f1cdbc8c3c63d402b108b4236b2bc63ddc6c6be4a05633", + "id": "f9ef93a7ff6a96ddfe6b0f0a58f5c5bd9d906e6f7b5f2c8d1372fbbab37ebf74", + "senderId": "DNPBUxxGQUKDPX3XKUXa3pc4GK8yz7L97T" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02677f73453da6073f5cf76db8f65fabc1a3b7aadc7b06027e0df709f14e097790", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "02677f73453da6073f5cf76db8f65fabc1a3b7aadc7b06027e0df709f14e097790" + } + }, + "signature": "3045022100e6a5a91dcd3c70f3beb78eb15432171b000606557c5c13073d155422d931b1460220073b2aaee4cc7f6788b3b1781e881f20cc163ab7f81be9d32977e2eb194d9ffe", + "id": "de4fd6a03861476286161177b6d4e4b421871d8bf393dea05c5daf2de733de56", + "senderId": "DRW3wNMA4ijPfm7KA3XtupDNb5Hb8kL4AE" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c1670", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "02257c58004e5ae23716d1c44beea0cca7f5b522a692df367bae9015a4f15c1670" + } + }, + "signature": "30450221008a4abd4350b2069b2552e6b2c26d1d8fffc78914a071d98f1682de78497fbac502204b65e5b30255438e92d1980017c2fd5bbf3436839bf42d933d83a032d5b68f1d", + "id": "93288bd9dda27eaa1a395c0e575802f2c89a088c2166fd49e4dcfc8480d5812e", + "senderId": "D8vKwaX6ksU3mWg7tJDm7v1dbxy4cMo4dh" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03508436f55577f406be58a5e7e59307cea823943c5312d62f4e3f3c63966f6e7c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "03508436f55577f406be58a5e7e59307cea823943c5312d62f4e3f3c63966f6e7c" + } + }, + "signature": "3045022100aa88c4528c44c168fa205cc88b240ad73bde9bd0bf7b6c3607d15cd7dd1a6bfe022024c361cf430531ddf6345fe00c40eeb4f7ebeebd853ecf927f34f465ec87d134", + "id": "7d7418341dabf8406726f30b33d22db8a6e5713a36b87f4d5c2a12e44cae2564", + "senderId": "DKY5eyQUKKYyaCfPp6MUv3Y4FW6EbNF53A" + } + ], + "height": 1, + "id": "13114381566690093367", + "blockSignature": "3044022035694a9b99a9236655c658eb07fc3b02ce5edcc24b76424a7287c54ed3822b0602203621e92defb360490610f763d85e94c2db2807a4bd7756cc8a6a585463ef7bae" +} diff --git a/packages/crypto/src/networks/devnet/index.ts b/packages/crypto/src/networks/devnet/index.ts new file mode 100644 index 0000000000..18f24b35c1 --- /dev/null +++ b/packages/crypto/src/networks/devnet/index.ts @@ -0,0 +1,6 @@ +import exceptions from "./exceptions.json"; +import genesisBlock from "./genesisBlock.json"; +import milestones from "./milestones.json"; +import network from "./network.json"; + +export const devnet = { exceptions, genesisBlock, milestones, network }; diff --git a/packages/crypto/src/networks/devnet/milestones.json b/packages/crypto/src/networks/devnet/milestones.json new file mode 100644 index 0000000000..33f0c4bd85 --- /dev/null +++ b/packages/crypto/src/networks/devnet/milestones.json @@ -0,0 +1,50 @@ +[ + { + "height": 1, + "reward": 0, + "activeDelegates": 51, + "blocktime": 8, + "block": { + "version": 0, + "maxTransactions": 50, + "maxPayload": 2097152 + }, + "epoch": "2017-03-21T13:00:00.000Z", + "fees": { + "staticFees": { + "transfer": 10000000, + "secondSignature": 500000000, + "delegateRegistration": 2500000000, + "vote": 100000000, + "multiSignature": 500000000, + "ipfs": 0, + "timelockTransfer": 0, + "multiPayment": 0, + "delegateResignation": 0 + } + }, + "ignoreInvalidSecondSignatureField": true + }, + { + "height": 10800, + "reward": 200000000 + }, + { + "height": 21600, + "block": { + "maxTransactions": 150, + "maxPayload": 6300000 + } + }, + { + "height": 910000, + "block": { + "maxTransactions": 500, + "maxPayload": 21000000 + } + }, + { + "height": 950000, + "ignoreInvalidSecondSignatureField": false + } +] diff --git a/packages/crypto/src/networks/devnet/network.json b/packages/crypto/src/networks/devnet/network.json new file mode 100644 index 0000000000..6988d83b6a --- /dev/null +++ b/packages/crypto/src/networks/devnet/network.json @@ -0,0 +1,17 @@ +{ + "name": "devnet", + "messagePrefix": "DARK message:\n", + "bip32": { + "public": 46090600, + "private": 46089520 + }, + "pubKeyHash": 30, + "nethash": "2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867", + "wif": 170, + "aip20": 1, + "client": { + "token": "DARK", + "symbol": "DѦ", + "explorer": "https://dexplorer.ark.io" + } +} diff --git a/packages/crypto/src/networks/index.ts b/packages/crypto/src/networks/index.ts new file mode 100644 index 0000000000..06ef443a67 --- /dev/null +++ b/packages/crypto/src/networks/index.ts @@ -0,0 +1,8 @@ +import { devnet } from "./devnet"; +import { mainnet } from "./mainnet"; +import { testnet } from "./testnet"; +import { unitnet } from "./unitnet"; + +export type INetwork = typeof mainnet.network | typeof devnet.network | typeof testnet.network | typeof unitnet.network; + +export { devnet, mainnet, testnet, unitnet }; diff --git a/packages/crypto/src/networks/mainnet/exceptions.json b/packages/crypto/src/networks/mainnet/exceptions.json new file mode 100644 index 0000000000..5c63c31763 --- /dev/null +++ b/packages/crypto/src/networks/mainnet/exceptions.json @@ -0,0 +1,93 @@ +{ + "blocks": ["10370119864814436559"], + "transactions": [ + "608c7aeba0895da4517496590896eb325a0b5d367e1b186b1c07d7651a568b9e", + "43223de192d61a341301cc831a325ffe21d3e99666c023749bd4b562652f6796", + + "2d8ed494d852880eb674c279fad64468bfccadd79d2a63cc69a5ca80116933f5", + "31de62ecd08b12b897d0ab50fb8cb07cff149b8cee7116c17ccb8b3916e2eeb7", + "12be521f2bafb28cc7ada7139d424eaf0e68f7ac20216b5840d68509badebbe8", + "cbd6862966bb1b03ba742397b7e5a88d6eefb393a362ead0d605723b840db2af", + "4f83bc708288044a6f5f2773b90e1456af2ddc50ed8f3b63e960f5f16cac7d73", + + "e54fdacca38409781abdfd273d9ab11bf04c65286311b9c17d2f73829408b561", + "38ef7225c653e9aa9e78434a30403c379318bbde514ccb7f32aff72c067043e6", + "67f9eacdde5e4f8c90f430ea6bca0972984e50d3fc404c0d85c59a2aeecacda7", + "7a31e574c93c61aecc2801b543cbccdd1776e44edfcadbc08439e61cec68a7cb", + "da7590beca2aaa04df430cd30f1ab57fef2867b7afaf2c4aba09a8902864fece", + "a2743b3ed2556a31ab1ac0551124fd507b10d86da95bf3a742744a5e55fbeb40", + "8d150b3ea9b1bc3609e763aad30089f784fa3349369d62eeeb6de62a0d3ec56f", + "834c1cdbb55f78a8c390dfa28b352671a2ee452e69842c5ae0431b909b295ea2", + "1603029ad41e4cdf22a91e7650513b1dceaf5c27a854b9ff6b66f7f9eaa51f0e", + "0977bf68d7349b9c10160c6fe7e533c650bb593514d4289dc99f6b0f9f28c504", + "24714428cbcf94b4c5a02dce5a9ed22019b07327e71873764ec945465efb56f7", + "13e5f2fe5eb5197e50662de599318bac9ae364bf78896de356c40bcc9c6cce71", + "c41555d7dec8612966803135a55550c2fe0753219fc31f6b55e7d20250f33430", + "4bc5378f069c476ca9f7aead1592eed8664a381ec40405a12ed2c5ec1db734e1", + "044bc62e540079946b95d0cb43ac8de4d4ca0f8622871bca2e3748d3be3f7487", + "605b96925202e2726a69e4076494dbcdb790bcf97740580fe19442b3b14537ef", + "09de791caa9c19349b1f085659a21767709984d461024abccbd53bea0018edf2", + "886ccddc38e922d7f931252776178a684c8d170451020f238aefcda5481224c8", + "a53dbba9884500b254833d1b7bd3fc37a89c75afbd09df3c2de1e47a02322a6a", + "9a70c55cc803b3de9beb5e976450620f673f1aec49a71bb88e618ced2a8d1ec1", + "4496c966fb8025297101d12fbc03509d47dc949a722cf11783ad00c71acf127c", + "5290b62ac6ae61e0319e98a7bf5e22264e1e42fa4199bd00ddc9ddf148ea2ed8", + "6774f5fca7be329fba7d49ed88bebea42a5d2580e44e16a21c1c8d3677d7a38b", + "50c9252f5956c693fe04bdd9538ec2cbabfab51df90b2d16e098708a7e30e0d8", + "813fd1b603094159486f0a59f1648e8ada12f0654604b2c3207daa6c934c2752", + "ec952f0edcc10d9ba8a96b3377078995e4761ceb2483e1cb86db0a51f07c8abc", + "874c7b1b9191eed223cd3ceb837bd3b144b25725777fd0ce709983e50d304475", + "978b7e61409d333a1f8a888a0dc64da19d7940aeaad6bd26262ffabe7b46da79", + "ba6856d223f16642d916b33488a3825c61ad5bea92564896febafa44026a28a6", + "8c03b130bb847ab21b59d9da807d6043d33d2f7587134d32fcfa0c460e78b36b", + "500e035ff064f19599d41e98c2725687fb8736367f85080517dbc91bc9d2f25e", + "67456af6f26a14f6f6209fee5e9b65ec517ee6f8035853d26ef4d0196eca369a", + "c9f9fa471a19515e6b57ba4c22c434044041a39ebffca7b10cde8483d1f8e716", + + "4f83bc708288044a6f5f2773b90e1456af2ddc50ed8f3b63e960f5f16cac7d73", + "2c0f79b3689cb1a065aec5494070a165b1183e93aac9f8be6d6c393d71d04979", + "fe3015fefe5b9d6b023c922c00264c92af07635374865cd25f7a4b91188d5c47", + "65aa2caf22785619d1a0f53f2dd56baba47fb02a702ee330161a89c0f148b7b2" + ], + "outlookTable": { + "5139199631254983076": "1000099631254983076", + "4683900276587456793": "1000000276587456793", + "4719273207090574361": "1000073207090574361", + "10008425497949974873": "10000425497949974873", + "3011426208694781338": "1000026208694781338", + "122506651077645039": "100006651077645039", + "5720847785115142568": "1000047785115142568", + "7018402152859193732": "1000002152859193732", + "12530635932931954947": "10000635932931954947", + "7061061305098280027": "1000061305098280027", + "3983271186026110297": "1000071186026110297", + "3546732630357730082": "1000032630357730082", + "14024378732446299587": "10000378732446299587", + "5160516564770509401": "1000016564770509401", + "241883250703033792": "100003250703033792", + "18238049267092652511": "10000049267092652511", + "3824223895435898486": "1000023895435898486", + "4888561739037785996": "1000061739037785996", + "1256478353465481084": "1000078353465481084", + "12598210368652133913": "10000210368652133913", + "17559226088420912749": "10000226088420912749", + "13894975866600060289": "10000975866600060289", + "11710672157782824154": "10000672157782824154", + "5509880884401609373": "1000080884401609373", + "11486353335769396593": "10000353335769396593", + "10147280738049458646": "10000280738049458646", + "5684621525438367021": "1000021525438367021", + "719490120693255848": "100000120693255848", + "7154018532147250826": "1000018532147250826", + "38016207884795383": "10000207884795383", + "8324387831264270399": "1000087831264270399", + "10123661368384267251": "10000661368384267251", + "2222163236406460530": "1000063236406460530", + "5059382813585250340": "1000082813585250340", + "7091362542116598855": "1000062542116598855", + "8225244493039935740": "1000044493039935740" + }, + "transactionIdFixTable": { + "ca764c01dd78f93393b02f7f6c4f0c12ed8e7ca26d3098e91d6e461a238a6b33": "80d75c7b90288246199e4a97ba726bad6639595ef92ad7c2bd14fd31563241ab" + } +} diff --git a/packages/crypto/src/networks/mainnet/genesisBlock.json b/packages/crypto/src/networks/mainnet/genesisBlock.json new file mode 100644 index 0000000000..0398f6cd58 --- /dev/null +++ b/packages/crypto/src/networks/mainnet/genesisBlock.json @@ -0,0 +1,18176 @@ +{ + "version": 0, + "totalAmount": 12500000000000004, + "totalFee": 0, + "reward": 0, + "payloadHash": "6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988", + "timestamp": 0, + "numberOfTransactions": 1492, + "payloadLength": 313052, + "previousBlock": null, + "generatorPublicKey": "03a4d147a417376742f9ab78c7c3891574d19376aa62e7bbddceaf12e096e79fe0", + "transactions": [ + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AU9BgcsCBDCkzPyY9EZXqiwukYq4Kor4oX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ed57f27cabdb01f5398b30e63e3372735ee428e17e95de675c37586b6d1a5c12022062a0040ed189a4adac6c3d105e05180f7c74e8c68ca9912b3c60286c2226f3fa", + "id": "44d9d0a3093232b9368a24af90577741df8340b93732db23b90d44f6590d3e42", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AeLpRK8rFVtBeyBVqBtdQpWDfLzaiNujKr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022018618cfd5dd1024c0dd7677fdbddcaa6977b57f832eca130583d36480dfa452302202c067556fd93899fb0d18ea28e6f0276a778099cdde3d97d3bb8733dff965a59", + "id": "512f1aa00538b24a3ba55d65519d34cea83d753f5b2cebfd7004d5c0eaa7177a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "ARagsXvdeTHYghaQgJkwbdSkPLZ73qdMkR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022021e056a123b4a6c30e3f30dd68ff56f4cc1a994222cf27ff5b48434947e45f300220424cbc671a54a019cc655d02b2313a324702908a4a05c86bac4ac83029bb01ef", + "id": "8bb3997878a6a359f1418cf25f31c84f660e5e6897ebd6d07549ff6a4374a44d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AT9xWcPQ8hGYuXZ8aWE57VJFohyX1TTLkH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd0ab0bee79152978d8d5835e2d244fa159e4957f48d602c65e35e2383c0d14a022036380dac439784075befef7f7b14734f9ed782e4be5ac7f2f4c49985b08fdce9", + "id": "30cb724924823c689058c25243d1c213b9cdb8c157eff26ee9c89fc1e705fedd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "ATjjXXcGPTum6wogPVGb9pmimpSo4EDDEv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202e4a8dbbc11c3628f7aa9ef92825d84cb662a20e0af724d80475bcaf64416007022063e859ef5e9f9dcb294956e14d0680bad69641d1c254ef0ccc733f25b7814573", + "id": "69e5ced4fddc54dc4b688150e8bbb5bd3cb056252708e0d03401819490bc6125", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AW58iWw1ATvzGHu338WqMYvgUhmvMMsRjF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b1a8dfbf6c37a984acb364311e22336c367c5c76741a29dae4d5f894a496738202203e93441406fe1b23ab6d8807179c2cfe9e4c33bfbc455cc30733b5237af35d60", + "id": "62a85a7d3c9c31eafba7b39b102ff4594c5dce17e5c9ed38ef897ae0970ea613", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AFxqVbsVuDfnfyd9ciRAuGq6waR5GqiC5R", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ff14b9189e4f4d6199737c9b9b00d7572b2e9a5e1d1280b0bcb02b51b233668d02207a9df93b66088faf216bdbd60f438974dd5000cd1dee92a5a391294d717fde00", + "id": "c7b221db20709d99cf25d4a5b75aedc2644c963be994f040c9582c8ca84524d5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AJjkVwkhsvsX6dVKhVxpmhRvz4CyXLEvQ3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100aa43dec0f44ed1ae07fcca1bd4ed5075954cfe0a0bc6ae88f0a2640c9961cd1502205c750b5c957bb92e7dc032bdf01fdd2765cffa5dcdfdaec7201b8a2fd17e56b1", + "id": "647143b65234e1dd78f258cb32ba64008a1d23baea7739c02db82e60ce9147bd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AMG1Y3LP4kZJMAtoPhsnVkpsMTJ8nnCDr3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203519061e2b618b44181bee15f4ae561f710c6601995ee5e38149726366b3d14302200d0414c012b86429eae098b474acfa1c9f29ac4ef06d500eda8a9294c1ce14c8", + "id": "02dc2d050528586dbb9ec9dfe8a4118a21bcb3244d14e61a8cf41337925972a0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AVFkEkCmEg7cuCXoVrfvtH6mKz6XC9XnvV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210088d431420331051cc29ee00cfe3851ac4fed969c9592a81dd0492c0dac83d0ec02200f2efd316f7506abc7c60ad6151b00977206aeacc5a3a538df94fbbb88a1bd70", + "id": "1a3522ccc27e223953d3978b891e4adc219ab41216b06eb956b138c8dd532243", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "ANstQM4LaxfsuKtFMd8vqdGte3CL9s2vMA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b6e401941a798a09d5179752ac67c39af596f95d28aaded45a79cc8b8689bfa5022079ada29c01231e3fc2ad96050204047a7d0a9b56c56f609532bdb3d92b152e91", + "id": "574fbbd0c1f28cc923b03cd00bc9f6867fe246a645e3b261b6b9b6b041491c13", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AJqbav8MPPAe3zdzo1f471gaciQbx1SRe3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e4f4718c13b10300c478ea28fc4a6444f626fcd1fbe6d45f80504abd8f1e5ac022078c0fa5bd5701dc989ee3432a561e659d7a07c22e6a9eb198745d162a3eef958", + "id": "e1d3f456bc81aa88b5b9f790c383384fd24593ab4dd50b308a77a843e0de346a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AbfnTeGFiRM3m8eHcMsrqNvrpUCoCnuSzH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fb740effa4f878b5afd3bf8202cedaa7bc4c76b5a9ec0e1791824008b08c04ab0220340e130184ea186b9c7acf058b9cb62c77fea9cbd954d5e5f0d883da8136d15c", + "id": "85c540e0e42f449a14541c4ab66a667a64a18ffb1dfc2213a143441c6d21069a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AXArbuCVSiRuYBkzCAajboxCfNiw8AyQX1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022073143336b15ab2a51da194873e14a2e08da329a325e570d3d525c764a8e546b402207055a169c2cf9b1da89bdd40b1e9a19c579865cc19e2f5e868343f594ebd87de", + "id": "0d00b60f1a26a05d52432e517e59a6e42b1597ba16547f25d5b4cc5a4d821c03", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "Aa5rKoVusA5xiyh8Git1tJUWZE48ScbCR8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ea59db1db3462c4d08ed19920dd694e707181f63dedb48ad62991935f3f5da10022005d5981a84a82f8b7a881e50a07ffac814fa62d1052d43583de50e0ed62498a6", + "id": "bffa21418f3668024901a65e57f484ad45202ac71196e857d30b61c2a300ab6a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AbrgipEvLp1ZHNJ1GcWWAsCLNgSgDG6Lxh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203e5fc71ee49506af5218a4d6dd3870493081984f16234dc53339e6dd411d774302200293cee5f33cc54c22c766f420a0658107339905c6d6b745e88d49be6154be18", + "id": "77456d6e6fe11bd3fb7ec636e115dd6cd105cfbc97f2c9af37513f9f74737c97", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AWLoVgSScNNB2kecswuhbY9tcrtv9kf2iC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210089cb088a883d07418a6be847f45d2fdb1c8c09cc506b5ead7df7df3f54cb503202201b323319c4e8ae723c46886fc51ba311a722e25a0ed87a130ca57e9d6e725dd8", + "id": "79c7c34636d6bef31813438d5db5c2b02bd8a7964ec1654a2e24cc24563f5695", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AbyacFxcWS1JsokdRCx8bFsWP8f48XftmS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc987030f590b970eade8df22b44f13deb89a652837b3cdbd9274a27b9d97f9802204ad6718c262ab2eba80202503ef5cc9704c13b3ebce7e2e5e80489e783e34540", + "id": "8e40e07b53f46d8b42c90b18383ae19d5af69ff657c13f5f6550890eb65939e3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AYTEu82arYgRyvTgi7dbYwjodV7ignYucz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e30c20254d8518201f0a46ec347352159ae81ab2d1a47d582dfb20bbbfbfdb7c0220300c012e88a107f9268e7593f2adebcf7ab72f2d240aad37b58b139d02067f40", + "id": "23241fac8b9f633b6749f0b7788ffaf9ac8997168c55718d7c20eb688856cfbd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AYLTbdiYWHvPvJo5Lh42MEVS4Fnepg5vgc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207c919607f7d4b17b0dd54cd82843f4b4da3f5c5ea92f07a1794f0fd666f065f402207245dbc1d97ed651e14849c84579f11bee9dad72ec7b73dcf0b3d79cb7d68eaa", + "id": "5c78d1808a47333800b9604fc1ab2352bd847ea5fccb9f834825fee74e5aabe0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AGdHR2UZ3MJuaXWejKGoAycztyN8BFQnNG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6f1153d93d37483e21cbad1b6c07bebf95a8d13dca45e8c51008a69be736dd602200f81dbda9b257e6c12314bc3a20c5b1c8ba86e0deecde76bd1606f79e836e008", + "id": "fe843047a6d408de46081217cea6f5f197f6c2aba86bbacab00240f147002501", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AVi8PFHr5jFBFGWSissPFDFXUPABuBgxSa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022002a5daa6a6ac2cba5804e8bdc21769310f76fc6501c61147f9a5d97ff35737ac02202c6886b09e65cd90b3fb5ab5c906bf98512ff823f21cba66ee684c3e55e2058a", + "id": "eba8ca8a4c458bd3e15aa956a15e4b8cdf9336bf7d1050e9ab44bc83482005cb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AVoTzMjHaaWKDuA26ysXGU681N6Q2PQre6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203e6a954d0044affbfb9bf8097fb12fd030fa4d7b9f921a797ddbdac7d64d357002202404106e049c35fba4483451e9522ae46e201c916d50bf6971e5ffe7bc1f0155", + "id": "3c8b8785bb4213cb8df30aee1b061fa2ef0a515b59f05abbb0fc7e1c36f2052d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "APXkAdLbLiLJDC9Ls68Y9a5ws3PcgmUDAo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022022b5f2321cc614962cb4fd9a1eefd128986d39285b59f6e418e459139b07528f022050988e49f7d25d9a44765514d50765a0f43a47b306df56def037be36d9c97d01", + "id": "ab8a92d54c08815a6dcb5561c0dfa0e93b8552eea75e3feac096f5a5e4eff784", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AGZhXHXFUdvd7W4aWs1fYJe6XFUYeSWrd4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a66c0a231cf6f09d6ba27d7a835a4458edac26f7efce8fe1c6b58f80f88cd00e02207f92221ca0873e10a6415e62cdeb4da836cd16336715cc861784762d387a1749", + "id": "ea59d2aca71b241d063cc41061adab57d97369fe0e9e3bda651ada6c712665d2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "Ad8UiXMZPecuNGwCXZTmF8Mysn1TchVVTf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100af001967b21b5715f68047911026eb0cef51d14a8e0563e1633b2bf213c191cd02204b85d55c4b13255b488a661dba416f7208237dc12c114040fe54692f76a7fd57", + "id": "b8b63cd15f19ef5671309e8d571482537d88af6226248c2fa0e5bfc3750606ed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "APQgLh8MaztT1XuWVKb6d4FKM9HMmdmDVB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206bf4937703d0b2fc171b86ed8596ba5ec58e9282e3cc4ac269192ce670478a4002205cd8f627869c5071fb99db462c47655eafdeb1b991beb75bc48f62a56915c137", + "id": "e1a4a5601e46dca9c9e2a5a67655bf976764efe72e82d2cae07c043e46e29928", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "ANUfH6C3Jjp46pDUxWgeV6WUbWCagaVxM1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100acc09397ff3c0b38214df5bead49d458931197b22e28426826d0173594a75a0702202d22e93e84748b52edfc2e420ddbc6887b9cfae3f7b7b3d7781886f085b9618d", + "id": "e47687f406ffdc59012079854958532ae22512e2021990c0c11cede226c64d58", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AXuVpX3tAewNp5YVWvXWv3etxjrMDgaZdK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100df1255aec444d8ce3ac9cd459669412376eb92d281ec0153c7a525b43b2cd7ca022078e22a0c52eace076baf1d89bfd03771ed1b45dfea105451cb9490371be6908b", + "id": "a9301a31302384e0428be63ca0731b93ebd2a2c18534b2ce771525c4a0c1a708", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AdCa1oGLMRQqygEBGGq1AEtSV44CJ5Kbdo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220181358f06e1a2f2d43538dd72e7ec33ab64a39c92a512f2cb08e08fa32c3e2cc02203fddae5a4c9545093c9668c95688ec07b9b7ab1be27eb3efdf0c0acc41b2bc61", + "id": "f10ea8612fdb470c754f6ba5271d0b30c52c12d3a89ddffec0151cbc90e8d6fe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "Actv8ebcwNsbfx8MM5k2AeJidJuLL7PotT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b2c5014c3967c971f57c995bad2385f5d3cdbef34d43304d17be215984f986de0220080cdd2e5a643d7dc8fa9c299c5456d554a2d46713ce3a8901abd94455dd5929", + "id": "2a992486b31f723a33458ca055b878e51f1c13359cc4bda87509fc124d57c6ec", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "ANeLRbMxkSguPMq9CJeMgr8xZxwZ9mbqbx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009fddcf0b5071d43e7cfae4f9241011718d324ae16692ee674d668f09434041bd022050fd7c97e4206c2e07ef98032ed731593ec0cd8209a9debec36f39b153b752c5", + "id": "3e6debb349d1059af502804311bcb2cd37368d67b33845f9b50e4ba296d0aadc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AR5z28tRUnvaBWC14KSBpvNJDgsVCNtagq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220747d72aa2180e80d190ae4940dbffcad648ab743d551ee70ef221439357aa14a0220334be2dd1a004d188539af503467b37dceab37ec55ae5a035801746fa356412b", + "id": "7b97374ee88b881af118a757167c49ee0ae60b0ce88e377a200b015f87441b19", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AKeDRRgG4TdDo4Z2iCzaBZS2UcvjWKMSrC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205882a3aa05216dc8495784c9b842e3066f331014c8b55644804deccc10d696e602200fae518daafe7c9599a3e9a4d1ab8388fd3e5751af5e946c00a0d5d7c062f82e", + "id": "0d1a17ff619a2dfd5b1d2abe09600fd1a9ae1f373ebffb70bba1dacf7a15947a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AbkDPAUDsfQJgvHn5g8AisDm9XqLAtFFai", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022016ea5da49920aec7c20e293270fc5aeb99a7a8e6020daee092ed11cf48b01a3002205d10abf6cd111443e4ed7ae84a9f29791c5441cf99dac64af09611d6dff00dca", + "id": "4888da0a60c337f287a6526c9fc86be8bd85460ac1e48aa39b5d2f5c4a4018f9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AZvjdJSG5V4WnNjPaRbDNfLD72j3rYWqbs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202d089f163508a18c061fc42c70a79583a0081d9afb49dfa36b7f881cf73a48ab022021dfa71c41e3b7303297dcfcf63a180094f0ec58953f52e8b8c424251514f7f6", + "id": "be67562ccb6fc1d6a04791726fbcc111067d5d54076425b91380bc9442b31ea0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AbAeJ4YJU5rxuNtXpDn3E4W5E8UNHyc26a", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022015b2f957cf3fe5f690786bdec5831efe41b59ea82a357bc520500c77ab20d9cf02201ac6773a21d83bf1f0b744576b620838ad984e6954965db199fea922ca5680ec", + "id": "2101c235de152ed1a0bde3f57593362d73149e1cb1e5b4df855718c392ac245c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AY5GwZtG9sFvDzMKAQfAD1Q8EHDh4bW718", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ad645598de2adfa46f68a9c0b849457306a8744627c9d5a085238bcf51599fa022035823c617d15fc35bf7910a88ca45e47c9b3d2aa6c9146f6c1db2b92de0d3ecb", + "id": "f9aa1c9331948314027579cf07368180d49b452bb747f153c0f25c39328d955c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AP8agRcU4WmsYhC72pBqyfLwaDU6NYKUL6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207b203b7f10f7fcd1c29379603c765d37d9c1eadb787885211729b63606fe7b3502202862e0cdc7af6f3f2ae8d48ce56b9a76bd38ef3320bb82f21bef96c6d793eee9", + "id": "f6445a905a38bf0454327fc4cd9976bbd971994b48a55906ed3c123efc0f5370", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AaFxHxsjYyYCsZtpQwwYGvYESogJ2SHxe5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206f4c7616b756110ad060c31c2e4402219f77bd266bbf2c0bdcb4e8969cf59cb4022028355a38d68cf842b88b67ca9d9e261ca5adf017e84b2b5bf04e7adb6062285d", + "id": "4ccaadad7365662dcda0d68a13a18ee08174e2b0ce3638de5d45586cefe1fec7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AHmBqWJgxZfaC2azkyASdrbCxF6csE7Qxx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220088ea11151b787f4aac1c5bed478a83ed2b31a013942ee76f6c8b5a45dd1fb3d022068d99ce4db49d383fe1987682a459620153e616de651b45f092839b7afa4b29a", + "id": "600b4e8a38b45624d23f8f6a11424cd20a9ed956fa970cd317b126221c2c870b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AZengw5WND4WLC8JKz6xUDFwLz3yCKPpTC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022001c34abe513df6f732d459e995d5ab3ac6ce064213ba79f502472fbd23d2a41b0220799e96bdbe031aa06aa9fd14cb25b49b71244298cc04f5835fd3c947bcacdb61", + "id": "fca76de221fc1f70b642f30c4e5ea024f5ff886fbbe2f51f4ac7fac4bce94629", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AeLV4NUMsPJW1nvHquBWPFVKCWirs1pshf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d37946b7e677220e9333aa5d428e8b82a8553d2b3e0c1adbd8551a75112b54ee0220465fdfad076cdde91b5642305ee4c237c4093dcc210b9445c374660b1a11ae40", + "id": "6680f73be6c0d1f96e187440bf3575eed655477e15d8f1da85388453f5c9390a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AJrXd5u3y6FH5HktH2jgkLQHjgD9ZztMtk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a97bd4beb5444b5c2dea233a7dfc11c5ccb705a88799a7386f188a159477ef56022011875cadccf32228d693ebf24b6a1acbd574b615dabb2763e4a29b75591dc75e", + "id": "0520f4b24257ba8ad735613737ac0f0f2bd593ab5c52edcac746f4bbc8c635e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "Ad6y8ae35QWkrtiLBpiXRR5Cj2KK2u3EGX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b6065b0dab807b36139d101154d5e16c145fcd9ee807c06e46ba60e172efedcd022030e641f2c5ee29da508ceab6649593a6cb872d89d4661b3fb2b79f48b20c4ec9", + "id": "04e36364fb8007bfe7f85914c22cbe4bad262c12631ec4f339858d82213adc7c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AQaKx7UU8857b4tJij1jb7aURUzd3GDyKt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220540f725157e6fa9e1d5a7e495504b5007d9d554ad8f0c275e0e45a04028cf27f0220660a555068c7402672443beffc2bcaef8a85f06f9ed38ab558ede7a21e265bac", + "id": "3dbb56502f32939958b41aeaf84e9cbd4483a85b23ab32f979a3c1f0d683db10", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AGb36aHfdxvqbMqoDCnm6wVkKKtqkE3zAh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022063d7b93cfedf0daa9255bb1ab9a3955367c5bc8941b386f548fb01db85507bb902200cdde437236daa8fc1b396fd7de9294a9cdcbfdbd22df347bd635bd66581704d", + "id": "1c99e21a5232ccecf47da6b530eba9ceef87f02baa91cb333fcb3b8ca18edccb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "ARDkQVS4DbJnErrptyWSWdJD8gtaPEyRH8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c1b92b570f6abf42650414541ddf44693c61835816883bc2de0c4e7997926640022015e109e9f622844d8930a8e497ea2af2fae7089a4a110ff2d3c6fca46b19a70e", + "id": "fda4e22bce9444a6bc7d952c0cab822a5408ff74a19aa8e6d47b61513e061bd3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AW91q3n1QYTn3caBm7KR35zexcJU7PgMtZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009010a3346835bd198bd011a5a3cac7ef94685c96acd668ee92be386e9b267fe2022078a272a9552454e80ff83dd2a01b3621fce5818d71fbd6430e0848ba85cd2c32", + "id": "52cb2975b2dec5cd21beac470055a254a84169e51b1a72387757a340509a5049", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AYaYHEKaJvLLWX2NgM8VXk15zSDxV8vCgn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100894832a480b4ce89972badf315ba89a21f9c200cf83dc402dfac475107197852022036158362b58228e69defeeed4035e9491f20e38b8435a4b8afd86718906ec42e", + "id": "1b693cf3a23efe80385fb0f8f2b7e1ec123af4f5b99795a154f7dd1aca505950", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 0, + "fee": 0, + "recipientId": "AGNMmJ5upuU38ucaG3TUsG1ESaQDExSMo4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e2d35dbf837de046e351c2bdc8a7061edca5af3cf01d8c47ed468225a85f688e02202cd057264c2d6f17a4b0ab9d054de7e6db6349eb1ffa2beecda58960ce80b4d8", + "id": "509e2950f47ac036f4ca1545f3f3e4dee77db3756682175d292558b920948579", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 128537390, + "fee": 0, + "recipientId": "AapqpT6xF7q1Enu44UkL75c76uNuWmRWkB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ba8eec5c24356ccc89437cfb53440d3db1929fdae0133a4744a4fc61ee1c768502202775dca5d3ac6c2c3b6d5b72e58a975cbdbdfeacf31f26d46486a420fa12bf9b", + "id": "6300b6f026c1e1657f7d7328bcbe58e5e1c6a8e56eb5f1cb1402bd1225786c90", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470150572, + "fee": 0, + "recipientId": "ARc9Qz4v9wNbneBSNk5s4M1RKXwoMzLiug", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220084a67416a41da67ff634eab70f88443a7f3d531976c84b310a07cb0a6ced39102201f26c0d886785aaac15d5efc79b9545bf8da36ec4b4a491a2d33a937a97bb9d0", + "id": "33c5449bf830f309809f67aee50bafdfbabc3fe284aed0d5a84059baec213843", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1939749374, + "fee": 0, + "recipientId": "AcE7Xh9FTaMB9pUKQgST1BHfXnpmganLAN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a3515ef9744947b6b3cfcc3c6bbc6ccc05b7c158ecb639b0a46b0c5d38132b3b02202d2c20ebd19a0f60da85961a6dfcdcc905858e11556a0fbe3f37a36ea286703d", + "id": "5359b1e1068625f47432ba3a1ac12fda7411225f9be163d7248c25a92080c496", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2223350287, + "fee": 0, + "recipientId": "AcGWNuiBErRUP9MeXGW3yUQrhQTNKh4keP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207d9fc26887b0a6ebc7877d0f96cd49fb6f51e09c5f7086c8030740bd19c7f8200220229ac976a89a3f1b061d7f833c916fc76da131629d0373e20f6a10680128c246", + "id": "68e9ebe8e03c06a773223752296acaacc47503411398d75ce9aea8d1809900a1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2635948642, + "fee": 0, + "recipientId": "ASHDLexaM8z53tcrddDC9qNrG48KiykCFj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220390fd742fd9ad375f06b2dd4be9fd28976facd9ab58a3b497f9c53627c54903e02207a13f2cf4b2e35b512d9f25344d7cbaffa7533a841b444be4131ef3ccbb9a707", + "id": "140dad637fb5e85d9bb48885be2ce2caf63599982325fceef875f5438dba6379", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3319628053, + "fee": 0, + "recipientId": "AKoFbGWi8ZpD1FKLtWbs4PFdR4H5L9mNqj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fbaf95f5cf4ed909feefc9b0d3d82a27c6281837320174fb9c1cc65c6dfc4b3c0220571f50b5ba6c1b9effff224d5ea08a1dd79753d07487694897f5b053cc7bac56", + "id": "fc4460c527373e2cae1ef706aeb846d0d011f5fe3a15d5b29727ec6d4a65b378", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3500000000, + "fee": 0, + "recipientId": "AcT6rrsUh2T23TXVgfXxXLQKmpRbECs4N4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ce9a0287b36c323e4dfbd845a2d0bd30cfeadbaaa4314c5055d3f4194d05aa1402207aeba8bf92955383a8bb95ce75628a258c5ce379e3bb482787b66db194bc24c6", + "id": "255952a161258cd10a8e70bb03be46b8005101f05ad0ad453e50bf8b5f1c39c1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3500000000, + "fee": 0, + "recipientId": "AYxobqMdZyUFvvX9o3ihE4iMnZWCzQBhgs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a8ba213f88717697168ef6647a89ea7d7d1a7bb56c8b82a27f4019cb351f13b502207afb90964461534dff853f2bd8a55e36fe6660de37b0864f65e4220c45cff2b5", + "id": "4a761497352e5687757421e2cad81fd7881c45395fafd33fa5d4266d98d3b40e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3606920711, + "fee": 0, + "recipientId": "AMepHWLBpbShRDbwLcEJhyHRatqGAMDa6r", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a35fee7952af116cb4ec7b4881f9fe5056371287ee26034206d964171c76f05d022053b2d4cf6bd03c89db073a25270ce48fda9b1c98a6cf19136291b912040d8917", + "id": "5cbe722d6e53c668c603db420a577c8a88f2c57c838141d7b0d336ecf4d67541", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4373745201, + "fee": 0, + "recipientId": "AXZCfAqHYyFUpYxYgNyw6W9XVngp25rGkq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d430485cd384e4a58efddc72ce62e203a5ce80eb29699fb293b7d378521c932d02201e0d8cd73d3c98ca4973fe45ca2d161443f045125c971304864f9dc4e7d47645", + "id": "4f29a835860ea8533c479ecaac35bf14af9c12689a4d40ad8f4b07e55efe9a91", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4410451715, + "fee": 0, + "recipientId": "ATyCrCUvZN7hJfkYWbCXgKfEZp7PB7ez6V", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e90a709462f9654e698f131cee2fe1ae82852e12909909c32bb4ad8883e5e764022065c75a2511725d489a625ce0f4321119c43d2df7831fa748d788c879cea94eca", + "id": "16f6d59d7b48c528332ba917f7710f5af66dcc647a0c129165da7508aa93e826", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4410479871, + "fee": 0, + "recipientId": "ASrrGt6HcDBYe6hTqX2p4cD1AjNfiC3vnv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f42315460ff894d560857f7b863e22b8e9b14bde5a86ccd34c4189e37e83a8ed022036ad285ab692485715db059896f7d394382dc6ae8386627d6bfde5771a01279e", + "id": "6497901d7e0d33d9ebc7b510100ada1146f9be06e960528d789c4d62f1198855", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5445780417, + "fee": 0, + "recipientId": "APRgb6yUmpvtaCyPmLuSzXPucgPiJ6FRpJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cef35ed420e2dc8c5f0de5911474f0b22c0b5f0eebf9cfb453f6832892b47fe502207f52c64e0bcb8fabcfd641a8c0a680d6f6a80ce47df726e276f620131f681c70", + "id": "1ba4319ef107661772377dc6587a13c08493dc0a02499eea9f69de567fce243c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5549484283, + "fee": 0, + "recipientId": "AQqQZPw1ZNhRqmbYfVisYc6muRF1TrUk32", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100aad3e80ae4d92622e819f38872ef85c0edb22300dfbc3fe8ec67ce570c0fa45a022076ce462d4b9cc5ae6340eae28219b1949ca4e5d70eb01fb70c92d759513402b6", + "id": "ddd800bcdce22996cf22036b8de5903e7f5719c5aa9142e4aea0a79b25837df7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5618558679, + "fee": 0, + "recipientId": "AXd5pgP55H9Xd9i6ErnzM3mWpZ8hZpYaCN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b5860683085463cbb8f828e4749c08b31e1c8a0cb5c5be8166414c7d38da33100220053a0efa7541a6a63cdb752bf3ca7eff289d4314c01c1215ee86dddd27d0c68d", + "id": "12bc04a7470e5929cadeaaa4f1ac786861eaecac4f6792b38b5bd6a8596b6395", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5677245896, + "fee": 0, + "recipientId": "AKURdwRzPFAS4XhPUtBAqKrTrLu9LPqFbj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fcef07fa5b582bf19686f0df769c110a74e2503fa653475b82f7f5e6faa3be0b0220647b219bd0b2163b6fa363b62bdfef06f2df6006325b878455f806dfd44c09e1", + "id": "2436864a65bb862ba864aaf5e6334bba91a7674a032abd2ad9f2a3895d08309b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6558699223, + "fee": 0, + "recipientId": "AaNyZo6YitELcMW9SyWXiG1FVTUmrK39Zc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022078ce2baec0753ba02c05dff2efe59dbb628cd279307c7bd7d884776f2c15f35f022007f3cfe57778813c908496b147ada7c6d3531111cee9fb56053d09099a4b878d", + "id": "64277b25a02735c525a4f11417772288ac3e71f0fd75c8e00d6f40e6a2af8cb6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6900000000, + "fee": 0, + "recipientId": "AY3adwSjUDSKysWv8Ym4UaGYeqDx9V5a1v", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200cd7794247be234bb63d915a2c777372a89d0762c3c5299178bccec15b6be0ce022048130b001b7d24136435c148838e1caa6b176b98a0fa052314e408f26f81db35", + "id": "644fdeb9e3e870bf0e6558234075ecd9be5addff36f9fec81c28aa7c8ff57a86", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350752858, + "fee": 0, + "recipientId": "AY9k6pyVCWzkCNdnfges2yjGJVJyhLdYw3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220216dd68a632f1f0846cfb628921df1ff06772d478185d05c20986ba09efacc8a02207ea5c3932b37e98b491041ad0169554e3145e06a03088ca5c4e92a6fb184c6d4", + "id": "00cdc908312961a8350276e109985ee2dc325a984d92d8ad64d60dafae5e1ef6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350752858, + "fee": 0, + "recipientId": "AV6aU7fieAyQQhMpjAdiUPF8w1SMujpVo5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f9e222aee99a87a56193fc9661fcb15754e43c41d3a37a1104c063b8e754d37202200c31da497f5e8e84069c9265d816d5ea8c80591836ea68ce94b4cfad308c619a", + "id": "71788c698bc0997546b1c9c2da21cdc2f391a3a295193c61582cb5788571c5c4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8174205069, + "fee": 0, + "recipientId": "AWhKaR8aPiTxfMaMypQqt2LtfDEUGpB1kz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ce4bac79228cd04ad09239a51a423a4c0e3e0aa48feecb5de9d3ad9402b6678b02205acaba891280ef0fe4c1ea18ed56d6c0693f5a0d31ae9fd00e266e3dddb0c5d5", + "id": "586cf12649c475cf490b6f7ad6bda018032da30e87bd257ebe2b222391e932ff", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8335753741, + "fee": 0, + "recipientId": "AW1BDJsPkEV6AUfZtk5js9RKqHYfjEnuBH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009c055053f467f1571cc395463e23f7225580fbf5814d17756a7a421eb812c709022011f5a333a164b685998de4fbd5625c55a35c4a35e31643f8653b74f84d488836", + "id": "dba64b2e7c7f94a2afc083b73fe947c069070b534e442cd5958361ef4b22880d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8793099579, + "fee": 0, + "recipientId": "AH2QuDHZFX3rkLFkFTBrRshjz9VAaDVu2z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022075820ff7058679540c808d1fceea1033fef3c80fdbdc1f7409ad40e9ee22249b02207a60b839bb427497387c71965871304b2418afcc7aef71495b3bed16bbb87445", + "id": "fd79cc4dced6ab4862faf2c0b5cde19b3ecda72ac2453558001cee1e5f90af09", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8930739717, + "fee": 0, + "recipientId": "ATiYQXzewAtwbKtNb8n5uoRpATXvASGu9C", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022043721eada610fccc21dfcc12eca7604854d55ff996a1aee8ef1b6ed6dcfc9c7c02205c1b63a8dc3317a90c41366477b12e6bbb4b26aaf1951546b956b2a019849633", + "id": "4b309d61a46af825c19faec1e116c949e0e4958bed87afe0ef3c81336e269169", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9285850665, + "fee": 0, + "recipientId": "AL2DsLKiUjXFKBbByaLP1RsdUMeNyj8Pre", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207310e88bc29ed44d42c60cc2bfd8cd0cf41751a7c44a72d47f0ef597a312efc0022074f06ab9ddb0dd66b6095f8324a65d8e57841db18df7569427e3001222bfffd1", + "id": "fc59c5754fa17a47bb34b642ee71b99fff47e1cca8d877788490a6401c1f969b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10062477006, + "fee": 0, + "recipientId": "AQjGegCDm4ig1o2hAmxBwWYAta74ZvS8f8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022035956acc21b16b19f446509f805fcda6ec6aa5bb09803e9367e3459b05de6a630220310de9e98092f88787e9ccc503bf67439dcb47e4516eeac015ca5064c6c8230e", + "id": "96db83cae120bcee57cc27436207abb76202272b558e5fd7579fcda5f5f9f61d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10090126421, + "fee": 0, + "recipientId": "AXhCBxMufzWdxQoGQi3ZPS1SQokqHr7SQ2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202cda06e19276c0d76680c0fd5177c8e143da4863cd47f2ab4a9f9634abf0b28402205e278ca769cdafefbe74862d94df273382ba379ac0efd90c306d817951d75703", + "id": "f5a2f41b66cb6ddcbc7d68cefea3f5b884f18c2e981eeb8731280dad92d781e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10400000000, + "fee": 0, + "recipientId": "AbDSp8xeAfoUR6mauGYE5P3JTDXXnWYmjJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220684bdff8dbcd3dbee5bd9d98d630db30d6e285f82f06349cd4c85500d4e20fe4022049809890d215e9041fedaa8996dcacf2b59cfa949ba44d5b19bfc0aa3d993f77", + "id": "6bcee0de136de52f7fa27f7641d3fd661bdc17069f874c189b09593936b9511a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10469786602, + "fee": 0, + "recipientId": "ATQACwzUT92xq8ftk83nm9gc8cDELLk1wt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203261caf9bd95edc131e4c18f7c0fd985d477314f70b85fea340501934f6968f602202c0c225421471e7ca9ba8e1bf3fa38ee5ab35187ceb5f272c6eaa36b2fcfa127", + "id": "8a8a7657c453a4eceeba12235add5d02a27dcfbd27a82238e586e673ba2d8dc2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10792241696, + "fee": 0, + "recipientId": "AaD6oZFiPZaWDgAz3NGdjASA4Lcw1n1yFB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f80771b2256f6e642bb27369d356716861ac83bc0d779870055e53a362bed01402201c5e896aeedc8acbe608ec78f14b87c623069b627d5470d835f07db98dd3394b", + "id": "194ee2091f2dfe779eaa985ae67489ef6f4caef181911050d2d3615292eb9345", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10860889308, + "fee": 0, + "recipientId": "AQr31AJnzbHCTxtVgbguzDEL5VLHUqpjhb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ffaa693ebc94e479852ec7d47a90b96f82eebaf2c00b73adfee062ec8f764851022039b50274a148a43a23f7744c30cb00a370ebbd5d39a2e8aff5d91465f3b3a95e", + "id": "177f350bb7801ca83c881fa56256c9f9d63dd8b78d508e67ee74d8455852eae8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 11300031743, + "fee": 0, + "recipientId": "AKgJL6ko3BvdJXKdRDuZ4f9kAdvmNRiq34", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d2012faa0994f60a9e9ab7049c703e223de18e4b89fc71b2204fe73b17af9708022024c5294f0424a68afc1d2975f7d300e35a4440b937f49684ceeeeb64277cc0cd", + "id": "b513d351901d269acf8389af328ed20dd0146542f83b80cb4c3ba8f8a0fb9bce", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12028504677, + "fee": 0, + "recipientId": "APg3XHNRy3pxHmg9jnbb6iSqq67dEyeup6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022018b80f68abdc7e6940dca597bb8d24c3a6150c3b6cedfa85f13f3e73aeefb720022047f45325bb01e0d6b9f300a80b0b5b15d5f9f453b646dd84371cc23ed63186b6", + "id": "e19f84fd9f142d50860f6725a79d8c1a14b90e19301ca793ba4a1d13bed8ab22", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12416758078, + "fee": 0, + "recipientId": "AJZA2GSpqAAjxCBsQS6ASjryGwFvEDz4wY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022012701d828ce4db33b2f03f4dc42720189cb3e0aa82f48ff8e0284b3a4deaa0b902207a1685bd283e1c51c42002a8041c31ee518307c4b86c7f22c78acdd863cbb4a7", + "id": "3c75b71a126df25c97986d07402e8b153919ed29dd8e86ef7b5d232dc4c27141", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13626793145, + "fee": 0, + "recipientId": "AHUzDz27ouUBRnBB8MVYEAjaEkjzfYxAZy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207dccfdaccd0afa9d7fe39da6737f1fa1588cfaa83011f9913e46f9e0bc2d60f5022030b46c49b11c5513d7041963c76394f6a643f60fc9f76af2549ea72758d0d33e", + "id": "ff635c6f6988222c2dfe24ebecd0ce33f5c9310dda198657343c16d9c2c73163", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13900000000, + "fee": 0, + "recipientId": "Aezpc3WQ487fMYKeoH5CxF3X1BQpG3Z1Tv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ee21909eedef0a8e7c587263c5a6d8430dc487a1b96ed2d5c662e69c9cc6cc610220341bac16dd02f854fc19f9f3af37489c0b4f4035bf309bc6ef81f22997a3b814", + "id": "06f9cae8f3f46a0f3fe4f1b53efe34cc8c502bd1fbbb2fd15d52d8d0cef3846d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13900000000, + "fee": 0, + "recipientId": "AUhaVctz4y38gYtk8kAH5iiHsUZJyiV9QK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cc8a312b243e021212b170071f32ce4174013460e275166c9f17020e769de2c202206b71d09fdc88f4ac32677c19ccaa21408045e1756ac5d7ab4b94352dd21ed038", + "id": "e5a8d56567089b515a7878383ea67dea1690fb4624e8870f566848a28fd2467e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14407475602, + "fee": 0, + "recipientId": "AN5fzWHTJm6LuJjU2iLqKyS2HGrz7n1awJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6203ac00161224464cc3c4ec7df00bf57446cbc9b19d96b4895df4a550bc126022059ec4181988c1f19ae9e790098d17bafffb2668d11b4aa2dd6581abf4516de0a", + "id": "817ab520c018dbf31f24790d8ca23e5f5c1f6e598cf204c58427289dd9c7299a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14554490659, + "fee": 0, + "recipientId": "AWQzdRCybJwnWsJcB4Ux1ZWCcudmuZj29L", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f61c81d36b176998d3c00b6490c0ecc79726693b04f7f32bdd0522076602e99e02204d8bffead0b7adc26cb5245c5a8de9db29909a4982bc1d371db83e69e005864f", + "id": "397f2509d05348aa49de4a15f4d3adec5379ef12521541ba3388d2c73d45ed35", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14561654186, + "fee": 0, + "recipientId": "AdBh5du8W41f6JXJxFVmHE5x97cRG9kojq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b15dbd1e14d3a4ec461cf52edf840144bb0094e09e2a9d1b91f9a9669f6b929f022038ae77df2e975e856a57f656bd04f4fd8b08b11166503de53affdf50da0891f5", + "id": "39d14c1d51ac1509fc9dab4be8b3b40f540c12b11392fc7fb45ed208f0696ca7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14701505716, + "fee": 0, + "recipientId": "AaoM7ppRsH58K2ABYH1YxSyBKW7z3FrDpY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049720a06d232f1232cd8f09e17128721f32bac33a8b210baa322ff6bd813f5520220402aa73d01e414a5953260b79946172ecb53e02326c9baef65c0d6a80e71afe4", + "id": "6d93427a7be1230f2faed5c635893d0f7e376b9bf13fec2ebbd7d6d314bc7e7c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AM16EJe7VJpQtFPkoYLyatgNDJmaoNW1EK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ab3dded9d9a320de89ad11a137ec45cdc6caa558b1ed312c2cfab6a5e73028df022050adffc944953f798ddb791b9f233e1503856f6c16867e06cb91fc18f503685b", + "id": "a9e8e4e49962cffc045abba3b6eaaa77f2955daa6c3b2bb62497f861e26c5b04", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AUPFE3QxTSpq61H8T5pumPcZNBtxbPSCvr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc62bb643a1344403854746bbae7bffc44228db69528c485a1bac9b44051160302207628f7a3ca9b230a77dfa13714572f0f28d421356288fce43d695753077173a2", + "id": "c4ced95ee40260f04bb935b7709c4a19c969b77311dcc75537165c30f43793a4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "ALeKWmZh3XGjdgSboRyFU5p1k1BQpuriwt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203abcccd1b15a9857f111db5dfbc65135ae361ff3548a94cdcf9d18f38f1ccc4d022064f4dfbb1a13e532ea2d629ba4a4d5cc85622c18e6f5fea4c5cca9de6e8aeca9", + "id": "110689874c1ab40dd0cdb8af84630a1b89074c4f751e662105017f5e351e1841", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "Ac9tpzJ5h72V7rCobsaLxeC2Y4aHmki9EK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200a67c0321177682ad46b56c287807a6c6fb3c68cb35e7fea4325c26b1ff3c3b0022038d12784c4b342b950f43dfc5592d57703c33e331e867a33fc0bf5d6243406e3", + "id": "3293e055b85c9c3a34f2099261032891a326cbd39541a80dab24cc4c5efff8c8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AZ8hMVpGVT1hyHt8nonk5ip8fKFCST7KZ8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e1d4503779cf4c4e9e90579b2de7eea38a344ae1d1a5be81961309118b13ee7202207b977b82d13e913e0f4f9f37ac713b8deb3ea5b752cd3495af4a00a99c62f853", + "id": "b1d086b1b1bdfb25160c121232d62f2246e0f2125f557f7657439def1a5b473b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AXxBftbe7ARvVnaLTpXyY6bSWSnL1b7iQw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022057b0ec2903cf6d7741db49ae7ed7e6243e2bda73f878c5ff93e78b9e9941169402201c199cf06a81408eaafa26c8dc6195f243fbfd193076df3ee362e502b555ba8c", + "id": "5555d7f9855671019c47805a4aa14c3054fd4aef01fa161cb7d7e3fc79388b0a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AcB6nKH6ueGiN1ygXurgkdXUVTNGPrAiVA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b9a86d98d13a708b647d00ca9a28732af3707ed3ea34b173e66a20340666eb9802204de9b88e185c87055b454f146b8472d6513883ff065ebb251c155e59ac56ce28", + "id": "3922852d56b26b389bb1cbcfaf3235d2721a9e65e72185946dcbb46e09de2889", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AZJByMD3mzvS1ux4emnxrJbYAf2mEsS8bu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e7c70ded41dc8ff70b306e5fe5a01fcbf38057d2a38875e9618deecbd5e1bef40220031687e7a7286f4856e056fb86a600c5fe06cfd05d1373ad451a8e33c3d7ffb6", + "id": "dd619abb201619f8e6361f54aff5d95ab8a45f1b75c866e86a68b4319094bb01", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "ARVv9D5LedrdyjyuLPM45ALNowdWZCu9Ww", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022029337e12c30c5a1f3ea0b17a53dcc263bbb88343f140765e5dd5b7661cb7249802206c50a6520a2b47313a614d9d657199b5bd22f6a58c10374bde951078ab66bdf4", + "id": "271e64895989fc542cee02a54399c1e8dfab53f596658a095d7f3818f9d147c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AJ6Swgm7ABDGZd7vdBuAv3xHL64MGSFZ1d", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220640d9872265a1f6b0649c593e9850bb77023c1122e0483e03924cc44d8421cab022063368396d31392bd0fce5b5aeb2d0a745a4462ee3b47bea7499a8bfdbab3e218", + "id": "b1c51b691bd763cf532b060f04238af305a3965f2755d3b8295c1145c2c78acd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AbiXRAWJQvtxiEMzVZm45Jj7dTYF6amMx8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008a0d3a40ba64688ce776449872561a1887d272b885ed9dc77464f5e0e9044ae5022062db52bcbf422b12bebdaffbf4f207f0240b803478764aade7c5440b0efdf413", + "id": "4616809d23f8caeac00a90f33547db0d3c31b6d21dc0c6d1223a2f2b6f8acb9b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AQd294jzG4M7snWPK4chtyrKDTGBN3xU37", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220073c9db4fff5e1b5d59f7b36a29d250ccd0c352d8e30f42d641c0aecb9fcbb780220304f3f1237fbfdd6feb055a4d193a2a24f3d78d36891f86c752059704cb87d84", + "id": "8356eea142b2f8dc858aaf9ab5038dbfc8f71f6a6fae25eb0f9a1e7b0c9c02b8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AJ4WmzahkzX1mzQGQ2ASfXUwVNsTr2nK8c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100beb99fe85584c1db0e164c49ad211ef99e09b62fecb4ab5bd388b82f3069a3b602206657572320712fa459da101ea422229843f65f7c63cd0f2ec86d49aec3895cb4", + "id": "d3e6dae3d4d3bcad431182bcac5dff365e0ef615c1bc44be29776b9b62eaa847", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "ANZyNXsDsx8pCWwodv4VBp5vQotCgFSLpQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c1b2099ba3d7dcc366b013be5a09fce01599bb707d25c8ca4b8112c1fa65eb1a02203468b56d9ed1247cf21bfa67fd83307eb6c78741f51f24d5722033249193300d", + "id": "eb1c6fbc04b75fdb056432946f90715f02c4a1f3cbba7a1da1e11cf923d9c26f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AGZ466UoZ8rmMwYqCobkJieDn5WnmuNJBT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220115463a85eedde299e2181968838add9326a9f5f11a066e2de1063372d96d2b102203299c9a0998b75353b474a383f6a8ee5636964ded2265284924a1810dc8a2e6b", + "id": "42362155399528b008cb1945e8271cda6ab8232ac38c1a9f1ba092722162d5ef", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AQK3Ew5GGnHTyrFyt4fZRY6xobNoiWAa9S", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220232b18ce1bb8fec9f105de89b07be45b4851032fd57adb11227c7b408fe4910002204fc98352522425910dedc1a10876da390acdf2737a3efeef36afc0b77c26c114", + "id": "01899d20bf6bb2163d05c2dfb1800fc9abfd8fda8c708b313dd38c44eed60e27", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AKLLCyWEnnsXPLxQ1S91J76WXL2YsgCxo8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009b8269539e07b9055f9cab99b49218c0535124d99a366cba09a674a31371e82902203b6b6cf090facfbda718b53f181e046bab31040ed1a6bcdc702c519b2fec40df", + "id": "053d16a85becee53a301904de426d478499b1de157a9f15db98f91c4783173f1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AZ36zcRJmdUJ64NvN1M2T9pzv7dLAWtp6U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008a664c0db5cdf291bb0da5dd05b22a539f8b538286650c2f7dc91def82ecf204022035033583b9bf16e4b0e3db65b4125c567e0591a8c69b5739ee158b55f8c842c9", + "id": "968c02469edccfcc11797a12463cef24aa7ae281aa648854fbff46b6634b5279", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15000000000, + "fee": 0, + "recipientId": "AYhCgzBpHXC9kgRCu9FUyRpjtqvfqNSr4o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f207cdc66a586a04eb9e7a5c777c4ea37434b73dde96b05a0c7ac666064d69c7022058eb07e5f8adac444c5eb113d25d09da6fa42e9f7a3a5ae778f479c953039610", + "id": "67b0005a47824abec015c2799d99ddb2036bbf932a36cf05db7575fbaee92098", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15155129039, + "fee": 0, + "recipientId": "AN5x6WfeFwtvyqDvpaN6C1MusAvSCzZU45", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f2577c3278f2aae85a9cbce9b581c9c094d2f33b2e8173ff6b4091a12008da9a0220753f0064d21a0b871807911194832c3c255060f3a6dd7730fcd6538de884392b", + "id": "c2f08ecf56771833549e92030bc839761e353fd88330cd7e414f9e5e33869b99", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15155129039, + "fee": 0, + "recipientId": "AcgtP5SQDHp9gjDwe4KPVVE83JLGwWGvUG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210096dd68b49e599249c0d35342a8375ef543fa62670c6f67602e2415db4b788cb302202cc2edc7b1b318c4c127acc7fdcf34c7a5385d078a683cb6f10593272091a6a0", + "id": "887cc5bf0335496a821123b14efb9b9e989645f90911c1f7fdb9f44924c89f52", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15155129039, + "fee": 0, + "recipientId": "AU61SM9NBrBfiCVyJ3wYMZ73GhvGPcQhkj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022017fa1b374e87bb1dcc55ed07a0dbd64efa5a148d5b1f3604b6890dc43ff6022402204a0e788d401f1223b27b4c2e0cc5631cbc92d1301827a53bd2d7a7ca411d41dd", + "id": "3dc4b91d426b88fff8d78258c20991afa63c247572ba06ea469ae2a4887a4aa6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15155129039, + "fee": 0, + "recipientId": "AJN82CKCtJ912F93y6mHEmbWS7ny1He3c3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022042cced4fe76ea745f6e41b70a696a800c2172eaecb24c77845b35355f882dde402200e7a5e5cdcb4411a92bae306df4c01a3ff8ee5d53b6972518dbb1f50f768ee0c", + "id": "445db40bc02d41ea28aba75cc2069cb212793b0c202fe5346feb1ae304288c35", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15458231620, + "fee": 0, + "recipientId": "AebPWjx5BvurHauVa1Qj6nJD1h6h7hrnAm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eb671a7f10f305a65bf38632f205cc28f850d304e24d49cdcd19d7ceecd20b830220658bee7489d4555361f2d330fd31f59e146c489070c2522dbfb2009a3ccc5209", + "id": "0a6de5b1e8dfbccd33051fa1bcdba56d8f8ba2beef3c5ff7310620695ed8f7ed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15912885491, + "fee": 0, + "recipientId": "AZTp75YcToEXwnZU51Meb5etPteCx72DCp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100831c7fabcbfc8e4f0d7d093d2f80f4dc964bbbb7139d1f438ee539cb3e5266ab02207a4c930391f10a571b7047df2059de1aafc255da9225425cc09f6008e8b93889", + "id": "6b144c56b46f2d418538d2af9ba9890847b3073cb6f0d91c0dd4406402361cb8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15912885491, + "fee": 0, + "recipientId": "ANFaKygQ8EoZFiyvWi2SaRT8WbfwXpW21R", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205d825095fba54447404abf0c7721ebde7ec9d1bafc56bd4673eaa2146dd0c24d022029586e4fd33e94280ce99760eeeb4f12c51e2b068df63cdadc090dc5de09a741", + "id": "74f7c9d800d11facf0e9fd059069e0a05eaddefddc35e684c77f8bc9c6502d45", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15912885491, + "fee": 0, + "recipientId": "AVbpuFDTAyRmt1BRakzWTJyuokTRpBbeqo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f70e2674210db0cd7ab970fd8b58cb86da605ff850284058d8d6803668950ff902202669e91f8004f9ad85b19312539eb5b1a393d9ed101659980656fca3a7f2b978", + "id": "d828823d3b2287d38edca76dd4e83a88557b2b8da43475cd6197ce16c906834d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 16670641943, + "fee": 0, + "recipientId": "AG31y1h83VJt7MUvv4zu3ergGFqfpUpNkw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022050e9976c6dade3e5c5e365ff0f4ffb1990ff83e6613015559439c18e62395ab20220083ae7dd6567213a1ea0d762c24af46f8845080bd554f57e675b501c30c11ebb", + "id": "85df60d54bca2146dc642dd2a2bde7ec60c2b00ab7868b3487c3e4cfec742385", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17079830426, + "fee": 0, + "recipientId": "ANaUP88ggMTde3BhtTXvm8N3UwSLADG54e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207e0f782747a7020e5f2b0c72ce0bf366bf43ce2b8016c937d376adfd583aadfd02204607186b59392cf5d7899a8f020fb7298d974dadac28f3beebe82cc58b2843d3", + "id": "4a78a7be1bb24b6b44a6d8d20a5be158edb30f7e33028703a01488194439628e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17300000000, + "fee": 0, + "recipientId": "ARUNkZzsiJa8YECfHbTxiCGv9vrFLZcdKN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d143d7c5f2d41212209c85a3f762dff3321fbf3e766b6746a9483e5136f62ab402203117b6c3c8fdeb69dd8779319c3652ee0390e3ff8771cab0fcf18512e885fd18", + "id": "ebc1f86a8dfcb7ae369e124628a80298b868939e410a2bb5262c467b43a21b3e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17300000000, + "fee": 0, + "recipientId": "AepyttoE4qxeaX3Q6tmhvJmhWR4e59UgNN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220100a5efb6722cb9e96ebd5bc5af40854bc765e7b68c5d42b56d7b92e48c32ecf0220428e3ef95633e89d1b125fbc8c75ffeaf093971b70c553722232e464965ac6ae", + "id": "3b6bb91064213ed8006dfbb57a7aa9fc9b92acd1ff152625bb13c84d4a1c9fdf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17300000000, + "fee": 0, + "recipientId": "AK1DoQ2TzXwZFpqRuuLRkuMLtxvnwYA6BN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206d8e23fd7873b9b3b176c2f81ca68bb8f6a39e86f48e01afd58f1d72b2f370170220160f43117a81dea41d0bd16bca567744b30a0df789d4cdecf1b4969f0de5a16d", + "id": "f77518bfec4286fd33fb36f0c6bfddd86fb51a4394034d80f12616b9c839eca7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17300000000, + "fee": 0, + "recipientId": "Af2dxbRzquQbsEDdc6YyK38EYoa86bLaS7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022072d8f98d2aa1dc1ea479aca3d4b3232de3619674c0b484b4f821394ffb91663802201ef857234756b2c4dc090b32375a399c9b9981c616d4106ce35f18b55e7883a2", + "id": "c05281457bb1438b3a8dd85840f6aa8a4e87089f8f41e3243ca146f1ca406836", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17300000000, + "fee": 0, + "recipientId": "AdgRuyVNXHvKYyqfTqHNN6FL8nQhFuybe9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203fcc0e870991a56bb59b4d5e3a8be7efa5a07361a34c19802e4b7310bf2a79660220281eee0aa154c8ceb369173ba099b1cf0a46117576dd77d9d30e0bbe7cdc55b3", + "id": "bd02783756ed0a83080748833bd610847e7a54be76c138b07b417bbd09589040", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17300000000, + "fee": 0, + "recipientId": "AJZcbQZWg6yk2nZvxpuenxNEg5vdNfP7d5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204f2170f03f42e1a6013ee9ab7f585e420480fc72309e219aa536de5a1eeaeeb602207b5c6838c72cb39c48357df2419b569982eda8275bc4662c066faa7b390dc4cd", + "id": "3742052eb3b87c2e55476c66c85fb7d6f71206ef8c3c94c737a382576f142746", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17428398395, + "fee": 0, + "recipientId": "APGnKfbcBc7pKjbSWd4dqYbC6zAS9UQn6u", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a9b09e31f0cdae58a7f91e18a967e12436dcbb5dc3e56304e4803d942cbbad7002204d398385e1776bb0ed9f1cf6325cdccfb8bdb64a78287e4d96d18d5754bab1cd", + "id": "f7bbbad0d7ef708f2df7af6116907e8b89d34348d1786d22687f6e680e70b918", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17428398395, + "fee": 0, + "recipientId": "AMBQecZh541er6XkgwPgZxk7fNLWUDwjXZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f78352db270ad2e970789ced7fb13b976f20d45905cf4374d9ecb7a7b89a072d0220634c8330932815d3c89aad1f22d74f4305f9a0209279c3b5b5e849a573b763ac", + "id": "849ada089f0422ce85f82cf22cb73ea146af98d1166ecaaab0f772873f854bed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17428398395, + "fee": 0, + "recipientId": "AZHYssEeVUGz2WkDiWWkjefoSRcmNtKcxj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206cbf14e3cb952df65c1688fd79bb6dd657325b21dc9831ea4545617bd15acfe902202e828910f28cc99a91b1e28c8000b53c5389fdec3920c502ab7c2116c11d111c", + "id": "de2af52762d16398e677a58eab98730dd6ea3fa036b148fe8c701da96501938f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17428398395, + "fee": 0, + "recipientId": "AefDEneWp8sNsesZ4aQkGk91vKWCpMm23n", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022023939f4888ab00f3e2c258fcb8671a70a8b1c25a2eb52d610e1efd91ca5f813902206bf4c893b636139551282d0274f6155df35e46688974c5d9225dc1e3c7ba2230", + "id": "dfe1495aae4a5c62976804ff5ecf1e993416996f4cba9bf1df0ab0795ef5005b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17428398395, + "fee": 0, + "recipientId": "AciqQathcu91PZkK7wiKqRp61Cddop7YwY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201b8ecb50cb16def362f572efe3385be176d0ffcac0aefe6f9f378d5e899e445d022062907eb7d5381840cfcd021a1c9283dc4047a61e76df88e9cecd7852ea63e3e4", + "id": "e86a19a03fadada17fa359e563c25ebec320915ce818f28bbdb3dbe7a15545e7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17432660696, + "fee": 0, + "recipientId": "AJTnGpoECgBAwkcWJFgxNAqq6BNNwQP1hm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022045f5da273f2e2f227b3d422d418e3cbf3816958282d21131f3ec2844c9cd76920220591b8a380ee924fd2550a1e52a425fe3c7f185448347c119d2f0d515e15135ee", + "id": "aedb2f45e57c01965371534c52adbb99629ba581c788f7597da354fd72a0b885", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 18380568436, + "fee": 0, + "recipientId": "AUWioz7GLb9wrTXGMLVMGa6X4HytaUz1mt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202dbb34ce6baa94e4996bf50310955636c5082e96db5314804af02e4c446846810220333215217bd86424975be8033217fb1d74b5897ba9cbcac196efa9006765ea6a", + "id": "bc0efb1b00e63694849e719ee3552176f4dbeaeb943910ad34b681b8250c0280", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 18726879545, + "fee": 0, + "recipientId": "AXJ2kuVeuwa2MMtc9XQqe5VBfxKgt8ZAsy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210097fe86ad5302cdc3a189eb42fc873abb875e7f3c4f47b4b5e581e04be5a37a0902206e8a1fa9cdf78cd7d69d0e12059709f4c2301fc6235585db9dd8d638e01a3a3d", + "id": "a4cbe3e424ff70728cfce9f31324b8115012bc47293d480f62b05228969396dd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 18826504375, + "fee": 0, + "recipientId": "AaPfmEsY8yCH9UhUdHaWr29UCruPbQC8EK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fc0dabf71ef6e74f376b5c646ceaaeb09f65c47322096e0d130e69463bd0a32f02202b52c16528b3106348834e75e68137fff3908755171c8ef89ae44e480da6d774", + "id": "79e4c62a158b1d95852406021ecc176c2eae643012071320c741c472be24ef25", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 19758347933, + "fee": 0, + "recipientId": "ASWwdLfs6zYsAnQB2MEVq2KVhbtsuuWELk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100af87163f52e7bd36dd86f728b18360df16d9d9f499acb1709a0e365080ead6730220016e4040fe172f041da4ea90eae432d28f8fe4f49326ff7178f087eeafd52945", + "id": "b60e9a8a4229478192e1bf5df84df85e009a8517b5add54ea0e779550571515f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20185909534, + "fee": 0, + "recipientId": "AXYVmQKznY2qtzpryr19zTSjTbSkfHd8aH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022062ddc327d35dc5a02d06d6a91f9303fc340829d731a2ff8c71e868482c99d70402206c4bafa555098c358b0b5e68823b103401ecad4a05a65e5a49176ba0e435d09d", + "id": "60d339709c06fce066b14b06536b0afe9d7d8f0cd346da5763240fa937d2b7e6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20235049486, + "fee": 0, + "recipientId": "AeR3975uH4kfYydrwgLQgo5pWkmJUww2nm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e06a134e0545f41c5f3a6f7f54881ae07c5e7791d23a7c9786c3015fa5fd4a60220422553d947eee3af1faf66e6e80119511651d5a2589d39c34d7cee4daba94ca3", + "id": "8242c9f18a6fa8535be6511f8b75cf6cbbc8da6cb962f8f9a0beeb697a95ecf0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20301994953, + "fee": 0, + "recipientId": "APSfK5XyawPuumG2me4ovkwDPKCSx257gW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220789829516e896e6f204699d632176feb9a721d69bc3b72392ed1a1a7b787efea02203f736d9ffcf9ff0b899d7ea648d1b74e1e938a4fe26cf3c8c6f556b0fb4a4bb7", + "id": "e0ea1ebc9da7c1a67fcee0b06203e7df1bc6ad83444f4dfff4fc37ae58f41c2d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20800000000, + "fee": 0, + "recipientId": "ARwCmALF48ZWiY9cCBWqhCV6PkFbp2Bre9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210098dcf23f4386de84fc4d75258d22469d2bd67017fea3417396393437d831628b022064427eb68be7fbfb7d034c682b2e5a3c0a740594da40843dafd4fc8fe1593528", + "id": "9c76e2b23ef60016ec4e1e54fae90bdf727172be152125e41fce7a69fb04f73d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20800000000, + "fee": 0, + "recipientId": "AUGQoqoSkCGdT9gYPNsuFumhksQBDjFLeS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022071580d7f06e5a33fe8e140208441455af5cd013853c94fd983c457e34685960b02201d7c1134e7c3e408b2dfdd61916e8b1fcbce9fa3e5539ddbb00ec47c424bc510", + "id": "8f444d8c999f8cb2a73f6b48be6f497b0230b4956ce62bc3c2c646367ae79d79", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20800000000, + "fee": 0, + "recipientId": "AQayPyz6AWMwY8RPTCPeVhxkr8hNuez3Qi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022045fc1b2a0389840a6e8495f0bab89579c2927936bbdc1237f56b2159ddbb152802207f2216ae4e65f848f96a5da8db61a64c7b0ff16a973783d1a6dc93afbabade13", + "id": "805e65bf89a54b7bf5043f510d74c9bc89a8fbdea7fa741a68bf4427bab5caa6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20800000000, + "fee": 0, + "recipientId": "AGahFaiYodwt82Qi12rt5j29p2C2MeTLBh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100897790cf4bd81566f48c2ade97bed334735704f19a67f8f9ca75f53c60756ad30220468cf778cb00ad16e4921c2377a06c2ea762f87ca41fc55e7864fe1cf18b841d", + "id": "133692fccaea85c45398542c5d4c8ac7971d4882764b60daf649f81a3204c49f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20800000000, + "fee": 0, + "recipientId": "AUPkqi1hRw1Xk95PU9DmKn489PztyNjRYh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200fd59320b0f0962b330c012ce0ebb55791d59ac26ceefbe0f89b140ffab7e5ea02201ace6578816b4751dc693f4894e04c25ca654e9f962d7f7ea710cf55a759df13", + "id": "faf85b52fd458c9be80059cd065cb328853cd66ebe35bc2f0c76160e271cdbbe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20800000000, + "fee": 0, + "recipientId": "APuwwoc1Btsz33vZNtuQDHNznD8G8CHcNY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206633a9f528e3430e1ac997eb95252c483c94308a97462f9d8d8b6175e04efc9802204cec670befc7b7e2445aea1ad7af8d300a0fdcfe5f8e185df47bf5bf2918b210", + "id": "886bf5c983e9f90993a7fbb5a50064a6c9b32db2408d09561951578a12db73c1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 21622003904, + "fee": 0, + "recipientId": "AavfBaCqpww1LarHdp74ayFp3TSVZ9QHdu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206329d87b5ab1bf32142a5688d09638ee7b80b79109350e42fca433dd4de0170702206d27e82dedb5ec747f7e16eae5c35408ad6600c0a847c130c615c7cf2875ecac", + "id": "3ac9d7d0173837c2079cfe16d4079d1f960ccbd320ecdfba026819239c4659a2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 21819296752, + "fee": 0, + "recipientId": "ARfsXLuowXfLk3pWNrJprdbn7F25hwMRYi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207289896d987705b7a0be5edcbe197f61cdf28efe3a0844bcfd70f1f1e36f3bfe02203e49e3d9e01428c39a8e5966851fe94d7b798b4c3c6690be3a098f1ecd16db17", + "id": "ccfc33a398e393b8abcdc28d431fb5a3b4753b15544d58864d7a405b828ce957", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22277220240, + "fee": 0, + "recipientId": "AGaMNkFFH7n6bXwKbXXEdfuDCN2JC4JMEY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e619395ae8cfc6dadcea3a709bb27a014c8836092bfea565ba8fa40c5fcb42002200b46a3a4503971b4238fd849b15a40a927945babd50e7107e205d9fb40bd4110", + "id": "ee656950500341fe56fbcb6b720b120a5e6ad4eb5eb75a2b89651b0526cbb782", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22426664055, + "fee": 0, + "recipientId": "AYL4yJL3TfEafvWh5abik9mjrNGDS2N5ZT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b94073a33031731ec6ca84bebbb2e63e45d4d0c509b885684b7315d769b7cb7b02202e0662f659c239b2a69583fde44eb38e8650eaf91a9edfce4e9c602106fb33df", + "id": "b5e89bf8f85d527e891b8ca3ee0f2a2e24a1daf63e50e0ade2d791aa52e03b06", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22493303746, + "fee": 0, + "recipientId": "Ac4V3AcXoBTb3gDXkBofGd996DySEZ8w68", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bb49539eec3c2ddd506812379d6d9a126f3cb252fce6e918d72920dc389db7f40220135ab2cb8c335ac8e2b7fb563f9b3cf5ed4a3189e4ff9008871b21765def468d", + "id": "f418f20dd6f1c0826b102833e01fee05ac5eccfd3c69e70c6d9694c9885fef2b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22606906279, + "fee": 0, + "recipientId": "ATksXZghzmn9RkctvKrpwPEctjEFG2s6Fv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206ca341544ec2f0047a0f1001838d1aa42c0d94b49257034e037258bf09d34a2e022021c6958774ed74bbc8ad0fc4fefcbbf3711bfa0c0b47369fad575001fd3d7df6", + "id": "42e3bda812617277293b0efb08d308dc5494e47c5823d088e1c0520d2082ff57", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22720508834, + "fee": 0, + "recipientId": "AKy9y4KXZ7XhtWmiodsb3mJSE2HH3H2VtS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207d201e258c9244f724e94f5764513a16a42dc2aeddd50da09db88b6fac5919350220437202432b51c67ed7538ae1af5ee3445093d5fe85ab500923b23c866a4e12d9", + "id": "54a8922aceaf439f3efc4ebbcddcc60adfff7170351c57fb38a3a23ef2b6e1ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22720508834, + "fee": 0, + "recipientId": "AdQTTtT1HGKsAuGzs1mgqBnwXHtMVCp6MJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100df406f87bf59816e855ba7749af423879a850e20863ea5bf5e0ff96972aaefce02201a50f1f35dedc824860e5785dbcd6b49eb09510df251b5be71d4ec1fca16e546", + "id": "7f97907d31a72371925ee8ddd94f6c9460f2d7bd837533245c2aa41b9fbcab20", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 23338898720, + "fee": 0, + "recipientId": "AJeeb4cmTjh2oKsSUt6b1Cted554SrkCuE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201500734913f9fad425d61e07ed00838e1c1e8171020c25dced36849b81be9da402200cf05fe867256dfb7467009a78e47ffc001eb80835413b4c558a465cc08c048b", + "id": "888fe214344f8d288fd0107747d8bcfc1cdd1baf596c75a1aa493b2e086861fe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 23976819323, + "fee": 0, + "recipientId": "AXtJWNWJctDDFkwuYC3J8CNBGNJorqs5KS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220678d7e0489942b5faae8fd0cf73542706f700bb4a9ae7a7037f1f13a64b74401022073f32ad4ca5b3a8ecbf843d645876656ff46382057586a85909c8454261d829e", + "id": "d7c0a0b53885f500999c7efde8b6f2dddd186247bf94f0f9b4a1864630d90dc5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 24200000000, + "fee": 0, + "recipientId": "AYet9i4fohv9fYye39z6TVkTkdAeb7g2Gm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204382461f91941863a5256166d0202c684e4fb1ba39ac43eb7cb432c8b0a90a0002205ad3056fdf794935c28e94bab65673c01e51154624cf695331687f0a3974b420", + "id": "a14fddbb3784cf2bf6491f244fc9830559bc8b3fd355decee085db44c9953b5f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 24200000000, + "fee": 0, + "recipientId": "AS3b7yzfnNX97TLzWm4ez9GiAfkbzs8WTa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022075bfdbe554fb74d09142042bcde603b6390903a8baa2eae9995193f87bcabad2022048a005a2d88496da6de06cf027ec78de4563267a8de12c0f258580d1e2d44638", + "id": "74b83c00431943cdf0a97d5f801eed6045c1f57e10d0c07c5437954b4b811efe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 24748325720, + "fee": 0, + "recipientId": "AGUdj5MtJ4HnmcAFtdd8GscPBGwRC4hzYK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008600ceac976e10082007b40015e56d19d6924f888e6e620ddf8ae114bc877b5502207cc0afad875ec422d817fbcd6a067ffce41c013d6d971083b52389ee567726d5", + "id": "a64ef1a8982ad70a13440e5a901066ae8bbab1b1015113bd53a48c14e886d2b9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 24859867470, + "fee": 0, + "recipientId": "AVu5QHc9C727s3wXJACZyvFRc9XJSiHkHs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ee316c58495cc4729ad60d451d129d7a4f98574e764f8cf5bda95050e1a8c79a02204ba8d962c6128d528dfeebe64156e7ea6aed5b4c98f468265d5886066c350711", + "id": "81e9ca6c942e14621b6c0de8ca5deb8200114cc8dff364ad52e541663095090e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25052610401, + "fee": 0, + "recipientId": "ANmAMut1Egf7zK4DBHbaki6tv7dzgsEzBt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205f1aa6c3bc84dc585915658e7e03bdca6b05307a86df928cac71cb523be7510b02206cdad96a72947eab1a24acd55869a8888c12252583e3319ce11ad2de65f64857", + "id": "ea5534e532dfed08d41e0c0668e5b1d3e1aaf70831ff61c6afc65562580a40bc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25157514204, + "fee": 0, + "recipientId": "AUncJXmGFQDzHE2XWPWEqitaQg5Azw7Rux", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220354217fb3a9f69b30e0c74b86c3474260e276e4553c0dccbaffa5d2eb1101c5702200b9dfcb85de1773d239fa99288959577030eba8f3908a4cf2611d52c5f5e58dd", + "id": "bb609aaa336872e1a90049e64e08b329ba0e9b1b155b39555660f13e900cc5e6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25248444979, + "fee": 0, + "recipientId": "AZ5MTXTuHnMtZsgpZJ2zM9B8gWojkpen4A", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100892b7674c9045e60026206aed6c6d8ca9fea571c02fb63a3d852e26de4d9aa1002200f4c29438eb6a7ec035105771370937732a0e076fb1bae458d42f6b864784d0a", + "id": "9e7cc0e06c0f91878772d1f3b3ef00fa270da3087442baaba6e2ecdfb1e788b3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25763719365, + "fee": 0, + "recipientId": "APxxDqpuPch7PfMZ7611osF5bZYEyx5UMB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dafcc214d6f27318979493288f12f50e61a1398bc4276ff9e559dc2d00a2e61b02206b173725cbca509a7d26ae49ab1b063540c8d75843c887ca1e0139029a26d395", + "id": "b838f9fefc43e5780733198956cb88817b6b57ada2f8b9351ddeb38a1b1b4a13", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25763719365, + "fee": 0, + "recipientId": "AJdxs7TGgcizNuXVMYgd9JaXpuyKe6fF5g", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008402e0f1908c2a7a75474b3262ec6a6b492cb8d63d9ac0fefd5e7935f91cd0bf02202e6225f7b03abe5f580bc1c7d7a2a33070fb139fa22122fa075a70d912fc7749", + "id": "e93052fe6fe08457995307ee8c843d5293f0d62d3a644b4613af01057f7b4182", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25763719365, + "fee": 0, + "recipientId": "ARjehcSFYaDpcuEnZ1iPquKMnq7i3sW8Yd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201b094974dd39f06be93b8fc08eac96a942eba37a5ee4aed9b38b4c4d5e85414502203470bb9167f1a5121ae1909a3336caab615fd947a9ba464ef4cdc0ec5efd6182", + "id": "f9b86470f90ca95e6ec3dba7dcf43a638992d501595dc2b311354b01420267f8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25763719365, + "fee": 0, + "recipientId": "Aar18iMBogeroCZw8S4ELPzeLDuvXoRdH8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f884dc287f4e68750c622c5cbcfa02efd10ef032e95e3dd5163fe0709f331f7002200b8fb3fb1c0a21467618f265d012a2d0094d62b6b95c5a36e77b1129d295033e", + "id": "7dce050a4abc7ae6ada53d03e78ee7e42774ea2dab60a084cc2f2b2a3f77cd4e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 26000000000, + "fee": 0, + "recipientId": "AXbPyMxJTmQ2iN7qoswFQoxAfZWW3wZj6t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c343b8c44441d845cd4a56ace540f15ea2445b00a22a350a70f4694052480f2602203110b618b42594b5d8e148f53cf613b55d2eb83685120e40eb9b8f2ca2ee36ed", + "id": "78688e06570c4b2ee4017450f89cf8531cfe1ec31a5422887e84ba2a6a21427e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 26000000000, + "fee": 0, + "recipientId": "AJbQvAY7LCed3KXVa1qxJiySXMKWRrLZ5X", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022045b6f2b658a709eb8b9ec4f9d15839cfe270ee0be5f96e6550a6395e0f887c2c02201194477e971ebaa8b7b8dc4f281f2cf1157960905618602c060b35ccc29d8c0f", + "id": "1604c3e03314a5a83519482df25366aa40aa3bcb3738d7eeb42c177bd1a38b08", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27559966014, + "fee": 0, + "recipientId": "AXBwNDce45Bw5upoPTup7NUmdhn8NP6D12", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c3d6ccd2349a07f0a94d99292863b4c40fd1fdcb541dd15554131b6f03b5c427022028e5387876964b386a74c01ce79c9435ae71b6690bc65284f6a88b6e313498ad", + "id": "cb5a0d3fd3c4edc2414b8036df50caf75b9b8c25b1bb4b645b368291c8506cda", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27700000000, + "fee": 0, + "recipientId": "ASXb7xv6zz5N3JVKzhvfLrmZHGGVETBvSw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ca228022c4c0932b3b548d1c3262894183c12f65c56981b54365360acd175db4022037711473cdbfec5110a4f4467d2f4f847cf8e1424a70bd31ed391c11440e4cbf", + "id": "1d466dc4a218dce9c63f753870cdd645e994c9af33ae0777596bece6fd95bdb5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27700000000, + "fee": 0, + "recipientId": "AJMMbNCj4gMpfne8rg9QezvdBF9zxPfysh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202ddc599782c761e60e98b40880fe924a162495a916e30b4fb7e119b5a0ed0a8f02207429cbe9b1e529682b78c2e30e8269673845621d809b9b0393b7386e396e0378", + "id": "a0c287269d928a06bad77dc7366afe8579308331b10f2fc67b0c249edd5652ba", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27700000000, + "fee": 0, + "recipientId": "ATj3133FZRJqKK49nScR4yZ87S6BAWdtZm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220690493de80d6ae99cf3108b18cbb9ba0b18d9c283f65d9dcb8158df40623e6df0220376c3e63badab403ed5bfbb298622a9449b8f40aa93f797b4678fb03a0615570", + "id": "7ea9ac0687d6c936dc61a9c6a8c131412cb973869a7073b7a679508d91243917", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27700000000, + "fee": 0, + "recipientId": "APunbT9dgTkTpqpUAqxHHW1GvQ6pNzaTHL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc128ca47b50617210c4efe3991eea82f3cfeeafce7e3ee464acc43c6962fb4b0220434e20c12c72b9e6f1cca4b0f79f5f8023cf054bddbc4e654cd195bed26ad923", + "id": "7fd55eb9e7a1576beb9060840cc704c3db6c2022c904d6ed4f6fd42bd2b94b96", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27989653191, + "fee": 0, + "recipientId": "APTFQ7bZpw8n13J5v5sbgZ96YpgqbdYsNj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022045ff469954bab872b82b892d9cc5422ab3284fbcb4976af1fe34abb3882fb18b02204ec503dd4f4491143fcd84a875b6ec8fa7aca68b2e8f6240cf5cefbc281538be", + "id": "6941d8c3cb4a3690680b6af7656f683e3f4d7dc77a8ffed8ca64af9ec501134c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 28721448453, + "fee": 0, + "recipientId": "APjsYt53nwje4wv8sBUJkuJPgHYfkJrvbr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a5fb78077c9bd963201b4b73f26d1f57f9ff804d0e56080d7d5b11365b97218602205fcb7dd7009b4fc091cb21d8fb32c3b20ba8feee9eb95f19038caebaaa1b3de9", + "id": "653f5e251e1fa91014fc5d5fdb5160f1aab6408bb8e62baeaa69733a9d11b847", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 28730083421, + "fee": 0, + "recipientId": "ANPpsAJ5zUvsFSH89FpiuKn3HSE3F8MrvD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203d66184dab85d76f32d99f406814204822417081b933227d247eb99aa5ca69420220470e38f361a771362db029ef353e384f002982dc5a40bcfd846c0db84d413576", + "id": "24df83ccb61485f2694a460efbb8ab77b38d0587adc4e48855ba4c496195a2e5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 29339140141, + "fee": 0, + "recipientId": "AWwHvryaqeQv7SsZVysNaG2xHQD65romAU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022037e16fba790a42b0e56a0e7b353fb740eb499278cc8b9e638209ba06885e27f7022039d8876608f1afc24e7a27400844b4855e6f6b05f7fd2eb45f682760efcd5442", + "id": "54ec066a0475f3d3bb34b431aa58747a246a1e8513c6d295669f0738ad5208e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 29885914464, + "fee": 0, + "recipientId": "AKWZFvNtkFcyp9qCd1qoaA1XHTCo6yumHz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203dc53e2d991bc80bfe7ef58bb5836b1016fac072bb04dd8ea307c675fb5fa86702201e4ca6d94cd78e647c9f1796bfe6855852a1b08d24033e31861cf4769da707e7", + "id": "882cb0fe98bfb862e7c1b0c89fa209cc1611691a92290ead9bd049b38fc69dfb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "ANPjQL9f5qA6hiVgntKsdre4hRnt4kVXqb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220641e29b2546a81aa442e6b13f7c6ca49cb3fd6ea836a3dcff16f9b8ac88b30d602206804d133dd4ab791569c95cb6db82d9e9ae9e8774ebb14be8de6a9ba15308bbf", + "id": "a1e0e700abedc3d7ad878be06f16cd2d565074462d81a731976a57411d1c4d35", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AbGe4yigaf5FgBd791PKMeRkysFeRdNUJ6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ac923911201411e738cc3d738b588fcefc6809d1cabe40042fd145624958f32c02201f3f39bdb47e5ed315cb3d88efd53d7f7582370261ec2956d1eadcfa33e1d560", + "id": "99992456df9d1108f11876975230e1f47bbce3defecb092aefcde7d015863426", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AGWNji5TSq8tXMXK2JftSbUuytzkkUszKX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202ed1f7fadf1c77665beac7333f23ea2afe3968219944d538a894fd5763f7cd320220637dbe52b04f82ca4f340695b9b77e939d6abefbfc10bd928781abdfe0770b59", + "id": "f11dcbbd114f6d70f1b90c5a54bc4dbabebe788ae0aab1473b9464d48686f0c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AZTfaXxi8ZEvtpvZnEbdJZaajFXqCf1B16", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ba7d814fe5391cedaf1eae38c14557e7f7842b0bb618a2043aebf040c10c23dc022002780b3165d68dad87c0dbfe646ca1e11852cc09e8f57443787b3fb4ac636b1a", + "id": "1ac2891f76ab45cce0d5297e9d896deb831ae644968c4b7aacd3c22ac3e2cc80", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AbEXWz7aphvULaWnTs2LMt8PTnuvFM1tcQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e5b9e4859b7541fd99124fae2704d5ccb01d9fd424168d931dd8ce7feae9e55b02205faed9d0716e2abea81cf4748db0189c7cf802706087f0570add7399eb51465d", + "id": "53ef0aaa67a4d02eebff7a550715542501d59849dff9ecd83b1be58956403276", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "ATHcSoKCMZzo4KW8ukygbkeJjQ3Hhdm1oC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cda20fa0f3a07fdce9d0b17acbd67703863c40311ded26bca2411ab6ea2f03b002200fd03ec6c9c62e33aecec9f704b7bc5a409cb727cbe2b43e7a9ff7ac894b7e85", + "id": "6038460b4fb005389c6df2e4e1513468063fe6ef332af5471e7e47390e09f7ba", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AKrAqqmZ4SfjPqd9UqtbMoecpHR9tCgmWh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204d13a7b0c8f6267c5134f40c9c03af1e75eced936834f100607ec8785b58b9fc02205f99a3e0133114f48a42f0eab3e1cf9546b2e71799244e31f12470eef16a5af7", + "id": "4727130dec5138ce4720b7c347fdba334c80d638549992d58e62d8ac6bac5e56", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AJNdz4hjGwRxrt8CJSBzsqzTHgyBUmEkp9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e0218eb720635af56f74b4c3f531ab1146764cf082d9c934978c3cbf52a3c65e022041f23226a14f86a8893cee1ef08bbc8521df699ceb6e3925dee5ed947b06d70c", + "id": "9ae2cae32a3895d6fa5f6b7f6b36b21416a6fe14c4782e386d5ebc0831d9985b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "ANWw5vn73Hjh6pLHztyWjYvMiNxYynkbKn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009a7f5002efea36c366ec5b7afe37b22e12a72d59b2ff43626ecbedeca83a14e802206ea4127d8d15eb80c28905d5047c16128a23ec64e9f25262345de8066e0a31d5", + "id": "3735e0e88737e8ab56057b4383d091ca5f0779177d5aa95da30cf0d8a81aa96f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AcgKpQ3zVCN4St8seZaob41Brk7zpBbyF4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220455306c09b4bcf68b3d503c3399d266ccfcd7355014e1edfb600e17ec67e4141022055e7128f5c2caa7b564a08b0dce9492448aaad0e4cd093b603e61d245b27fc28", + "id": "d4c0238a3d04e69ca8e2ee8ec3b8da3a41679ae8d131a1012c2cb300c4a437b7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AdT9uerGBa6ivvJ75gGJaDYjXyPXfGwJBF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022063f7821d3d906617f9d60474e54259e408ee9f88c724ce5b40455eee8701042202203d0ca800bfc80788ed4888351174f6e1524b7ea63a41f222d770f305f97a35f6", + "id": "aa97fc714c3d8c6b5f5fb76c351b81e4e0fcd263b90597a72e94d55cffc14f2d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AQ96VLizRnB1RtTb4voazDaMLB3VYKhoiW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203c248de6577db8045e71f536de61b935a3c6a2dfbf9ab80f92d810a31fa34b9f02202484f612bc171400b2b0471b6f5820be3ba8291031740e2cde5547948bd25f65", + "id": "371d63607d0af6c067a50dfeb58e56737e2636f4b0b0ff98aecaa44507a73f16", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AZEYQFFZ31ybrZ7gcnt7YtJb11o72HcVCE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202cad95342229afd4cff162e4f6166020be810f803ae87247e609ac6b476be6650220643568fb6eab880dcef52e35a6ae597da18d849ab7f77e4847216817dbdf2066", + "id": "8d4bcd6bbe8c0d21408c5c956e963c7edbe906e8d51618d15946356ed05f84d3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AQrgHm3rfWGVXDhoPBSz4vXRYSJnezg5VE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204abad6d12a702ad5bfe5d86738bb12e5fb77ccd3dc963f385f9fbc60369406f602207e473b2bbcd7176b9d1b8d3ac9429b651c8afa5e4f1394d97114923017734b2f", + "id": "c16763bfed71cf294553f0f3d6b3b0b3ca6d133d73d09c4b918b6e5cee11a895", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "ANBMsW2NRR5QUp8yhQNJc8BW6yrL7iRW8W", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220344f021c3a169186dd6ae0cdb48330b6224da92758de49503fff882d1b71b2c7022048f81b28b9a8ff3c07a1879fb2b3f0f31489f9409061df4cab21bc8403053f34", + "id": "3a359c2cc190a82229e1366b45d4c882bca83958d84063bbdf513d69f8da9bfd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AWHBi3iRAFasvWqRPrhBHYXvayzn6VNans", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210095a887589f959790c4c01da0adca893b9f288a8fc6ef32de25d19a4501f2fa770220267683c7e421d85c5d368c0416c0f067110ad2b39085a57b396300df68446827", + "id": "3deed8497e891cd9c8c52cb779542e4e0c14bc1434418558e20cb2d2e7a26f4b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AKNx6tMWjp3wkCVmMKc6Lf7evoVQaVW2RR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008a303a51a8eac6a3b47ac696cd9d561133dd3b08392060a20baced5d3d7659e5022032c11371eb5e80d42325caa9933e9cb218c03b17584bc20c8aae6045687c1a45", + "id": "8fe4b99d4943a9107010ec38e159f8208e1242087efa376d7345cfc8798fcf57", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AX9teDU5aZqYBJUiGXFmkxFoVARFHpSZv1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009c77ca7fd50bbbe8244d6cf87d31fee1502d9cd53bc2719b4977b163601656cc022049cc61fe0848c5cc67bcd401339da0edf6490d59766f1eae6acf071f59d11bff", + "id": "960f6fd34b2aee0ec704559f85cb7889acfc8c5a3b635593d4660a5c4e84077e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AbB54oR2UpH7Jh9beZbHAMU7i5FAZVqEPL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205ac4b25ffcee117effac747f983502385778170bf4d9397aa36f0b68b660817502204d73b9266257237d35a1de1afed230c6512c7edb749e92cf978679aa9525e131", + "id": "a841cd49a92d5ecb39a5439a958d9d1d02622188a73b71800b45631d15ed14b9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AdDxYew7tm1TmxC6ASHJhzS1qgtoVKLEPc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa7ad99b7c0c61a861d253948568f93ed985cd832ccc49bd68b5fff69d7d0a3202205d3b4717783ed8db230108f8802644ca9b63e5edc60652d743e3792574cd0a18", + "id": "1eae0aa7ebb5ec9e7e421e704ead21748c4d211d899006b8e0c4df6c0dd54018", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AXsSBj89cjVAu5Sc9gLiDDwoRhASaVPWpA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201a00e76784ec8a63958aec45ea6677c6af0bed2c0fc698ddf5a62911459af0ea02200efae75686a8e3615b62fcb698148229837f5ff32c2cbc2d289447124bb2b5bd", + "id": "193e570b80302b146e5c034c941e1817f34979f322c1c8fe47bef69c8b129b94", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AKUncjgDtuG8pJk7YQrFkwBxujN8SLp1By", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203f9a8eb03c96849a9327c877eae8bc07d3c8e469ef3ecb7bc92f3b10ff76e77d02206b2d2396c5f18be1a68689649acc583c7a827eddf3064d16ebb095a3cb6d4be6", + "id": "4aea1fd822d1a22ae2edefbf779ca2ffb6f8b49a0971e2f43addf9607933d513", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AVbcaqopSmsUjmXDvW288cGUTYq9YSmWRR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c26e6789b1f61fc93d15a669de8c5a8dd420f68fb8e2424bb5c40c4a15fd1abd022007007a2055610948879608dbfeb831ffdac2535e26614453bd029b93820c648c", + "id": "de983ec6b437575acf2c16bb9da752f6a901ece688be7bad75e24591baab23c1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "ANhuDMbZ8GPX6MNLNrn1wC13yKTKL4PjL3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022057f327c74d0539fb43790a2315867fba5f2e8a3f1faa261852c146bcd26e570302206d543cb594a980b408f5bd8fa2c45251e7d4eb005b2e55950cebf36c92c4438a", + "id": "a869df9fc61b6e1693527a3981d465f6f46d96a318a09d9a92c9888145e88a7d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "ALg7ozgLSN9wLUGu94LwMhPhFzyWBxQobR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a609672480799e9034457b36d6c8f78c1cb253af8a055ac82117e30a3582f96f02207616bc3a6de1cca1762a3dac65dd48022da00e6800c39b44ae53e0d26a322fd1", + "id": "7f7b0295b52a45c399379f18ee56ab09638718fb9346c5bc1963d11cef412afc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AcF5LeL5ooyt1wja5Mj52gU8X92TVtG318", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022019f8c0dc34957e29ad1e55901f0ab865b38a7e0e018ed69b8793253a5d207e8002205901d7f3d081664da8469ed8e6bd59b3041ac1a8455f629f5508b50dfc2f62ca", + "id": "dae4406da93d88d9398fb59cdb154503104687b2e3a0754b169d17807a634900", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AZ3qjcSmk3vacEiV3GdcK3x18hAut72yAE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dc74c59b710ef47a0568517dfe242dd8df2bd1305155600e7b0d39113b34a9e602207abe3ef33953e3aee86dd4f1c8909edf6ccbb2a3eb181a0221665dc07e1e0023", + "id": "0d3cb578f886acc03177c1708528692590db30c8ca270b5032b5fcd86ed75033", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AYdMCtcQjbEAUKWLb7f5NEkkBx9SmTJzcU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100af1a06c135f43cd0c67c499d1ce310170ead6e61b87f5d673a3d1eee424ae1cb022065b02e0dba32c5f235d9a707cf279a8178d5e5b0baa34572327c9dab785500aa", + "id": "ca2382312af236ea7f5ecec7f860db5ca528b71a4030736eb824db55ea976167", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30000000000, + "fee": 0, + "recipientId": "AGRJC9bwD6npvCuYBZywtho8dBD3RAzikr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d060d1ae9b22357dd0fb6062abc88eb2ae7e47eebb7af7bc21440495acd3803202207e33d96cfb29e5cc40e45b53eca85b5891ee1b4122ef041e579df50464b8b053", + "id": "40a6b024984183f0809d08d199fb2d7c1742995adea05f1fe51a2fdbbd969842", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30310258076, + "fee": 0, + "recipientId": "AU2s8RgEdGWJYXjSSh8M5zkoJjdguh41Tk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be9cd07b90814b2f4cb2a868f08dea6eddc297e79ad7d48c3a1230d078454edb02200171f1277e17ab3978a16fa260903bcad9e66b8667e632cc856d09ea9359a898", + "id": "8d3fe39309daed03ed0e02d4b1808d54a18b71108ad3316898a9109981f0b840", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30644619264, + "fee": 0, + "recipientId": "AQFEMBFnxk8j3ShwNErwoqSK977DidXAAK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100929d3ed3bf40d9aab26360d553f1016f7fad051af511b4111b45ba22571d2b4a02204600751a7a3dd8d0b9e5ae93b6c014facf6191b52a64963a893ce4ad6d74cb36", + "id": "49b13cd9ea10a35ee422e6cca7044ca7a4e4201f397cbb8fd8627d1ef641ca5c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30990774050, + "fee": 0, + "recipientId": "ASVZwCuU1a7qgUKSdiZzRkuRHJpLp9Fp4F", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c352876accf4af2e7395b682bb88e69b896166e3ba25ecb62de62dec09891c5102204bf978db3488674cedc28d875a218309195f3cf7fd2ff1e35b28ed9dadd60995", + "id": "dba6677347d67b9a1030a3f883d978a72c40870414b9dd9a6fafd888195d96cb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31371117110, + "fee": 0, + "recipientId": "AXnjrqJujCyYt9rMC5E6NmnbuoN8N8LtR8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205a2ed6c565adbcea14620c4872181c55dd5df09cfdb0ea96242a6a8a16fd446e022019cd448d1b41aa454ac2cfc476eb4d8d95b3283489fed359d7228d87f97f4192", + "id": "f03293bcc0c837c7d0741081cd123574c51a55f7e58f430aab867047ed0ddf0a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31808712368, + "fee": 0, + "recipientId": "AWecYiRcWfVFEqdU9Ph6j7F7YwZp2kuowr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220126963651521701323eb3bc6d78d7bf598d7b12896138f91d0daaaa0cb2a37d4022027009cf91eb481191fb7058204489db792fe49cc2925f2ded59e06f7740f2cfa", + "id": "4b77e88f7746845ee2b045357fb2f1cdc9d23608c50a5587c6414b3a0c4df8a7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31825770981, + "fee": 0, + "recipientId": "APATpfYkUfDEhUnoq2icXy7ECqSumPRK4C", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d20fa45969cdf4217de7c8977675794023ceb702d0986785fc7d14200a836c2202201db190f80f13322618d3d5c27d07b36f78c0a876428550ba1fe3cc6e5a2be8dd", + "id": "d6dd825e9819c4bfa896bb33b25ccd26c0d6fdf07dd60567c8048b57a6046cb0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32300000000, + "fee": 0, + "recipientId": "AWcBdMSL6DFzxf4zcbouKnXqT3xnsaxi4c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a69a449089437fb0878f351b9abb5770b9e84041516b1fa92deb06dc648627a302200b30af92639075735109ddd39d1247a57e9ee7cccba8760f0e18da868f264d84", + "id": "b3971af4cbd2bf84c9766dfd4124993189e2e445bf5c0dff235aa49f4942a2fb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32490327633, + "fee": 0, + "recipientId": "AWq1wsJLPzAY4uiVTvH14F14YNZ2PozaUQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f05a79d7bcd98a58c56630a91eaa25f14e80fd07bc9c70277ded30bf32dcdcf9022067761b7133837da252a7f12c54a5e59596a5bae74292751f4f71722d1d0aa3d6", + "id": "bb073805ed067d4e2efb8882453433f52d0fd6f44235299416f012d73688ec4f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32583527433, + "fee": 0, + "recipientId": "APruVpipmehKJWeienqh2jbAdVC9o3x2Yn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c421a27e857b78d81cfd6d12a5b5feb0fea128bfc43a7dd874c014040c91a29f02201df13abe3451a8039bef22d2e801cc09bc6f4d42f85bf4e62caaeb34131a5e7d", + "id": "439c60dc29d33a9d2e800a5b4ba1c1d195f580fbc46c4183cd4dfe1eaaf9dd27", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32900000000, + "fee": 0, + "recipientId": "ANiKA7nwrdSW432oB4Xufeb4MyyTk9QeLb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207a79a82616ea4c83b9bbfd52f8a53ea0ee6ea8bcbbad23a0aae1b74e5eb133390220179678e1b59b8e435fd19e68541d890136f85656f31ad1513169df3b947ea481", + "id": "e645add44af4985a0c44d63a7d33d6bbc87d8d0ded51a9074d8796a2af61a720", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32900000000, + "fee": 0, + "recipientId": "AQvTNyqxVEHLVZfpVFttfMankBSGLyAfWh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cfc612e6c035e4ba731bc662abd91c151c0256379c975b2b0b301e4990f99c36022028380c0fcec416d7cae09ff6ad936087ecb43f162e7edb98b6773891003a494e", + "id": "d17aada75d6a2e7566d34ea4d19a4f044e7ec8a77954de872911f5e603d0ce2b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32900000000, + "fee": 0, + "recipientId": "AYnzEccw2MrcTUT4yXRp7sHQGe6G7x7UkD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc272d36bad69aef950ac0551b96dd488bc9a83cb4025fac78c57ca286d42a30022003e275f6f1f923c61d6c3a45141274ca2b32f396d9f15a37ecdf8e6299df9254", + "id": "685f94850350f66b039fd33e550f0c2d1114c9f2fd7b7f0f723f9a8dae08f4be", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 33114472223, + "fee": 0, + "recipientId": "AVzMmzLTmugSxbwqWPqxxSQA4QtXVcz5Le", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220699428272cbe81ce84c5556bdb47e05285e8f2a533c5e1f0fafdfb2ff47fe92802204b0c4c4ae35dedca243f3a322e82bba73d80802f01d8d584ce5abb75154db06a", + "id": "1a1efccbdf78b25765e859d14e2d623afb1fc2acffc1fcd8d2679c0aa1fb697a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 33341283885, + "fee": 0, + "recipientId": "AY5Q9bdBx2NnXsQ4L6Zw5XShNR5gL6ExPu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220398e0ab17a01dee9508b61e2a42fdccd6f0e26ad76168dd5f78b3bf8ed5d0a5502202fe39e30d85935a83060a11be934999874065d837f18e65ce96041b502a4388b", + "id": "eae73dbe65e5699583a3db3933114222b264c45ef69c6ad26dfa517fbb028665", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 33341283885, + "fee": 0, + "recipientId": "Ac2vcL7D9SLoQKwm5K8G6bbFQWgAXNbN9o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206826a73682fbfebf2b6e78341e339ca1265bbdc3a2a70ea36edecb3d50adb2d9022064a2a77effebcea2f7e8fa6de6249b1e205e518e0efeca9000826f5a493d46ed", + "id": "16158b459a0c1b2953d98feb4d5b43df41ded0f72f3d8f7b159d47f5fddb1c2e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 33341283885, + "fee": 0, + "recipientId": "AYNtaJsLCbs88Dqv2Cc9SUEhRn3gMi6y7B", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009c00f35dfbde9a28230439c618354b5bb60defa091c8795c455f8a3519dff48c02204dbaa156e94921647d58d271e1e94cbff2fbca92263f225b46fbafbd65aa67b5", + "id": "7c0a5ab8ab2aa880dc4688437645dc9d825e030fdac4df2a25d5a8e97f4b5c70", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 33412512992, + "fee": 0, + "recipientId": "ALa8NaNCRdYYfS5sx81qzvoFEVsA7H4VQw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ed45f09cf19eb0819dce65b619923909e116fe75588a96d6d2e792857a1a887e022050ce6e62dd65fa58e4bae5d09c191290c669eaecbca5dc38813d1b043f8f565c", + "id": "bab26302a9ea1d99d9ae0a90ed17f44c66f1174dc8ff11f7e7b79ed7b2b49492", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34492604732, + "fee": 0, + "recipientId": "AUJRMxuAJjUFEbS7sed3otS7K4R89caZx1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009e20c799169e14bee9d76da2ca433b8d98295f8593bcedbced345a3ce189b4e602205fef5c8b8225b20744c385f1bd2994a0a824849e2b65592fd5246d625cb6a28c", + "id": "6cb0b2faa48c09b48dfa6dd0391d3309be705c4704d55f72d8a39347da0cc6cf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AHWoDbD86gdtvHfgRyKtxo7jHS92hLAnPL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022043bac64f1f5a5b986fae5cf88d182ca4698d667a0989fb2b89e731282487021f02206b8d22d28a13bce3d61e513c6aa2ab9ed8aba0370ea38305c86df90796cca026", + "id": "fdb9291d29c3ba236ff008224764227942056a4d28f9c12c7ca7e22b4c4c9f56", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AbJv6ehN7bETURRPJtTgJ5LfJcmjnVCjjY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e01a874648848f53e3c58f382b04c4d8372dfe14d5e715a0aafa483c4c1593e0022024d196708b95991b30f9a735aa5bdc951ce80455fd30b32265fb1ae8e20c4376", + "id": "f10183e8291eec17c2308b2731b25e0378a2753ac11024c6b967c04138491900", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AMGy7FcNBntN6FY9ouWS62dEmiNW5SNDSs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc4ceeb3be6f423d167c08e8609e5224a43fe2692675c4cccb7ef35d9b5e286502205b9639e02cabb93e9d1262b8291022e87c8fb26e104ad46f38ba1ee4eecd8b16", + "id": "e72f27cd791fe40da52aa33d4534c6cb4061080e57a1f00ac5c0ca2e0f8e0adb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "ATgMvSP8FjG7mtDN84NKvxCJkHP63S2EMp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206556edbb04478bf8492500e284ba73afa570a1ede51add1d0798e70f349e728802202846f2751bc43dd0c0541327e1f20e5e97149b2bb29ebf75d2c17890fdc751d4", + "id": "21d1a6e3205127c1c2008279631cde827bf82ef542a9706f255f0a52b0aea1f3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AHs1p6BVoscsszE917Zhvd9BPWrQbaUqcq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a3e2779d7b46931554138df6b0cef64b7f087541eff83c07358e497e136ea23b02200477eb4b805e61433a164c1d0dac248aa2301bb20f774477bcb81c455b5ea709", + "id": "6e804117d3933b6731b936984bcd33a42319d096d5ee8d5784e1686658c72af9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AHqN9DLFs6S26HcG3zNL93VfofkFSoMXQL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022058b2c046dc42102008402d2aa1cedd8417f39663048e943227682186c8a5861b022062914b3c529429ed2e0612cf2dbf0e66baebd5ce3ee70679ec723862d8bbbed4", + "id": "d0d692c0b8ea05ba6adf6848c94c642a824840fe2b541e16c7fdc0acb8ab280d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AaAewYY6LEDiXVmMcsoZTbkvq2X1DQKoCF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd56b02b4933f9ad0f998c48d4305fc64c047d80fde18883c4c38561d787023702207f6c412361e281154d0868530a6bbe3261040d367087c401b9ad8a7915ff6a17", + "id": "72b77c936dfc9f4f38a825d79ae0360a351753a8aa7e89286edacece4ccd59fb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "Ac5rmbW5oQnjn51KeQeYPi8k8cB34kZFUY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210084307422d072f2c09c3ba15404fe9bcd44932e92eb2ed608c60d707b960b69700220186c1250f67c8805c39661d121b8051aa983e0962cb34e046372641830dc7dea", + "id": "7fe1f32c8cb0217b9ef57edc5cf687a7fb07c1b1b82a6e9d6422e1ceec2643ae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AR4BUMG1TKCcZi7rWMaNQ6HQkp6wikmknF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f32acf1ce8f09eeef89d9b42c32b76dfc95da77375d96d22eabbaf837a318fbc0220059b6de004ce6fa0a2ce3f41ed053cd0d8f735888707bcb6f5163e6c05e762e3", + "id": "28148ffe3c46ac548cf5ca10fd40575acafc8cbd43f01e97cde9b5d385974b29", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AZDhZu4Bxs1Y4SgLc24PansSLP6gXZ29to", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f4963f319382de0ba03c3c0856b9a667a00c82a90de308f00581299aa649b30d02205f7023de13f3958320606b1db434cf40c5294759c01a21691070c90bc2b02bcd", + "id": "d7b7d4815d3617e96fc2f47bbd7424a8b841e2dd5acfa42fd96ac52cd91d2e90", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AVG7PZt7mWxsbHEuJHF8xjqDLCxhPXsH6V", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a6698eead4fc3e21e659f6705356bf1c1cbf11865a735ea83e60eac5488ce1c8022002c23b6bfe049f8a0e9dae2f7eddc50558b51898459bcb2310b4376ef29f1a21", + "id": "e0d87972b7727a514955e01278aa1803bd33f18170554c410d0c851f7c0e7340", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AWiJZUDjG9KKM5QnKhz1Gnb1C3WQB23Xyc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220563392e63a4df3c65909808b2a4b69371e082af2e6f0a9d2c3fe8131b87d514902201657b05f6789b16a5cc4f83dacfd0479f222bbfe17ac97374f19238259566c81", + "id": "34aec54141d983219b001b20901745720f3c38d32cfc736b82697e11bd0848aa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AXDoHhX3gwTapS24HEU72dgjsc8TFEre1w", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204d7f9490bb14995339dd36f4f36a34fcf37726f1ff6a821731a2e788e62c9e1f02204804a2d78f61b94ca546c21285de091fcb3779c2af7aeb7e62c483556c221849", + "id": "152802a6a0822b0d9082c0bfd4109dce3dde9efb491cbf09d76e071911f62083", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AQ1n5gCzkyT9DfgJVBEMdApdKsBhXS2ZjE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206b5c708a8413bb94024010c291633528f6dcf3f86d06112312239bed0e488b5d022068113fcfc5dc3f522005841ea7972f92c75f57ac19f58fd186371d7548ed8ce0", + "id": "b0a303fff3f8d312ee52989778d5473173c0010bfffecf773d89e3db1a3c316c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AdJ7BZneub7qzyB8qPHg1FS3ViisAfXP55", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e09b911019131814eff411b87aec770b43f90b15dee330ef901ae18f57178f90220381febfa6e3cb1e869ca682c3efce6c5c07f5002a234475a1724a7a3fbc6788b", + "id": "05e2795800c06da22617f5ecb702c5c178c610a9c1ccc8dfc8a5310f80c169c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AXMgtmxcMTMTZkMWJJyZgFNuaP4jco9RZL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100db75661cda0f02805787729e123ea7fd914448e56b5de0598c187d193a1fdc3002202823a45c4a674e70468185ca667f45fd2769c4a763c915e4e4c0975a672950a7", + "id": "e7f397170f9ec10b9e1605974c37d1ab68794956b6687c03e96ecae5057d12e0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AQFYCZf3XpzZyjo4BtAQ7PzD72Xhp7y7vb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d3cc40176b487dafeffa3c068dec44b0e94e079d015811c0866e3eb96a77a2d6022051d678f7e7410d3b27a9c86e2829d31277462b37381eca23febd1d195b21cd1f", + "id": "56806a7b8d18fff61d209e977234bbafe94329804f06ce5dacb626c91e5efb7f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AZqUtuBczriacfQkM1iWaiobSGX4GN8KwP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009897cfdf79fd8c69a0508292f557dd46b4ee751be8e158d0bce8338c73fd86b2022044dc1e89fc5eaa374c0703862cddec760a8f692718768b221ca561116af6eccd", + "id": "c9e886dbe8d9344cde99eceec3f927ad6f8ac08525b18daa8d2a72ddc2a0bddb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34600000000, + "fee": 0, + "recipientId": "AW8RbZZiYe8FbDGNS77MGWEE16Zu8AxxLz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207bc3c68c391fe49ed8c7f06d35db75a739ab9c8d97cefd2e108f7b9bc5f164bb022040ffe76e6f3bcc39f993df7405860880ece802b28e56336a17b40a91701587ce", + "id": "53f0027d4810bd37571d6a25fe9a212890210ec4ee42f700394ccd8a1672c8a2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34856796789, + "fee": 0, + "recipientId": "AXk9foYqX2ExUBK9Rtdirj2XTZ2vReCmox", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049aa0eb602199bde5b2163a99fb098af4687a8ce249923d7007f5155a4219764022076e659cb2877771720de2d695b7df6c981221010217e885f42fa7d0c09d329cd", + "id": "fc2c5586c3e6aa5bc6d08272c06caebce5592b9f9bea2657707c3ced4c008aaa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34856796789, + "fee": 0, + "recipientId": "AYJyrRtDNZPw3srKy34buKUFJ8vwCxAuzc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220125967fe3764625cc68030e8111c0499a0c5102195f94aa38499802de03f267a02206a97bb532d9da550410499a3952ab4a6e01939ab247c17227792e67fc25fba03", + "id": "b4cfd3f8d50c8bec11bf73e8bda2e8c78de932ee460911e0b8ff69c49ed03a08", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 36400000000, + "fee": 0, + "recipientId": "AJtBjGqiABsp5bUduT6YaK9L9E4EVZXhjH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210087a0a6cfd2e046140baf9899ee9734ebfdfeb5fa86da06d6b354a7035ae3bfd602207de557dfade43b2b1f79b57c19133df0c0c0f6fe5b72f6b34fd0a5b5de987a8f", + "id": "379d4537f2de2f2cd6d9dcdad6c922ff45d538571712ceb2eca4ebfb676d06e7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 36400000000, + "fee": 0, + "recipientId": "ASE9D8yvqKef22qP7gAqKyxYqvNymPPm9a", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dbdf2e7a976583aa29d4bf6e0d46f3919aa5bf281af7d2a655db076cd73aa1a702203d2a6c322152f63bcfa2db3f535f40b397bdccbd69a13557430bad54c3410cdf", + "id": "0fcf2fa0265d5c4bbdb6a0a0bde602f3342fa59c479af9c761e8032876ca7a34", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 37488839577, + "fee": 0, + "recipientId": "AT2FkCcRYAttXPz47FGpdgURLmza1cDctK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207cc3222d47958a7db0ccf22b6a82724ab14774498c3015c1efe72a4eaa81f0c002204c6d395c17568ee5f5831170da2ed918f5bdb637d2bec9711173b444b09a250b", + "id": "8a905c558371ad7a59243e489fd76d5ca42e522c8a04d3d8908a73445d6ecea9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 37771522137, + "fee": 0, + "recipientId": "ARZQdfzNT9Bd7ym6p7VF4KTjnD1XyoBFdx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205c90fa2f53194d4d662c53420e4ff1408aac499327e46ab09c5bdb4bcb367eaf02200c71e40ed64aaf058061a224a54d21d1da4719816a4ad90a73b2d86a2795d3f6", + "id": "0f8a2e414407bed22c8866f8f3ad446bd932f24089c15c3122677c7f5ddbf4af", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 38100000000, + "fee": 0, + "recipientId": "AQpo8EbxDh5FA4zATjVRzEFfLcnmUnzMAv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201de54ef50c50d19a8c86aaaccd7ce55790faf0a58031f608f50929b0ea79e8550220533214087b327632aad01474d1abd56f24793bcddd2366f7cc8346cb75b72985", + "id": "079d07f46a09bde98d55c78ecb6c5205e5209ab32d27c4307dc5448552a25bbb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 38645579049, + "fee": 0, + "recipientId": "AarTZVSMDRD4k2YPRRs4vNpARZMEa6MtHL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e437c1b3391179c2c73f309351525aca426a52a050f84cb92463fb37c0f0da65022075660f2e0dfe215911ca518b55954cccd0e184f2d87eb3b7f4500a3e782a7b4b", + "id": "a16879ce73bbfd163115c767c3ffaba4dcf8d1cd2bbdacece77ef15c1a777ab8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 39403335501, + "fee": 0, + "recipientId": "AZdt1o9xiUGNbKtxX4GUjA2Qf8rrepeY8J", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022100c6415f478336f7eb59ada8084bb4656eff9e22a97670afaa4b7aeab9cdd38d40021f62e0546fdb8b200a526c79c67c1240fbd528f6cb332804713109b921b4a458", + "id": "6da4c777b0511056f1d5420621dd8b70b8ca6ab5fdc1a6df342938513e01b2c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 39470224178, + "fee": 0, + "recipientId": "AV7fCGPQZi7tcKaKYDKf9yGJRiaTpicRTK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e0bbe7320e5972688505a4793e0e9cab39914b53a9ec5fba0ea6e179cd8f94340220737cb6e960f6c2af403f8a80b7a553b6baab74e517f3bf9de245621b1ca9cfac", + "id": "bd2280fa9b6d30ec1530bd9e5601de25f3dd3e0344472a2b4cc7c32818e8037d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 39620933325, + "fee": 0, + "recipientId": "APLS8VURVhTsQ8gYRh69cNbUCETjL89jik", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220548e77cba610645e3b5caa5fe0de930be30662ae976b837252b3fde06bd2f93202201bc93d5dd1710ed48d95cae56f0842df1acbf6da100d4d4d4a56a43b84a504df", + "id": "6bda585f91fe13005e7c2ae9e317e55486f3e6cd0d83d0a64636c409ee8962b3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 39676127823, + "fee": 0, + "recipientId": "APfd2W7pvN7889NjkxygPYuaeAg1XNvKSo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f99b2ab861887bd456fe83981eb9bf17c655bd9fb3db399c57735001b8913c6102206166bff4926c83511798121ddbbe68e37ce8ef14baf47009c55385110bd470e0", + "id": "46aee861d13cbfa1e058f518e14f1accab43bc628b5aa1e5fcc5f28b38219a03", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 39676127823, + "fee": 0, + "recipientId": "AaLnoAq8B6ZQ9gWMNSc6EHa1PKKkNrPVFV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b9dd2cd37c9f1838e81325c4ec8d738c9554cd658df1588bdfd568d4ef92e1a9022026e3e132290b7c040220c7a6acd09aa98f4f10cc203f632fe8beadb4f9e1b036", + "id": "d40b29ae4603902272be5e09112e1e5291d49ef8a05ac9e756974c92ae50a677", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 40208679058, + "fee": 0, + "recipientId": "AXVNkc31rXQchNorSwfpweu54jjPCuJkWu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022078da406531b60613184867d8ab424d2de871bba0651bdfeb0fd90f57c50db48b02205ce247f5c318340534a0ab4abcf37102c09072085b78649b7dbfdc166e79151a", + "id": "d3c4f8396ff1a4ee221b03282321bb8e45ce23efb00d61b8e1f9c76039d2c744", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "Ab2j8GC1y4QtgT7hYv7pA2g1tiiC4W2Hmb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fab3f4e4d7b764645b4dc54ac55b3856fe95e32f5be4e1bcfa105823a9cb5ed20220053d7c446db556eba9cf85d8e6229360a28dc0cbf46d894481e29269e96ad74e", + "id": "cc6a17753f16a9bc0bfd922b5c39e54e2e8bd6c3daf60f0cb84752af997bcea0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "Abbn712o4ZVx9WVhC3vy36KuW8nRQrKR7i", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022027d1dbc4e47f15a2acbf82f28391d94205ce0ca67fc6bcd88aa0661afb4cae550220717bc9f5525ea5f38d8a17ec4b265b56a58f0caa523071e584d5bc1505b9f525", + "id": "79fc75d4fef939b3967e65fac8103cbdb9dd6b7e60c08323d735611d4f4a1db4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "ANMGxQS3nccdwd4E9VCkUPx7kqCoNgTPBv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203360d6ca61b6a128e399878bcff373b8ffbec4812df391d118eb972f8fa51cd8022057eadf2624d266fe0a054b103302832345189074c05502f9d9efd4969fb6678a", + "id": "000e2a9426529b65db56224bb9ada512a48395bdc14d22eefcc64ea1342b8e45", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "AWPKFKMqjSFjMzzimsKMgQhyzJF9JBbu6s", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c8f1def0dd762083c23c58977202e0fb138ac22f987ecfc71f915425e3492827022054d427bbadc47b5aba27f3856cb00960a9c07ede09e438ab29928e3ed0096e26", + "id": "617a1168653fd2b8c17ca88d0186949cb8a155ffce2a20727b9940254b82f2e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "AcQR4ymd1ezhUe82AuXZdWTooroxdt1cWF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f92c3391e8c66ee21f1f7a12b056e04056603ab6364055295e5bb9eca0862f5022058b6be51c4ae062433934609bf3d0b3f7c8e5aac5af9d72c0ab375abc89a6167", + "id": "948c73321a84c807cd7381d595361e64dfef9b87e957da57ff809f666b76f0e6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "AQ4Bp3zg3oCoS1Kb2grC3DMVtkRCzzgsgz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022057a78d857515ab4028b94cf3bc3cc0bf378213f226fd1160d18c17f298dcd5830220574d2d14ec7972b165c138bbca3f01dd51b34499d2ea5516381572ae3a08ac84", + "id": "4b1cbd8fbd593f29f8fa05b2aaa44c0e0130748b17f09b0c43d65d3e643a4c3c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "AHgivfsrR3CX3xQFoCQmui2DAvUZY2CNyZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a76da3d4c56cf815cd0fc51160e7a78408c144bfce412d19ee1cc58665e8e44f0220348632438eb2902e6af28bf0a5c074dad45e9060a44b011eae325fa52a70177e", + "id": "dcb2ea9333efa45f3d66d4a3fc489ddc3853f998a902785da39f4ea2ca1520af", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41600000000, + "fee": 0, + "recipientId": "APcJ3M8vmMQD7gU5ADj2sDgcf1N6FhJ1P8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b530f23d6cfaf93c26ab19c19b06d4424bde9d28d9a26383603706e15a9c2273022064b7a2879828a23310603e34907761104bb7f3a3df81bfce674ff11ae2fbb664", + "id": "69a05ee826cc2ba50c2c034cb980ae47812567b7307814444b72b8e70f98b43e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 42131258728, + "fee": 0, + "recipientId": "AUHWHwFyrqGvBcNXUjvUwM3hadXg8Ao29t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bdc6bbfd224056cce2382d92e4a81821720bff470853dbb0afd886afd149cc4e02204b98db8db62174711cd168c11900262a210fbdcf3a9a6f37095e174b54ef5891", + "id": "31ef234dacbdde03bd42a16ec10113e15d07ec0a7b7be5ba53c0cea80667fec8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 42255389394, + "fee": 0, + "recipientId": "Ab71JZB8rZ8kpYVn85Tm5Ra2MPP9dP9zrg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204adf5271946845e06db2bd764012260c81fe65fd53cc5153d197bfe49ec4c6b6022064637659ab09da617552f820fa155c0c8e927f6da4e64041ce1d1dc982dee5b5", + "id": "160d40dc3bf0931f17ba61708817191e818b5e3342c23de337ae4e90c865a371", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 42416655243, + "fee": 0, + "recipientId": "AQjvy3Lr3AnW7QzeEtDzaL2XvAZGNmA6od", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d073a33310126298b08ecca7abf4f9af03086b76a62975c13e335e77880ce4d02200e928de4e5726b37f282d9e483ddcb7d4fed27c73cfc0261d8bff9f382606768", + "id": "cb63ced56cddfa2952302fe8878c721bf74dae1ccb014309d602caa2b1cd3787", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 42700000000, + "fee": 0, + "recipientId": "AbKA7AXhr2Hz4N3o4uLgxCVw9q445xygRs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022061a00c4b115ae2f72d6c7a66d6bbc6bff9843239b65de88a214036c98bf2ecd9022059f612d1060bea12812fda983962c7909793151b0c33b57ea1c0b87cb979e588", + "id": "7e741a32b14ce1d4364ae4f93ba123042d430dbd76be81b0705a69921a07596e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 42700000000, + "fee": 0, + "recipientId": "AKRPP27m6RocDmDwUeSn9FPMZAdq7XZKeb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204976b03c753c921b6ffcaf08b742e53f3944dd99f7604e9491128c2cd5bb06070220735eec837093c4b48d14dd6d91d02a0433e86ab5156070361245cf7204b7648a", + "id": "5aa56c54f898b59c422a478990d978382aa5ae7168e87afb3a693db7e5c4d897", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43300000000, + "fee": 0, + "recipientId": "AboW3NE4S2atxnDiB5fE4FNq6ewXQX1jNM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200bd507090d4afe13d3cfda6d6a3b8cd84d07f501c1d0b4dcab622d836f6f2baa0220597014fae0b01e2f26bd560804a8d2980df2695a0b277945140c2f7f3641d072", + "id": "d8768e339e6bfdc242cf3c395d3b0f11d4b4796977179cabb06c2ec3a758c301", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43300000000, + "fee": 0, + "recipientId": "AYSgtUb1hE3WTkw7pQ2U1AxYmTXB6hSdAY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220548744d15a0172cad94e4a2da25632d1074c78de9b5ab01299ce319b7e9554eb0220252400f1bce65a39a0060a64a5cb44aeda15fe33e01ca7b3524f2dd41ef517e5", + "id": "af598dc5b1ec4ca254f60e4abfdddda7a400afd42eca30863b7565899fb73396", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43300000000, + "fee": 0, + "recipientId": "AJDZqWaKSug4kPadzqKYDzm4MHGxhhUYiE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210083e24d007dfe080315146299f74d8129fcadabd3feefee65133cb26b4a7205f102202b0378b31f5810c7aeb96d4c1784d1cb098a36df52f8f8e10d16fc41370f7c69", + "id": "1c2e9b68f93e64f431c95d1aa03edfe301d01d5e5061bf303ca3510462f7dec0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43300000000, + "fee": 0, + "recipientId": "ALDJhQh5YLMeT2TNf1viyoqJFDTdVxBpdr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205a6741a4f2d76c61a93c300c5dc9542ffe7822c1d2eaf4d63e7f5dacaf7405d40220675355e51b2da2eebad0e1409be2b674415ed903fbda6257dde37e1851ce4fcc", + "id": "4522f2e824f76a2ca38b8f9bcb589d32394eeeeb44521c27a7d29b410d27ba2b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43300000000, + "fee": 0, + "recipientId": "ANZed5qKqwcaj9Vy1emaYETWUeTVTuZE6q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022002d24d963c7d96285437680fa90647d6380df7fa3f84fc3b26ebe80d6d5b4ab70220292fd00bed98449c72e246a94a5265324c16bb8357d4d5863cbd9bf95d7ce1c8", + "id": "72ceb8e73f321a870319b090a0840e83bd8257d86500ad1c23fd3df4e9f76288", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43768613037, + "fee": 0, + "recipientId": "AVpuvWkhYahCaRt3HP27X42W5sLQp1par8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202bf9aaf14081129fade556087d9c51d31b2ca79f316df943834364dc655e13a502200fee586bf2a8edf195f939b9b850bbec71eea69a04f4bacd592e38dd69c21910", + "id": "4f0a3f9b10798151efb56686d7920dd3447d824d0ff85e805923f6e429f35dbd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44117933985, + "fee": 0, + "recipientId": "ARKK5gYqzp8ZBQu6Lb2XCrhyFN7DW2u71y", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100982ecaaa74b2b64bc24eafeaa49951093212380f9784b4cdba3f39b4c6d80e84022054da5701fc8b6222fd56ce68c1c36f125b588e1b4a18fa2512674497c553d807", + "id": "f75cdbe651257ba7454fae6d967686c399a041698c666c25db6f31641a7dc9e3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44286661880, + "fee": 0, + "recipientId": "AN87Bx6HtTjA6NpajYfTLrqSwKHzx5d5hW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd9e1fad2be125b433ad14c77655b8f461db35bdcd68e2cc776c22c887908e46022013e50085edbe22d2feda04815b035ff06fc6a744822150ef8ea7f67c893f0c66", + "id": "9a01c2222033973b17d6014c970e033280053eedac7c1fa9092b2d104a72caa1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44333481457, + "fee": 0, + "recipientId": "ASbW8T3Xx3bDZQYGincxMhgspZFZNrobJD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009fcdb37789a54a1ca4134fa8a5931cb5ecff33a6928695d68a903d088fb086b502200e7c474cbb7d1f6245c569d3f2fd7468a7993bde91bccd9802c59ce0d2b56ddf", + "id": "48de47db070de621b582c945d2eee43674ce2ec38b48d7fa80294d20e857cdf8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44556079374, + "fee": 0, + "recipientId": "ALBVdpLvZofQPLdHungdurd4iF2vHtoFbS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210098e6b691e6c5c75fe5288d2a5cf0cd0b79694a03e782d9aa982a6df7fe206db202206df4cfb87cc5fe8fc61c5b95c16c947e98671390dbac8b74d5d9acc02fd6513f", + "id": "bb34fd7dc178bbc4ccf41b5ffe7a9b8d8bae117f006006bc710d76f36a83d5f2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44571697840, + "fee": 0, + "recipientId": "AHXaXhX5ybwNZ7VDkJzJMSD81J6m225xNS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ef3571ee051193e032dbee17d7b1baf58ea08de031521582416e7965407babe9022055b390e25f42ac5240921d89210f4a9c7176bb922941cc85f92b9dc7e12a1491", + "id": "6834021b254515836168aaaba7bad2ea5ba943f2f6ebab2ed65873342886e147", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AatvqNHtaZ3bEbegvVZKPoXUebZkNQgJG8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bdd197b4f1265984c8298d2780746f2e9c3bc375a2e316108fde2334d025e06702201c836e38d63e67910c20791d9fc4813de475b83ef954778ed5a309aa7691ba3f", + "id": "f3fba4cd40e1f99acd6f5fddf5d7fea6758053db2c37e0408976a4294068bc92", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AYzwRwPZiXuvP33QBjKC8aGyhtbQJad56t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f87ba4784290c46da18275953842aa98133bc70d7b4e71d8ca12282f6c3aface02200ea514cffdaaed678ee27e9915546bd78735363976da8036347a99353373c981", + "id": "e81faed2cca7cbb2f9d059b2092adee98010bdd0be99cbd5a2fa3dd6cbfa2b50", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AFrMQ99PEVFX2fF6FHiczGKjXwetkGxYCy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210089b572936a6429869c1824ff5c093f32519eda2f184c25e488044564fd4f773d0220300200b1497d89248eb06940f0ad30972666dac7971767e1e3d72290a2017457", + "id": "d50bbb062053072e469d59ac609cfa67f1b42db5bf94f50f00002a89253b09bd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AZ5xjWNo8XA6xCbWvVeR5gWKF8Yh51ccpY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201c7336f68570529c6964cb333047edffb7208a41b20946e3f1e33a6fd8c2808102200df8378a745cfebe85881fe2145d8b6dc54bf002e7232e966f10f852f2ac4e92", + "id": "0e671acd9b32bb08daec418f01e9a2b92533928190485a2444ce5ed10798903c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AUR1TuACuFHerTUB6nkvzfr8LYKw4D1DeA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205f37144ad23e3cb508c39225b2aa5316c64a64c4dbd8d9f683c931f986862e1302202a6891bc8c03f50f697d5229fe5d9e968c731f54ee2661b4d87c4178be3bc2bc", + "id": "1e0c135240fec675f246e042f8d3677621cf69715a0f8ce8c0480c8c7e276d94", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AJhTg2R2MWsrzWfn2VMSa7YgthV9ciSAaz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b8c307b1cd5f04e3190470d66972ab3887286424834389efa4c0f14b29d5dbad022053bacfe7cb19002963ce7999a9b324e7b5beee348b827cdd071514c4f3a94fce", + "id": "625634fef958d669ba75d5feedd43da8930ee4f33cc192ae99803f9643d9d44d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AawK835dZLs9E5C9KXLsFEVmK6re5E2fwp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f97b2eb6908446bebde6634b3b79d43eab3d425f02660216f1c2c753195d6aab02201c428ab520196041a462194e906afe6bb49ef7e4fd97955dd44bf4b2cb3c498d", + "id": "71ed3ebc697935d79efb1b1b0ec2f45a67a0e784c57fbff3965ce8ac73a8cdf3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "ALyaoK69C8NT8yiKJdx2bPnPpggTkuJdFG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f69603b3b32ed21e6b90d70b8053aea3c45c1c2e2c204bd5261246b97f43da2f022040756dae02033d7959f870bd1e05145f017aa7f9e8d8bde5ea9db4458eca7e14", + "id": "a38b475f390abd826db23697d8f3a11229ef4ff5e5d9a5486dfc2475cf9b2594", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AdrWr45gkfS17raTKgACtMwC1oeem29fLn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022058cdcaabd8201113db1e2babddf080db1958c9072b1039fa245c7dd83e92caa7022070ad9408de0f11f8a39cb43d2533a8b6bf079c7237250288e4eb96dedc3806b1", + "id": "f0865d7b5fbb45687410d8abd0e4a491320fcd2803a80edc40d3a300ae0eb05f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "ATLyQaK3RtKHa6eGCPZRkVrKjEMcKM2NNE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022005e5bdc9a2eaf86059bc4d631765da8da416e108e6d2e07f2aae071e14fcf87d0220085c0961f9ca20cf2e40278cb27e3cc0de609d080599d4547905dc37bdbc60ed", + "id": "89fe6d2b213226f45a138ea256496d79ad5144353b0f5bd95315c34324674ce4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AdMty9JNMNJ4eSoUZqwnE1pzRBZQW85Dmr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201aeb9867943ae98594b74f68a44e2194eda83b52a7c01850f2da145af34554e80220615f683129fc5e3b109067e5bbae547d6c80581c2653b7bedd7d986bc19384c4", + "id": "6550d4dff8ecc93472441bde8d4b87b174719d14b9778f9f25e826cb58d57d08", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "APEkkUCURK2SFA1rXq6nrqYyPVW13rsxtq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022056e356a79e482348494a82271fa5c6ea1f1ff3857fc2429d248eb5c4ab859734022000b20a70ff281ae78a4ad63af9646c92e11b23a0108c6d646e0b2680f7a8cb53", + "id": "2640992456466adfef85dc33db1f0ad1bf731209a988ce0c3af7b9e2f0a15e36", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AYiQeJcxPMdGqdvKVHzLQvmAPcgK1JNuwX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022072936a9c44c3c78e1da607c23346ae71a7dd55294a9f1dccc7ca7b995075b96e022022ceeb6db9237674388251c1acce2a1a6eb5665aeb8a42b9772853ab68284e90", + "id": "e288716799ad69535338745567163e9607bd2500e78f2fc1dd90b4deaa64cf7a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AQAeWRKR5mR124KMqCH44BciU6otGmbofj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fb9aabdf5f55bfe910272ad8ce308f03f5db3dafd8c9d2a9007e784e0ba8b12e02203843f7b9e84f7a2a37a23843605dc6e1943b3ec3be0a7330d075024be0338f8c", + "id": "b7a4725339a9517ce667d69b50279c93c53e432ac8029cf1d1990033ce446479", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AYHJ3GQkHzy9f3ndLg3CsEBvqSrAr9WicB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a609c9f810820be67db6b2ad25e582721da81ae29b1fbe95acc1e6ec9eae4f5502206cfd243d311190870177ddced286a50f7df71e3ed12bcf2544ccb61184441675", + "id": "6e68a86ad3f141ce115341c4167a0b8c8039671f7b8d593a9007c299a1742ab8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "ATsDqjHX5hGuURrPfnDNmQ3jpB4q2aMcmK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd13b731e92673d5ae20fbe28c9dafd55b4de120a8b8654b9db323c4ffce89bc02207df49ea18df0e5fd24e72ade2bfc2ea417f53d5a268364eda48a8af850a972fa", + "id": "f6432660a1f9291b952bcc0518abca3f0fc2a48cb553922f94ab82d5127d83de", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AbWMPtepNQdtZKbNFfkbEvBki3gLxgzpWZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203fc4e5dcf19aec5c7cd014ec4aade1a177bcf1e8ba4c1ed8ba6636e6091d62fb02201935e26746309781e360a48577ef5ffcd82d41c6540943448d1bdfe44c804b7a", + "id": "1c67042ad2c0ea1436ffca3f18bdb68e192dad5cc1af9506db9187880bfcff58", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AcA3ke2piEk5tUbLcaaeU25tZckcYaVRo5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220057351f830067dd2f2a6c554307d64f2cb01c4a41b4bcb56d92bd02353f2a00302205562002faae96f1915237f5ce1574cba8bb17f2cbe4cebcc4eb25f21b39018d7", + "id": "8abc18519bc37580a21b2b5d36bfe15eb4d6f63610fca4db2dee20f6a25417b2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 45000000000, + "fee": 0, + "recipientId": "AWMJezaNhsvRxDPMtEqNdYHt6iAMMCRroQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206f1e30ed77982601cd7186278023615f0f9c19984ea459629a7d4ebcc4f31e9402207109ecb76a21e88e3dde1c9b1c996195def546b3e6c5affb44838c2fd6342a0e", + "id": "5977367c87cc269564188bb26cd03226dc5d182f7191ec3dc7948f6f21f71d13", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 46006407173, + "fee": 0, + "recipientId": "AMjF2vBKEdDs292kBAsPB2HpAYzCqwUmEC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b58f5b2879ce4eb45501c18fc605821dc979e3e7efa7f204c85f5ff02b6623b602206f2b4c40f9e7ba54dab669024d3a5a53bbf35ad4bb815c3811a565e6d5325691", + "id": "faa89d3d52bebd59d0abe61d555e187c1b8c98c3446bc566a036a217c6fe5c7a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 46288347832, + "fee": 0, + "recipientId": "ATsdHdM4ZWFUXSmAS1MxvTJn4wEt75fyBu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cdad60bffc4d6b6e137f894becf8b7d67d2084a47df750d40590a133e02cd68d02205c99a8012fb42099ea7f26c45181a47e53a02602093a0fda29681ae77dd9e6e6", + "id": "87c1e1fdb88c4e153d3e3e85a767b0e76a29f7da8a88d299b79451abcead9365", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 46416322043, + "fee": 0, + "recipientId": "AZsbKy8LmWUf44H8XqGkbGA3mVdyoNsPc4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ea15ca497460bb9d9ef34b644d107abf984cb3ca26e6c31b16509d7b9346119002206b732e6eaf0a6d14f43ed5d889726a5998d5a190895d4aeeb355cbc2c48ae2d2", + "id": "8aa8e83047ae4bc31b0775949ec3e9a393888ce39b463d91be26a9e5393fe7fc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 46947045881, + "fee": 0, + "recipientId": "AG7uDVNphx9PerxNvELAPrHREsnDC4zNyN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220697f3cf632d4a4e8a3a424be3e15cf7e9b8af40f484dbd862fa32fd0e7bded8e0220061d59d40d3f63cb496b1b1030cdbfc87a9e06e71c63fb9b05764fac08df8dd0", + "id": "e351f47deb7122873b04a91a6051db3812abc8fbe84c907533e6f68a224ad68f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AGkeeXQM5NjSMk5FwGs4wsVET2TojDJRgh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210092e1610b6e99829aa9a149a92fb3117ea73ee1f0c7c2b7cea099b55769870de6022017ec1216d66d1c8f7546d9c5e2db23b95f033313d2aa8e1e4f9762cc74036dc4", + "id": "4d07eac2ace1a2cd2579dcd8b0de07d6dde9c3145a2d09ab7580f8ded1531def", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AHGo9dHkARtM2E2kqb8oVi9VMGZduFNFGT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b022f6e3c9b494d8c11e12c2014cb078876469c3ce2cf74ce1d2448cb7f813190220012990c9b719f8e80e69108a6e9e0501d7094cf47a0daa1df32f08c2cf29130b", + "id": "143256c98cf7c47331065de6db5e8e328680aa78a35a9b7c4038055a6a416961", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AVZugZBjYSTPC9ik4rB6RknCnjzWPBdP84", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205d010fa7f4e16cdd80da7f18052eb197794173f661abd51d5b8d3a377a88d853022021f3f75f96a21cd8e3c694b1635a286febb72427c1d7983d52d6dc20a79c3576", + "id": "058cd4a632a73218954a3b9dd18a5b21395ea99bffe9504be2c111e7b5df1628", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AVp8x3mBHzs7fpvPfpUyeE8WMPdtX5CK63", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c0b5dd428920c1f5148e1282ff66c13b845990b6bb31f497381dd060e8c6c0d50220756750f9565df70437787347c15c60cb5d71677b21c9ce6a7c8bb5e9b786b4df", + "id": "6a3a8a2adc4c7c5cee98d56b69f581b32dddf7da323eddec1bcff013841bded5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AbgCof5QkxSrpZNJGYsiepZuAEwPxhxT2T", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022063f69a740c7ea719eb624fcb71fee6b497a159b101f0f3b8f06e90791b7f508b0220710ebc92317ab726b2824684d51ae758059efa7c16b7ec78d38d45a4e7d6124e", + "id": "e5458cc7aff8e0bd8e2804a68822a783c48db482295997d53939305186376ea0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AJZtruVwKAPj1rv3ahcF3Z9GTXGcN9oc7K", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204849a42fc02fb6473c4a79a0c2c9ad9facda13ad294495b9b2786b38745b7f3c022070dbe71e4555efe7fa244a3cca10bf07744bbd0ff280b83676a2a2e188f44825", + "id": "3ee0be7406fb23f3ba38c0eb96698b168c043afeaa6fd918949e0ec7c90cc3d6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AJgvboi25qWyEgoZrqqWo39kPGaPVbrbki", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203c936c24a2e3490bfa4743b369ed09f12ed8dccefa55836551ba3c666febb58a0220586eac237dc31060f71bd86a893b78a578b76f2970b8f57ff31c31132b9f1008", + "id": "14de30e7e1bfc74127cca71e59bfc74a03b9b2f714837b4812a7e149cfef20d8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AU5SUAATEwfHjAtQDkGFXVPL8Vh4k6sNtN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049a821648ea42ce2c2d1d02c5c97d7a8964060bdf2e4028954528dd843faa00e02203e800a96beedff49f634ed9da667ff419bc8c9c582bccd0297c31006683056a6", + "id": "f41fed485ef003ccbd228edaec88d8087731e0005616a600dcdf94c67e72f0a6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47400000000, + "fee": 0, + "recipientId": "AT6sBJKYptsn7csAsQSzrKuHeDNv7GkTSv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200b60f4bf416eef8e104234eaeb92ebe80647bd5afa15953cb446fa06da0b169c02204dad39f650f29de7c6867453c4121a868a2e24b12443d5c7722c4b2465443015", + "id": "17a5a7826eb0676195d33e12b63705cf50846a4db052199cc330156a67607c55", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48367953807, + "fee": 0, + "recipientId": "APcpfWW8rMcGe9gmiFmZZ6qfeYBtwneDP8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100879fa90ae6b2d4c759e4ce307f0a1cd87980bce956d431fda9003f00ceffa6a4022054c7765423a5f512d1631d3ffa077d717d943298e5fb8cde08908a0a7c9b8267", + "id": "4a6b80c0f1feff81fd4450558c0da1528879ac2db42362ac7fe3b65f4fa52201", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "ALKpxaTevuYjZYSXvTYav47Nj8jQaWSNqf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201c5a1709172473523d605e4a21c02f3350df6bd9cc3c0951f20e8e2d13151f95022047b2993d4fe88e1e7d079de95f342202c45813aa0f1a6d17289b71022c03d512", + "id": "fc415685bb6fd5876d5ad3c492ce4696004aefe8995874b6b1f1dd0d5571ec52", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AH2ZufhZhdDsHWsY6qBbrdp9AwH1rF1kes", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207d9dba6ca6da8f7e49f6d2654d0a02cf36beaee6da12fe5e5470582e4d9ea28102202de0debfc82f03488b867493316c18949aba7ce6788b8e6b583b26f7d9335792", + "id": "22a3ad155d12e9679a0af1d5e8384417828eec2b9fc36cbae2785464b12b372d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AG2RijifYGo36FXJN2Noxq6hM1JEvX4Xxh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cc3629e0cfb25047f615976b40ebaec5d3048f34eec4739e4dfbff8537122a9d022034f893414c942792e86a25e64494c4fa92e72d2252236a2506d26ec1e6d64d41", + "id": "ce7f905a90772e79f6e7b54ecbff92625073fae404abce4af59244ac361b411a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AZcoh7wHpN3Ex7ijGMrdMRTF77i4sMEgiP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ce4ea4644dce81416d60ad4de4ce33c7c389b6c6cb53f06cc3bd2c5f60a08e7302200dce4ca71921b485fc37eec0326bceae9c7345af73e16036a195d024c97644e5", + "id": "46016076914aa928d354260f80e77a6cee67c75a0fc754eb8c84c0278c8c5562", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AYnAUcC8xE7EXKouH467mTCiubcpbgQj1A", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201697eb4f8560bb074f208bc71be81f1308a275967f9af4931066f61c9f56912602204bd8f4577f71c2f5133295cf6d9f9cc54deb0d8c370fd8e1c100cbc8a192ec62", + "id": "37b6ab528f70a015c24f1d9532cee172e1ac7e24167b199de6afe74bec419b9e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AUYW8cN8SXecVeBLmTZrr4XGPL4MEbGz4o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206a44108ed5a88f05d1de5932518d0c4f473ff9b85952ec7f168b7f013ccea9ac02205c0cdb66e19f9c4766eeae55d554af8e18954f88c008621eeaf4bbaada341df4", + "id": "9b7ce4722d40f509d67a01d676ea46ab007b7c3250e70cf450c0d4152c861356", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AHoMsGebVZJMkwd722t3SNQtz8kxbsK2j3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a6ccd3bc952f8e17fe373625a22692d035ce466241cdb96e5412246c4288bd6602200c55d0122b0e09a7b98df0940538ab3308937637b03640ece423228eaf724964", + "id": "abb9425699d86129a675ebe33f6021ccfe0423fb2ee919030cd44856e29ae0d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "ARRMnpJsXLabK1nWxQqm3R1WKiY9ZyZxtn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008b4c4f04e3234f263978b73e510bff42e7eecbccc61bdddc82c906434bd2665f022061480eeade9e85307b62121241c7dfad53168a851d5a3e141c460bac0c78fb80", + "id": "6c644f623c504fd6449dec1ece024fa5026b86d13896bd6466912ae6f8487011", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AV6ffXNFhte61rxqLoCuWVTs3ufuJ7Z8jW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200db348a71ec10ef1fe1498daf3c292411ce01e1f524cfbdbee6f2a9e86959ef002204bbbbf2e508f461a134a6acc333ab4c817c9e2c791baca36d1f235a30320b11d", + "id": "cdc3bf330b78fc143c82058b0674465da2b2e840270869d05a42c2d3eac7ac7b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "APhVjG9pJj2YpG3A7rh9gApT2oFf1HHdbA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009609b469e7ced5253be188cc3ccf069e155894e715ca8ce9b402c024ae62577a02204b6887fa2ff4881d0e539d66ddd8649783bf4aea3cae68aa5bacb0cdd1e5a106", + "id": "bafdf9d4628983cf25b0f411f0c957ebe6565d01dc043d5f79877d6503749b62", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AVa7ZKfPSMcnZn5b43m9Z17dLYAkKPepSC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fbc0bd9719f34d1e39ab2c3eba2ae9b20434d4ed9550d745eba0a176c45abfae0220261f68db9cdbbd41ab3df574ffa1a7e5d07d94917bf3455f0348f2a8c86f33a4", + "id": "cae63006ef7dbc6d4bbb8c0f8b2f0c1821292a53c3720046d2f3e5aa9b18df74", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "APJyeTaF5E619fNpruHNDppitroDorHt5a", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203a37ab7e59a3bebbcf9b1d55aa629f5eda60d60330c43ba2ba1c23c43783ac7702202e75368241dbcdf204a3d9e415f8fe755fd8e60c8c45454de3104e9212a9a56a", + "id": "22399be7427eedf993b9bda222c461f4daac136d9ae48988f073bd31d34d9c45", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AbWDXPoVRcHmUQ3KW4ZWeuD2mrPYfhodtr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b245491f63892ba07fa18e5c6f0d3dd50c141bf978094934d32aca1efd8c6bbe0220625ad4d01d27ecc58c643353396093c12c9ee5f617e8a7bab48f590b4e0e92d9", + "id": "e7b6647114488172d1c18263e301818829f4ad9185cae8bed3622cfcc2c001d2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AUeBAB7qtntRpSt6GtXZXcVNeVkhuS2RRm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206013142ca9cf760ad5f77a3f946e8cd39a254ba8d4b2424489e01897edd3cabc022050457ebaa39e2b84e773ffee063526ead54194a86281dc21492e3fb0188ece93", + "id": "b9d8c7f12cd758e451a70df541351b3f19dd2f2c5516f7a62629e04227d1d4e8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AMKTHeQDsTHWw3QeSRLAW2kWQnBrfrqKHV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6fae8c2f2fe7da5e0690c8b030e7ce4d7f49ed238d182c0bcd554e3c8daf1660220065e1ec76e1db8fc24fc360e04ed3a04782c04e29a9248597aa0974a67825937", + "id": "afaa4c75e302dfef1e9d36c63929decf4dfd7d8d1e8ec8f980bfb6459767ff76", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AJT7v8APQuYDV3uDJWsAFGBze9XDKpXdNt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022043653b6aacf91675ba1ea798f0ef37e7fb265811ae4a52130959e57d4d90091f02202b046afa7f108d0d1e2a377246e9aa295531e053c359385c1406eae8ca9dc4ab", + "id": "3917c81dff0713611f02f1427a92b63d26058577ec5f16eade5bc5f313f8861a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AGuBeT5rVgCETkMTTAUc94Yzx788X9fiqH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f17d6e2720d978efea176cbc85f9e5c472882df3f6886172c5fc94b8c207b1f5022049fd7c95a425608177ca111b78b6666829d6644a03c324d033de3d003f2a70dc", + "id": "6094c25254da89c6e8d08fb7c97e255448331fd054f3aeb2a7a239f4cab5c43b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 48500000000, + "fee": 0, + "recipientId": "AVvE9v9QyJvBPZK2kgMDwJxDMu1UEYvYRx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022019dbbb234bdb03d4652206e72bf2294f4d5e18112b86e1e52e29d0419369e55f02202be9a176cec5b00ad3cb40900080100b8c3b15a0827ae5132e4004f22f820895", + "id": "06df438d747a1cad820cde398e0198333f4389c12faeba195c0abb7fd4d92350", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 49717819332, + "fee": 0, + "recipientId": "AKFLHqUXHQSV8qchdjbm1jUYPptVatZyQy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100929159e14809ac7f07724a097962ed91909fae24bf045e51c74f5f212e2a4654022053ad7d90be5cd253e8fe175267b45453d5af2738f6478d611869ec5e4b124e46", + "id": "c57dfb4a2801c1ad9868dd2e14eca2115928f013ee8713e068ab670171e66abd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 50011925828, + "fee": 0, + "recipientId": "AYTJNTk88MySw2YRJxb24XQrHr8fYe3SFf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207a665bd64a6becf19aaaa99468984ce4e5e74cdea92ab4ff565d8d202319ba7c02201aad01d9484f0ec40e02fac399570338ff66cb5b8a84748ba1cffa0bef41fd3a", + "id": "89079a73bd4806bcf928c52bec954e11ec3de186a4caf4d1bc8f21a430373c88", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 51012164344, + "fee": 0, + "recipientId": "ASm2ZsSj9W7jmYCDdLi8Eh9RwLQALkH1T9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cecb5a11966ec1d395eb2eead1ebf4d9c41f7a2e65d94bece07d8175fdb20125022062b79536de24c87d93ace4791643d62e2af941cba4aa457b776bc12524cdc0dd", + "id": "199a36b4f6be8326483599431afdc07c424c761826e5dc915103ab677d97112f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 51523164985, + "fee": 0, + "recipientId": "AYXPcxBW4EiZ3hav9w8qPnYCfMC7UiHUFc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205006932929f7e0fcdfa814063682fa9576cd197d2ba944a52244d8ac573701620220540b8abb5d72775ef6fa039984fe4fb068fcbb11622715947e0bf43b00dcda85", + "id": "5af79acec8a2ca26ae1ef4a4bb4c36c9d756f4627407b9831dc1aefce88d41d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 51527438731, + "fee": 0, + "recipientId": "AN8fTyWTGzGMWndi86xLuPZ6sx5RqDU3V8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c86ad1039fbbf4dbf38750f6933eb94de081730aa4bbbab2e69fbf6bffd86994022065cb206c7a6da6a2cc49564fbdbfd861c2d7a43ae574a9152ca743ccef38883a", + "id": "0e875f8909e934d95123c4e39b0dff610726bf4cc793ff32ac3ab87607a869c0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 51527438731, + "fee": 0, + "recipientId": "AcoxZHkmsepyWCs2K77TaAAuKXzYFRhQCF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a556f84c6f3ab557c61a3f103d5a19f967a624bc247905e6433206d17b05979702202bda3ea0fe77b2217dfef8a79921a4169a421451eb08f31350667e8253eacce2", + "id": "baf5b1d34f909d2ef94d82896e8354fb10d59464743e95174fdaf41f5816d986", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52000000000, + "fee": 0, + "recipientId": "ANx31923yoBut3ToXoPugKPnzgRhRGELPX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008e91db1c38a78379d51739c8e7c5bba7c7e10423f90d975a83ae8fcd5073e08a022025c8927e171c351598b0ef39f93c2bc2eb2bc86eccd43b5b8c9f6bca681edc97", + "id": "6e86383cbfbcf26c0268180644f7a946325559b282acd1b4166d1edc55271951", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52000000000, + "fee": 0, + "recipientId": "AN1XZJrPk8cm7Vwf1ZTrKrZtCCLZgkyrJ1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205e39a2dfce53486be7370d92d65ccbf412e079c07e0405d68fecab4f1e17371e0220438de48ce7ef76a2bb2b65bb9e2013eb1790f1c9b26d2037138ba4796315611e", + "id": "6cd2b5fb62f45dfcff3337b52bc1f4750761e88df28c5d02acfecea7cd0164d0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52000000000, + "fee": 0, + "recipientId": "ATGUpBz7YkxB86G86796U5yTEgyK5u4rJk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202a418c3135e189187865442f8ffd4cfcd5469eb1858ceb9add3bd0810ea37a4a02204744e68ce981a9d4bbc54c992382dacd14b47458873da4488e3995f206b09db1", + "id": "722201637e0a6c263a9a2094f2ca0adda2d8b69f44a6f15e4d29a53a93cb2985", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52000000000, + "fee": 0, + "recipientId": "AGyRgpaEFZ2ZnNHL9Kr5bpMGkv5LUWebQb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f5ce9d7c0055100d09575738ccde05ba74dfe3db8bdf4d8a30d0bcd6b99592b02205a0f50872db6a29e0f0bab2c687c44077f9be26558b022dd74b3e2275cb6b853", + "id": "33f843ae2198e8310d2055803607fac2d24907bf8f8daa2f7290967e81672e14", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52000000000, + "fee": 0, + "recipientId": "AT3g4LwnJfZoN4vuFLtMJY9Wxq2uMtLuxT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022059078b6b0dcb5203a2c3ba3a46bef4d723a63ecd6fd3a2e3ad29f67b50fcac1c02206994e842c8011c3de756e762b831e9b1aa12d8df872d5838ff98d53aeec95e2d", + "id": "f5ed89f77f37b64aae398ba0e89fb520081aac2ef2ffb2aec87c42bf29a719a4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52285195184, + "fee": 0, + "recipientId": "AdJLdk5Vkejk95ah4j3ZRcEWu34E1Q7wVN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022056d42be934431202be893e9d5e6d640d2d9f5acab7aac3bc40017654ee6688a902201f407d488855a6208aeaf3068f109a0b5de4abdfc4233e8bfbbb89ca3e33847d", + "id": "4912aebc2a0e8081a4761cdf85238d17a27277193389698d25200c3259116728", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52325513889, + "fee": 0, + "recipientId": "AJE5ptbRLg4SGn7DQcaLChpuzjEo66WTDg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa8e197adcd0a62c4e79017cea097a9e22a2301be0ab22fb3b5466846bb78575022044a063dd5987a6e91f6ebc4858754ca6f7e7fefcf53bbd3e1e08e20c3f7e14db", + "id": "58aee6b0df8cea2a4fb9ec25402b3440a8e5076890a8cba8a9ccef0ca5b52148", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52512522119, + "fee": 0, + "recipientId": "AR57nJGjbRknsTMoDJVPnKjeH7H8pQ4J3e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fb7872a819b54bbc0b30dcf4d8c3e413c84278d631628dca3a6805ec1231e210022002c76cc1e9b7672ad3f0a1db859e4b36564f7f815e494c5f9b1d601b6081e8db", + "id": "3f54c13b225a0c71692660cc5cc0910fedd163ff1aada08cff0b6216df414f4f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 54692496501, + "fee": 0, + "recipientId": "AH1HPESny9sMqiJz1ySzoonvcgVLPNTVY8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204b168552ce3f30befaf9a566986b6c17f67471fa0cc2ea308acf7e580586d58f022017a5a0173699a7a5440f82dabea25270637d7167372d9aa71ec62b09e4c1860e", + "id": "b1ac5258577f86365c9c532150696897e0f5b304fc755657e4166b7f784adf79", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 56071540498, + "fee": 0, + "recipientId": "AY4gFPmvcG2GfTcVE25mqeDmnDQMPcWndc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203b1848de0b3be4cccfef6ff105581675c01006ec69da4492815ed63c194338f30220377f301d3fffb2a88f7ed13d42e2231cc5984f63775f082e3c85e02b37c1743d", + "id": "e1154407af38c6fbb875feb1badaeb5cd8262821b3b1b1a441cf506aaec41d4c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 57049922288, + "fee": 0, + "recipientId": "AQbcTN3TDRc57j6fpwAvLjxsc9FeAJThp5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dbc5ba06661abfcadade917512e640a45ba57ff29ca7fd7ef728405e87f8f0cf022016d0bd3ef6c0a88d161096520dcf4673dc2cb8fc96ca7e61095ea5e67465c8b5", + "id": "3fd968cf660e948f9fe340334684579db327ab1d6281ee05a840dd1deb0e4b7f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 57589490347, + "fee": 0, + "recipientId": "AZDQwckNFvu5RL2gP6x5SfNFtzSk91tPTQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e3824a2385b1f4133877047b7d5e702d269c82e64bf96196f526a063047d97120220327dfbafefca990f871ed25bcbad0bb7308279930468676e84c54d701890ee15", + "id": "d1d7712844a3898546271abe9b6c0d139b3d322f436d26d7805caa0a34e1f8e3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 58900000000, + "fee": 0, + "recipientId": "AZe1BULQ52K9W8Pm2oz7WnS8EFsHSmtcGa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cef7f753f6cbce6fc6513cbbeffb4fed56e32f0f6632d87f4471d59841a8af2c022036f7202c8e437be380ecedf8f1930d38fada608900e93d33334ea8815c4ada2b", + "id": "00acf4f97b4d6baf38f7a16619a7bfb62d93a96cd30f5bb081fa15099dbaadfa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AFuQS4a6wysBSmZJpMXq4xDNAetJJBAFsQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d8865a56a63f070cbf21dc8e48685ecd862077ee52ccd2c8cd8f40be72c3c4d3022057b11b12e968dd5e87df9da6928c6028a131b59bda09ee07a204dfbb9a055844", + "id": "d3ac3ea41460fd549c79fe3badd7a80ba4d467869b62b6b4bb342d8db1570efe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AeWauSzsoZ2FXZwP7GFX92pPLyJMSkSKph", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204531943ac76998e7bb5987455c901b6174d603114ebf915775590c8315653896022001eaa07b36c01e93b07c66aa139cf299ebf25ae16431eb85611dfb9cea9c09a1", + "id": "c6719c892816ee2bdd2c113614cbbd92a59de7b54b3a4c17de68c0da22256d3e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AMmqT7w7oXLazLwQcSV7WHnYx9S7KSXixq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200e9b8f5aa7d2471eb1981ce5482230a2c2fd1bbcc6db017f6a1ddee9e4e90eb202201ec640c890050eac7108f74d40f010d1e7d5f9bb05f2fd17eb5d8fe385080342", + "id": "7d269a6a5a9c507c1637707448d5fa61324f9394471fde06b0b53bbd51b0cac3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AXdGv5eEBNGK5UTcRaFHbW3FwEiXifcegt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd07c0502e712b804344fdc118b52e2bd1f9082c52aaa6a87141f4e46d42107c0220718a1a7d58f887e544c92404b49824154352139fbe33eddb5a7dd5e4ef88d7ad", + "id": "f0425c83dd9d9eaae6158673566fc41a1d4dce9304fc82703c97463b4f93db7c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AYA5LBnUiYXCraG1jz6XxGe1FbViiJbj7n", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022036479d87611d110554a7fc93c08cc78094a769674379c3630c8a77498626c1b302203712a96621cb8133a43789b7f4c64dc6dec8de903de7c7275582c1fba30007ba", + "id": "2b65f2d5ba1c79f39c61a99642af209df57514ea158bcdd790c637d6287175bd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AVf5Nefkn7H2m5ccTY2Gss783w4B2XHrZH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b3ed017b9c924a513e4175d1ae026e1f6e0cf5062d8abf944a1d456c9b99a3e902206e81a45c621846a3b3975fdbc63e842926e2f0736a8cab98e382fb2027309e09", + "id": "afc6dc49249582cb59fd0ef44d5ef26d40d7a4faf6cec4edea6410396511a57a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AZmiLVom6XmmQjch8mPaZjzbiZt7xEJ4Ux", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022001981eb483fc7c992ee3b6491fee84e92940df9fe96655b36a451771563a94f002201d0494084a12cf4de2441081cae004067d1d43f802c7c661bd75d0c8882a6d9b", + "id": "c177aef927d06946f2c80cdb33855fc233d0b20a5bc0b70a72d3e1f6076e4e4c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AUs83fWgyxfn3vFcpr5DLhiNeCrRcrY8YL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022025c7b0874fc7eba0e23e003d5172490a564e3eed0a504861ce0346e2782a28400220165dc08dcf6720694b67de43258f127630a4d4cddbec8a2f66ec1ddf853d454c", + "id": "6e6193eb69cb0ba573c5545757078fab69cd407c3ab4499ab75985fbf378634b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "Ac2vFD8sqzixk7MaK7JwEYoe2QXEPna1e3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ee6a0aead5de861b29caaf1c876304bd9792e917a5ceeb1164b69aee6293727002201edad412f00a6efa6148a6a62d7148466f4d24ef343e406b5291688a4270b92b", + "id": "ab0da3b48e067dfbdd94c6bbcac4f3e468946a508735dae2c7cae0ce051e2087", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AbJSiFHgiy1Gc9dnpnSUgRQVu7cmKUTJFo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022007df858f6b336a855e06e11bde5f75b4584aa8941a8d61ed54b91ab8a76670ac02205fd2ca83ba0694a6a5ff2e552330616ac9896c319a319dc9998f5c6b57edda65", + "id": "6cbc50accd6cdccae73f205c0bddb4c944bdba76323c5100ba6331f89d16f952", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AaSvUFxNvGZcvxk9ncd7wnqtMw69qTF2NG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ecadd5dc516c178c2d80b3b52caf05720883e07c0b28bef1e0a20f27c0607900220178af5fb06c3327e6b5c6767c848bb2be60e311ed477bd64a56caa6e66dfa13b", + "id": "d1bc92a487e9202d245e42abfb7e37f60e08d0a78a3126624dd0e0783c2a9238", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "ANFpv6FWVyodGiMNwfqBvfNo3tt59q2uwW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022064edc033b3ff422b695196c4a6d3a0bb6b321e467a2771632816d92ad05af9ca02203afca6079f18210a50218dc05956311a77b04c9b5c9f7c960f0f796c436c92fd", + "id": "a3429a6656e132eed4dbb74b2ed17238caf2790187166ecf4af877aa2a7087f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "Ab9vTWogfBcwA3d9BVdHLZ7RVasLqZ7qwg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100923954a62e4ab7684af8694637e66839fcff5fc11d54de68b4fa82f7974b313202202d89f173b1c62ab26c6f9210a8cf5b7d12538124c7366c6c1914f8990bdcb272", + "id": "6cb18de69128b7a4c91bfce46d17227f1103f50f0061a9228e2488c58d26d18a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AGonR6i4nHcvrHN95rsmg25YTgqRm7TBox", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f906a1d22b886f4198c8e6e8729d55f15fbd10761b445f92a2b7748ad4ed0295022018b9fe3b0e95e59d612119d349472090f1a78be9057c754dc17a0c23610247cb", + "id": "4c650df3dca440c5d3f2f17f89fe1000e114a60df15a51190134eac8feb777e0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AZp6btpTxvxPCWdsJGUqZJCrZMYrS6cCk7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c8211a6578d9f11108a1fd950260f2e30c15c854c5cbe33af0d6ee329a81bfef02201fa7a8226fee5217097353f9b00c39cd6901e8cd2471cd3d3a34daa56958dc5e", + "id": "9ef08f0bd8beda3e82d6c39147005bdf9590f6df478e87716c5c69942f6e34f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AQT6VvbyD8BaVSBtkq2rQJf7ZFxpQMrGFF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f989f104e83e5091791929078b408a0e6c56d86bb8c9caed517293bb377a92aa02201bd438311e99fac1e31bd4c031ebcb9fe7f05de8a330713be4bd25f5effc2e0e", + "id": "30e10c01d2e0a376ce6deb42cedf146f8a849f9ead0170286e7ae6f5ec14af92", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "AJRzekFXBecN23nD3H17GCvU5ykTqZ9Qu1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cf8ca12989b34d74df49b7f7db16c5d29d5c94e387890a6dc714628ec737f7c802207eda53f3c1d3f43b62598b1b6efc8de7a4b46ffc069750a90ef294b5a5d49fe8", + "id": "167ef310bcb21341cc2b7c32b1ded135a3223f5322eb438751a5081fd8a61c1e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60000000000, + "fee": 0, + "recipientId": "ANVX8UQEt8MkJokm35rGPiQbkMD6Kc7KSt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220723a3f4869ece9ec362b11c309a824ef17ff8287d3a6e2f1046db473bcc0faaa02201618ea49f81bf73c60ee427956afaa75826bf94788d73319c8810ef2c4538427", + "id": "b3734997009c33befcf1d9d3aad7917aed5eed0c4c7de9730ecba54a72db099c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60600000000, + "fee": 0, + "recipientId": "AVFpsz7LiwhRJmCAq63uMCFBXhXYaMNAq8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206c018a247cc32e4807e6fdc62fc83cab278d50ec7a4f0f67e26094387793cf7b022014782a31c92672ce921fe86b3b8cf36452e40b360830f47459c3fd8b26cbe5c5", + "id": "072dafcb1b3e774663b9c7be4c1c2054f7c9357e3708b5d61dde8104101026de", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60600000000, + "fee": 0, + "recipientId": "AZ7M9chTUSjuxSUkWVDyQhAbEDVXbP2ZvA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207ce6a75041112fe7cc42d42ee0e35cddbcae0f33ba051c425b5d3afc235b691e02206092e7c04965993b38e860019c498968baef52f05160e492c5bcfda86a868e33", + "id": "d728112b0c69a9c9f5a3c1800347aa8f87de5ba5407fb16fed5e1079f9a48ac2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60600000000, + "fee": 0, + "recipientId": "ALMaBHtP4Ki4epwJut68XMDxYVRXTwhPuU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022050f867d1d89a62e65a88c9861c6fa93971ea08bd0564d9864a51517244196866022032ba4ba30dcf55cd08834085ddcdd425eceb7cc784f4459e62c11e70f29eed21", + "id": "996aade098c28e002949de9eba9dd663f3d9a29a8042dc516142bd56c91f1b8a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60600000000, + "fee": 0, + "recipientId": "Aapmg3eAWSv4bWzPYMNUWMx2cULimBsKDT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a76f37bb0e4c4eb508597231f437289107f7f9fd266c31c653c2e10c45d59cfd02202bcf4f912212e7d409bf2a425d73fd9697836a63019a064e6ce2ce8f174987d4", + "id": "544531e7295c7333e4e770a010b73b7584d06751a7d05388f3a523080299f929", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60600000000, + "fee": 0, + "recipientId": "AZHUEDRF7B1wHT4qcg66t6f6AZpmAwnQzP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022037ff7edd4d48a58cb2d3aca7cf2b4a6f047b5e0ecf930c6624deec7e6e7ca22102202a038b5e27f88d596acc94687529c4700fffa41e907a34118e4867c012083b2f", + "id": "ce8ac13ef4d89b3c3e9cb359a4e5ecd8ece85313dd6ed08e247995fa12ae92af", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60600000000, + "fee": 0, + "recipientId": "ALMjKKEK2z3hLUY3XubSxkAhzdpU5i8jsL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203589acf0d84cf3f231ae931522af87cd17ba02138f1b5fbaf71b1ee6f530451702204881f4bef7736594e5cb28e5fdac8bfadb467fc853c58ca7f59e55e7a7b121bf", + "id": "2988196f5d08ebe98c6bb9de0830e0675c85fbafb60a2ab7168a633360c112d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60620516154, + "fee": 0, + "recipientId": "AWFzsKF4dALGbqzQgR81VbFBDdTSUJCBNU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210083abf4d927d402f0aec00e8c37e661e8be625b8ba47b3fe3d604500c358f88d30220645351b976be790dfeec71082328756d3610e33e57cb8b396657c69fff96feb5", + "id": "a68385459023327d26fa0e745c0891a34789bc3db0392294cee99cf1ddc1b190", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 62300000000, + "fee": 0, + "recipientId": "AGikMwhSBsgXuwCUZsv3EAkxeYSsHCRany", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022014e9a28ad7815e15bef49340133ef40a0ec92d8dfa80c125eb0c26e3d47ca96a02204a8ee6d3f847fadd1ccbf50e54d4a1f220ad28125f82cc1e653069761e953331", + "id": "71a75c1380f47a79f47847fcf615e733c9b8e4d44511490729cbc2cf7cb04b3a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 62614203690, + "fee": 0, + "recipientId": "Acu59K3HbqucEtsNMMLzogU4tFJxmuJkCc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202448a56483a3ce4580734e2734c1cda29fec792689854f77a3bd2aeeb4b395be02206b17cca51c241b725a9f41d7a7d15e308f0f94f33480fbdea4f4ca2c99d01482", + "id": "d71afbccce8620d94b04bc74c40038eca31034383a296570c0b3181b00433bd0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 62821647096, + "fee": 0, + "recipientId": "AGg3tZ3u4gE8mSM68gmeEiQnjARwUpT2n9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022011162de5da032f0a5e15596ee983ac470d62876eb2e8348cd015eccfa0b9831102207c6b60bae672fd6e593b3f5040733dde21c7ed75741a85faff759d97f8a5e27b", + "id": "ec44a065625a762aed0e093f485104dd5cca3e4d73fae79d6ead07ee6bc4704a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 63651541963, + "fee": 0, + "recipientId": "AQEsgEhmEBzGLwtXeDzYacpgHFdmZzgxBD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210099bf9fcf91b0efffd75888770663c351f6e8158fbaec8ea99fe43382e31ad5230220575000d242528aa103ac912daf6117cb4ffabdd867fccdcf847b46d6e46c0a12", + "id": "130c08efd433f91433f3170cd7bc85b484d2c3e3c53134c0d955932726bc5338", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 64100000000, + "fee": 0, + "recipientId": "AL3BcBwg6R2PZ9pmnYe6wzeY9xr2E4Rhdi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220684f309ec0d8e34d82365c29e7ea21a08e4d044bf5a96e17f692ea4320ad12210220155cd1bce15cfb31d60d097fadfd6aec6bdd14f65c079039d9432ec9c7ab91ce", + "id": "22372757608284b2b88a6c068c4dc994ca37684d765c14f5be0bdb48ad50e968", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 64100000000, + "fee": 0, + "recipientId": "AbpU1DmufxiTYMDQrbKjeKumxoVPwKyczD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202f40051c81e2bf95bff6cc78fcf9cab2b999d48ebd9b6524b593c95441902d9e0220347c380e7fbbdbe5dd4ea8492a5deb17835b48ac22f388c2b519fd4103fff0fc", + "id": "8726a184e737d57567c17b077a424fb7ac9fe5b0cc9acf509e3af87baf5167e4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 64700000000, + "fee": 0, + "recipientId": "AR51dQ3b2iPD7kxY6Jr2f8nvG9V1Tp4bXn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e701b0a5ae50d35cc985776be08a2af2a1189df4a7dd2bbaaf7b182ff5fe5ebd0220123def90996bfddd4c0769746130d4e13a8150d75e32770b6fc2024cad56e80a", + "id": "9f890472facb44d3f0fc91cc3100f33ff80a0ca419d6654479c0ad2e1867401e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 66023125672, + "fee": 0, + "recipientId": "ALMVkKVthbGLSyCAu99UXPTcJUCgycJCio", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204c190c4ae5a7885bdefb89112f08f782934fcd60fa2fcc0ffb5a57fc25589f0702206c086bd249bceabc559d1bce46a35687963c7af245e982b83bcb4679d27f048b", + "id": "f0db655b4c6a0cce96585893a9ae0b5834629b471c8a10bae2d03ba7b59a86a2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 66682567770, + "fee": 0, + "recipientId": "ALVWWv96ePvZpsr2LJiYkgczjp5HFh2vS6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d4223e4d6d86f362b5d9bbcf4887e3b4a7905504509d7cba066761e5ab4f2eee022053f2f8f1e12ea2c979c1ff532d2529577ee1d5128e4863e4b88081fe4223074f", + "id": "e49671791471089e19114260ecf0d0410571a828831fc693900129992cbdcd32", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 66985670351, + "fee": 0, + "recipientId": "AK5aMpBxSFkveYCPLK7655BNCYxE2oDWTj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022030059d3cb8c99259e250803496094fcbdf5c644a54be5de71ed67579c50dfe7c0220551dc20a9ea8fab2756d945ba96b6df226f786f1517ba500808990535517540a", + "id": "1ab441b67ca4f041fd0b4de0ee084f47808fc41327afbf0db652ec6e8eb0293d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 67500000000, + "fee": 0, + "recipientId": "AZiCsJNur5AHuBmzXFm8hrvzMtxaXeBmTP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100819f9e25f210711c95d4f5bfda34f90c4cac0d2ee7fc788867059cc8437153a302202f94cd63aa4e59b051dc69a1acf46cd905be00b8547d377f705f6f63b3a61119", + "id": "373da2f2c548fb4f41d36f7b39aed883804bc453846bf42ad223c7dc22f65903", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 67922251265, + "fee": 0, + "recipientId": "AaVsZhcJWbpsrwHFYQ6Ms7dwRamLQGbiEQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fadc2d82b1e12c7ade65115d3d2a80c59be0570cdcecba6b6d46500de0b1723f022027b16d7f88841422575354d423d0cde46edd1ff3dbfe77cec29aa3cce7ddb61b", + "id": "b6cebe893f177fac2b32bb805b3a8b6ef79dcf42d7e7e1e367da46fec3c6e659", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 68047923959, + "fee": 0, + "recipientId": "AcfGpnqyxHTTCg9qGGDNoJZxp9gP3RZJEs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220056ebdfcf97cd26c4f1ddb8d2dc2eda872aa41c72122dbf4b1fb7dfe6a13a566022066e8ea897d1845fe11262d60550d930d4ec0be1408c874ae05b6705838499ee4", + "id": "636db58a8c9e586a4eb157c7b2be73af3edae8b5ff48248933970e06cc14ed67", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "AdReKteT3KZV3nxYeaUwbxuvNu4Tbux8QV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220045e674e2020858cb6db65016b0721094b18d69d5c40b21e420eaca23c7e5e530220581f3f511f5ac357d3dd730ebf0e6084d62a9aeb356d1317463799444ed3a327", + "id": "194bef1b8604a0d86e0dce346149d9a67dec990def05321fa4ca2f5d857a3b45", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "ATBhoLR6mCACu9T9iHcvPmXBbeabR9o2Af", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fe79b98c6ac6eea23af36c1cbea5f5ce104ab7e8cadad9905bc86c36555e552f022050dc6c3a5334e5efaed6f072a584319c51dfda211522d9424561e0d48bf5d7d7", + "id": "bc5c5cbfbacf7ca885544dd956d82dd4a99d3d9a9b922124556711c35aeaef1f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "AULwd36ELPQHp3MBCVjQPGsG5vLbWDwsX5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220730f890799382a99a6082648a97e89117ee5ea2cf79405ddbf726c8732a48a9a02204791eb8cad7f9b987dbd4865a507c87d446b502f4faa5b469ef938be043e034f", + "id": "8de5f8c57d06f1a26d57f2b30e1a6de70c1f4181b5c7a5ac46d4d77d4aa0d1d0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "ASWtE77ekytMEB82Snx3iZeyECBwcyYAGU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009e5a8e6261a7c50f8cc6afeaaf51cf433a55a2ef85545823811c2bbca752046402204d879e4ff9709ac18288c75ff6b2d0bf210d4466a7c46d82d688727b3a49eaa9", + "id": "a0d0bd4e4470ad7708a1b6a8d8c26c5ac9de239b8e15a581cf2a29699648f944", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "AMWKeZaKAZgtvX3McGVZGVCjR519mjdxod", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e910ea19a113d2fe010320fba9848358135d3989f92c328a59d9611e1382a2c902205e83c177d11dd68147d97a9536fb8657b18121c69e3257034a101c7c928d4cc9", + "id": "622e1654011fa90a79be805a43dcd875b0d649c5e5d455285c2103430f5329f7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "AMP3grMYVWSxx5g7KdRf84PjvT3f9NDiZ4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100acbdeee8bdd4dc28809d30dfeff20965f48211675bddf7bed7a105a6e04092e202202c61fa35cc87ab1b02a8530b0021a8d05d2d20f34520637c9e17f60e603ac2b1", + "id": "9ade4fb6bd9dec55103ea68751040bb6cadbdb7f7518442327d72599dff0ec36", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "APyUPnRwawq7LHHQbuLVwEVvviUS9gYvqf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210082e9e6dc9eccb3d40725cd5434128ebb94f56d47bb864d879a79cf6a6d7d92e6022020e76a058819f2d1afb709d1262835bd3571112422f885d1bb5dab06900a41dc", + "id": "961478ef66c79ff2bfe8f9c1f7f530c1111e998ab0f36386ae9b5d6a27fe2875", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "AaygtE13tPoD7HTNBJiJQ2McDRxVaWdJYw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ef82c8eb76122d5557befd50e52d7c297ba3ec741cfc050fee3a1ca10d3bd01a02205b199789061011b890a09029642750b784f53a174082d8e113a43083fce2cd24", + "id": "5291bc37168adc5497986964e7f44a553aa5007143cb056be070857b285cdb0a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 69300000000, + "fee": 0, + "recipientId": "AamfrghtYBbWkQtRV7vohTfrJCPv8Sg15f", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220757f338c7cfd2ba9342a5246b2c20ab46c2fab13d8b8475f748ae596d721afd702207dd4f45e4a0562765392b4041a812f3caba35985ec8b0a27f59af91063ca0ff4", + "id": "49f82d3d4360a92c509afd96a16e76adb8cee98b82b17c9ef0d8403a7d933b8b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 71253891180, + "fee": 0, + "recipientId": "AHcjnPJA9tdupQcjcJMLtD6nnWJ9e6KFQs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009ac53ee5d6f50ae5eb6021aae45b59f77ff49b259a42bb7b072d1c9506c0063602203238177cd22e6d418db707699b4051639c4c530283d13a02c8c9ebef4fe9b4e9", + "id": "04069fc9bc169922314401d138568a956bfe70d8f3658a5fc456c33ca10ad67f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 71429604399, + "fee": 0, + "recipientId": "AYFJQE3EPeSkuxEa4PubKaS4eVitUtZkiR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200add47cd5990fcd49c559868724063fc58cbdffe8c79aa958a7ecbc75a603bf7022017f022de0655cd62e383d4b7d9d4a4a074b8222ae7c1bd6cf26716c1292bb3ad", + "id": "06ed48da4f8bfc579c4ef2298b43745eb77529889ece543daaaf68ff2bdbb9b3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 71590501751, + "fee": 0, + "recipientId": "AMP1K45yauAQU1c2NqwuUu6ekCmBqtaPoP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022014db9f5d0e46fd745c970ca39b54ce13f245ddff480e7abd19b318c262f3b951022062ca17464681bdfc5381876ebeb66740f14ea227007c34a4dfb1fe0946c6e2ce", + "id": "34598c7779c1aff18a82cfb53f52f7cd9baf51bf2fd9eb2d8088f38e9de615e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 71600000000, + "fee": 0, + "recipientId": "AQRhAZwfV8rwCvsb4WM1bVTbenugJn3c8r", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206b2f68e67bc4218888b389cadfca07d954a4388635974d1c4872819d9fb4305402201dfaa7ca2e83c0694ab80505ac5282ca28ae45970161acb774b38d87efaaff22", + "id": "e9a1f1079b09dbc72a80b56de6c65f7414455d7d569c6ef55540286c6e6cebce", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 72653688612, + "fee": 0, + "recipientId": "ATBxkNfkzM8NBxdCsJ44RdABoATkD98qdR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ca29827b5530b9846ee5a9084d75a067fbd9a932073e731af57c460bc403b5210220487342cde09bc5c339223b575a8a4c325098202707c68d09e0ed48bffdf72f1e", + "id": "11ecc7e569168d579cb6273ce6d27f58f29fd4db7c2897082d6e49cc6a9bbf8d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 72700000000, + "fee": 0, + "recipientId": "AW5d3EEDJBQnKFHkPJnKPTinSivnBzUYkB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e68f134e251d796c0c4044c61881a7e49f260d17aef7fd2f4d54010d9fdf52d02203dd02402d012f4d91946dcb449f95d61d874e02ca02ef4bc2c36f7d4eb5b4547", + "id": "f38d068010566f2d81f972525779b84f6e8693880ef6251b6a63153a2e98d9ad", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 72700000000, + "fee": 0, + "recipientId": "AMYvEFsHMcctwEmRrghWE46Jj9kWbFPA3A", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a593780b9925611723664234d85ffde90f314afb68d45e4a0fccb00d40c1bef302200ddaf8f1677d988f24302a388e71d069149437822d9c7d2330fa037f96770110", + "id": "b1d8f7741c54407d505bcefc467f1daab79772ff77e71bbd0ef7f8708ae43582", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 72700000000, + "fee": 0, + "recipientId": "AR44YGUBeJXZDQcbUebK7Ds8fVHy7DHjgW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207ac0a6b2a5468d60e9f0290e5ceca35b896db702038309ffae2a410ba25f2b6902205d3643b2569c0b185f2bce2a1539f5a1cb92438a1e6efe3512080712fbf4774b", + "id": "1bb883cf35619f46d4839ebaff381c9ae4e00c3d4cbe981acd00bd98a1c4ab91", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 72700000000, + "fee": 0, + "recipientId": "AM3F4yHDLbzGGqJ9uMCjnPCv8xzoJ872sp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d6ac3a85f45bd763fa1fa8850dc8fc12978155982c3dbc250a5514d2e4f6081502200d08e59cbb1dd6bd22a7ca8375ae7bde3dc2499367482483b84b5dbf2df4a0f7", + "id": "e46dc48c6353759f1bfd73f83112c808d8a2ebbb687fcd177002f7facc3da944", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 73796297519, + "fee": 0, + "recipientId": "AGcoFdESM7NdWm1HodtJyDU32BHyShDqat", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022071e03ef704b56aef2dc368d7ca2642275dba4b81eb085aef6b8c1fe48307006602200a4354795dd801e4752ca441187d98f25609ac9b58a0ab79d2bdad3659db6ff8", + "id": "71e2650406959057c6503cf336a99c7be97b592eb47e383b03a334bfc3cd5bcd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 74014667709, + "fee": 0, + "recipientId": "ATkzTWGZPHNhEKCToyULmtTaxSJYNiZRYz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210096a5bada156e5b921023bc5d2e79b9cb6058c2a6b722674c3bc04ff45e2c2d0f02202a804f965b55887303c2f15ef22bdfa0f70639141ab4db6b982832b76e5111d8", + "id": "ebab1e0dc5d5a02577ba83b3bcb0641722dfd2f08acde9ff14e7f4d5c5415ecf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 74124099984, + "fee": 0, + "recipientId": "APpwmia2SFKQmhNZq9zxav8aidP5vBBptx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a4a58e349be3f5d8da0b00d71bc150261f2538a2031e06fc8dcf0df8afcb6865022003308a299af829c6bcad2f9bd36c2648d96eb3a3bb8b0c33e687b33099cd1fb3", + "id": "0dc2b86a4ea26f04660150949d00091900028bb6fef863392f2bb3110e77b498", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "AWshU1zde3iSo4691GzJ1ibHDCqA8n7mGW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207a0ed602d07dca8bbf2b4d9458a0dc70fd92c192c2bd05cc455dab5977be3d48022044f872dff762af785e654198f8342b6083e66e9c0181a16c3c21a1f2423b982a", + "id": "2df19ff6e35c5a76a99a062d893432c27fbc7e98e6d4df4ded66109311b58744", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "AVWUFjyvKxXZLYexXDWLiJLuLXZ49Ymb5f", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210093a55677a758062eeb56a99d5994dbd886a2bdd33f76287fb9dee7ce6f25322302202ce4a80a24aa3b75d0b8dce030b04985f3b625252ee8411223735ed013535f1a", + "id": "40dc85e19386d391e0da2c9d50cec7dbdef55e9777f8e0952e26ee7c9a2523e8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "AL9X3YsJksdtUqAMS2yojWG3rThNH3rXqc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022007d901327f63e75971e3a5a023ff97c2513cea96dbd4f4d2969fc33fcecf3c8d0220606584131bc5a786e15e7a7fb7e8dd752cffb73c578b5933a62d6ea6072c4ad0", + "id": "9d0d26d7fe267421fc7db3b7cdeb2b877696d056dfdbbd4dbc4fffbd052c5584", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "ATE6aerp28bsHn4g9uhKk4ncJiGtKpuSNe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204faead30048606f6b057ddbd4ee76da47657a853cb8b644e588c29d375c9f2ac02207785f0ecd77c1af9f8f0f68c5cc3d344e4176fbf911037ec8df681e72f5d5f09", + "id": "7f2a17f9a14b1c84cf539431573a00a63d9059051a58ee50a3ae575effc08bd5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "ARc6St2Vj9T6PU8JPMtUn7RBb3DuM9uE8p", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100de6a748d2991d40d71bbdaa2de5cfd9cfef49c4bb9965174e0bdccc58cd7e0f5022027ed0e384fdaa03164d3e0f317f475d6f7728e1747afe8ba829a47edd3f39b59", + "id": "ff8a36e128dfadbdfd48159c0b65c608702425d36f3dbbe0db0657a80cae66de", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "AHfcau8pKsQVGWzFCWSXDyoXGGkTKvuXsK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b1a48ab2f689a43aeb5e2b8d59f6e777ef945685301e64dcee3325bb9b3ef46202204b125c359a5d00bc54f0c0d0d3580a9841c73cf8e2ae19fe3e9666587a83a143", + "id": "64e0e887e60dfe4cf3c0a0a00ce3c89c5dbf6d76607e6c1bec2c8e65611492d6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "AaSGhysJYBesZiFSf4zHj986A11DRvYtca", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ed44ab5a22710c41eaf06885e3b3801bc9234987085adae32f7277d1c31dbd51022022a855e38b1e57ad064a4004f37597ed6d31deaa6378e06dcbee180075ac0fbc", + "id": "c2742c309400d0bc0e4cd6b4a183cf9c58d2a8d00ad67564e6243e5019ce79a4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "AS3gXToMKTMydG7jNvxUMNxygvUc7okXXw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d8b5a57241bd6982060e2a5260a6d6a9ad076c10e9a6b863503686b5b1d9ff0002200933f2e858f279ac24011d5008fe4a6ab99efab3ec795954a5b86c121fc09a90", + "id": "27fb7f36d8e0e19ff8caf5f74033b5912eec9611c439561f1d3edc37537c569a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75000000000, + "fee": 0, + "recipientId": "ATS6jWiztxiZw8EFr2VU158ZiJev6bYfiF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c434b9605d6bebe5950ae910388235326b701d218a72f630a523386a3d085fac02203ba1a9016a6738f0d6078d89d7e481aef5fb7e1c698262a345453f8978211ef0", + "id": "e78b60e89f9757af6f2193efd826c7f4e43cf55e992abb49a49fc6efd8488ffb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75435988442, + "fee": 0, + "recipientId": "AHZ3K294SDAyoNfoBX7ofAvxNr3u6bt2Tb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b700ec2b35d2a4a0a36802e503a75794557148e37121ea7983bd3bfad456d19202204fc34d8b6fcfd513b1e7d74dbcde9963eb606b234897f1d8fcd1024d236a7118", + "id": "c331e66c718f09ab4d637cd139a5e0a9620b0aa268b8548b51f82113a6f91961", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 75912885491, + "fee": 0, + "recipientId": "AL8QkKgRswzkHyPfaYuknd8F98onyYfsw7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b2b7083ee6801b455e14c24c1f739e1f19f39d429054e86c77475d82c54be2470220128182d6a3c7b36eb456a5e2438b9cf7579b981feef78a31fd71e63200ca1959", + "id": "9f74b82aa9272226bff1637d566a3e3ce213957f053eb6fb167ddc9d1b8833d2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 76800000000, + "fee": 0, + "recipientId": "AcHCwqRbb4vneRguSP6SXqFq9RzUt8CCto", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203e95f92a3a0ff1cca53e0acfd410a6204074cc52135671a1cb1ab193e7e42b1402202cb648ac7bcefe13152483a85f5cb54a1f2dcbbbdef70fda2e9744d210692614", + "id": "1758ed13f7ab2c65bf54bebfff06763f6955bec8836c1bb84b9277960aaf3cf3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 77291158098, + "fee": 0, + "recipientId": "ALikhBgeXghUsmNXXwXD37SPJJKP9YKFQZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022037329d395aa796dec8db0f41eebeec510fdce6cbcbd5bf429bd308392def4c6102200f20bb5f7d5849d26ee324ebe881ac7fabbf3c4e7fe5e93605c0f7615810f50e", + "id": "144e1c573644f0461ed333bd72feca9b94c7233763ef21f196cc869937bb5de7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 77900000000, + "fee": 0, + "recipientId": "AG755Z2rSmeXi4nXc2Dp5xjEnV3h9DSePY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022072dad4689c20a7daec81141e12e12b48ccd71072cd2c6350321523515830f97302201e442d4448f5a0fb02e73deff73b0f798262beeae00d47c8f8a4ad94e041a4a3", + "id": "50259feb79aaba9640ac99629d6dd7cc84a43c14529be5549267c3fa219e071e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 77900000000, + "fee": 0, + "recipientId": "AX6JKGAGAURGfwYoy8r6BqCsiHibZQFtSH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100894bcf3faf2ced40b12da116be0cebcccbdc17d822fecf2d52be9dad273dda8102207de8bca9554c27b1a37e7679c1b8ea4bd75b1b47697b9535789d5386f2a69289", + "id": "c6a38a970ca5a17e2632b958bf0f035c4760e2320fe5991c19dcce0fbc2ab0b8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 77900000000, + "fee": 0, + "recipientId": "APkL2ZzBb5Smjhv8f9ugVgS8iRJP48pZEs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009f0a965cdca67e25a459cae3674e261d235b631393a521c95118260a50ef817b0220667fc3de471fea7ebe7d92776f8d0358bf1cb6e480c1964f45d104ee9ebc815c", + "id": "b11d02a6be882a82b57e34d0876feb673843ef6baafba06421adffbf34512108", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 77900000000, + "fee": 0, + "recipientId": "AaSzFFxYD6brxjKMaz6PsLM8Mgy611VWXU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203968e6322e183b266a578d5f6599b707c74b8d35dfd2722b1e43abe8850434af02207e4a7783ba60cd39d8503769f886d7f699b673cc1811879f37c97e000aee180a", + "id": "8a51fa7f53ee7372961dbbc9dae868c1111748d769488e36cb21c575ef3e9475", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 78500000000, + "fee": 0, + "recipientId": "AavMxN9AF7pJ1yV2qmWjYeihGZN4QBVSTo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f4b220829fbee7eaf190752898c3bc55359ab757413f2acb3d8f3f3c154e9206022076632671be2f123a47cc6b18b1f4ce8c5fa6697247a7b4a4da6e87e9c3810585", + "id": "becf405f252fa1c2c16031dc31feb20317838c2995476064f11fae9e7b3f23db", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 78806671001, + "fee": 0, + "recipientId": "AHTeBUJSraRKMDkpYmE2kzCTdEMTw2iS8v", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d1db2ff69f14e7066d9e3a8e9d9325f9c9c840ca2f3cb4499b72d615cbc47763022067e19df7ea14b245626624b76b408827093fe02036142966ad7c91c2a847bd83", + "id": "98e5b97afe377508e7c517db5d694167a9380169c78903aa92c557d673b7895c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 79513639585, + "fee": 0, + "recipientId": "ANg37TzJQgskwThhQ526R6EukJff7jWXZD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e68c3dee5a9eb77645c958a81f8b3684485ab7ef7108eb29c95239d618ae6cbb0220779e760390ec7ef1700cc84b80361dc80c23df1bdc8fae6e2686e20f489342df", + "id": "e6fc80f568069d7ab3bb599d99e81f02ee2e8d85b449e37b2456f996f729f727", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 80575550021, + "fee": 0, + "recipientId": "AUwoiLFPxQimPnE8Bnp7Z9GVLGwNMn777R", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204d6e44f453221389ffcc58e8bc98a9a2691483f2e0713c1c0ebb38ca87236593022041a360781403cb8f434b221177060f206b3bc7bb184e011eb1e5066f1ea8d70f", + "id": "75dd1bd25d4413d6d4d5c2d9853d9c03283a2606365b1f0458d1f05d2923d4e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 80648439946, + "fee": 0, + "recipientId": "AWojAkunWdVit59t4Pr29yGWAdhCrMzeNf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022030ee47eac2cb2b80f4e22f407d8eee4c29733efd0e11daf7a3937bee384e44be02205695c43c6ed886a5db961b87b6008ff171ebc5add97e0318680528940ed0f007", + "id": "52f8f83b4a2346b02e454ecb7dbd9fac6a8d4c3f2596eccc5cc5c1444844a0e1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 80880602287, + "fee": 0, + "recipientId": "AdTfUqtiB3NJuhhw2T2xPYHiMJYGJLFXBT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e5957017f40d2b9a0a7f10722697b00e49c16e791e1debccb1c2a072e3d063c202205c19ad3b770b138245321ff7dc299ed5f411f3d9fe727f22b54d1165f0447b8a", + "id": "f8900c94b8fd4e9c80d07fccee69881edfd4c525f7d624f076522bf677c50c56", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 81355763706, + "fee": 0, + "recipientId": "AP2TBQe9NmoirGTja8cbSuH6158GFprhoS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206a6476bd2d646155d0bab7dc4f952a8856c7789a6512718ae6d8b7736ae1ba1802205fc63cac9eb495ef7e2426a64668b771a694826c943023891f0f46f5ea8d4741", + "id": "ff059b0e5477e3417d2e3ed156d6cc48dcd62cbd7ab13e30c5120f80900d8e73", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 81837696809, + "fee": 0, + "recipientId": "Ab5x44ac1h97exN5QJsXY81WfMTAJx9YyW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022014c66256aef1f3094558a0890efc5a0f22a25dbcc1ff52c6f618a1ba519eb5cf02207a79ccc6cc6e562604c2d8e11eb041c27ff824a2682de9f399906e5360bfabc5", + "id": "4caf5c2aa360f7f94a872c50e41028b7c83540dc65ed63fe8c982fcf0472c0b8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 81837696809, + "fee": 0, + "recipientId": "ATpmYfefxVXdB6raUbujbqqj3bx5kVoDfB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022073d07498a3e0ff75e0786796c973e8700139fe9222319fed425e96b03c78dad5022012fbc2aa62a479c88197a3656ec4488756356cb4d568ecf65fce5e8a15f5c5c0", + "id": "5e3146ffca3c3c29e76ffe1bbf28dacecf88566d393117d870feee892111cda9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 83100000000, + "fee": 0, + "recipientId": "Acp2FNSe4DwXdMvJxBn6iLGGMZnvmtaR7X", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cc65894c33488bd85d10eb2104a142710a36d8f4899c8198dd50db51ba7bc6c10220527d3192c1357d324ffb84a70fd02b848328fef04d26537255c4f8790283f138", + "id": "d584d07fac0668f426731192de3967134dc574c6fedf27d98118df0c796c2dc7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 83353209713, + "fee": 0, + "recipientId": "AJHws2iLF46S5j8JYEwSHnSvcJsfGmXuY1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207934233bc6857a2ec4440fb1d3d80c30f3e71b86c59438fedc0cb8f773e6d15b02202f6915682768af0db3aaaeca4e8154acdda3446ba816d4086636af89ed8726b3", + "id": "f16ba7a15ee70202fde2f172673374204a3beab555954cb8edb829214d4d1f9c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 83989725133, + "fee": 0, + "recipientId": "APwVRmToNQFy3vq4wiEHfi8F1vYfwGXMxe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f04bd60b6b64559d86587bf3c83f6407c20b0ba82b91db0d79a284222d5ccabb02204bac2bdc4e1b328c668aa33cac6f435b666cd6e3f907b91463b3f5b03678d799", + "id": "6cc3b4a346d67a46d101749766f64a6332dae291d148b969d042cc8d178085e4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 84900000000, + "fee": 0, + "recipientId": "ALaNWEzy9zhCVGrJw28wSV91ES1fhWZpQb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c27e16af3bac0ad147ffd8a884565fced7a6a609fea72d2b3e23a54cbf83e77b02205a8ad93c4da3473bec4315ea1917af5734413171b22df8d1f1cc14b1f0071f0f", + "id": "d7b32ba893728ea38b48d632302f22e87dab2c04a01cfd809413b7991afbc70a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 84900000000, + "fee": 0, + "recipientId": "AGNzHhBmg7JkSgae3DKbhj45cJvd3at1ht", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f0dfcdf262541ad079d15430a8057ca753ae9ad4030811913110356b177afc19022031c0156405f2ca3262c121a648242b65f40e6b9e02f981af090bf5fd83d77234", + "id": "d3173641dc8035ce4e2bcfb9d20ad323b3b525d9cab25a86f8c3ecd6cddb1c07", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AbcfAjLyDM9xKnnKyPq1esgwrpPffzuhpK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100868c42ee9b8707f4c5fe317090e9d4bf3ee8fa023fef5a2dd1ab6e5613b58c5d02201898a17c6bc802c3bf03522db36e4d23a4fd74e780e83aac72043f51dde78074", + "id": "8c0b5fb020e1c904661cc97dddcb5ab0a946d6b4f8e74e4ec22dd225ecbc29ce", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AKFXsYK3u5Yys3gaQVtQnARgQYr7K2qi7Y", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202298b4575ccaa0e90fc62851eeb490411cf463a7f81205b530d9abba7151376502205b1834247d0e24b428d211069e86d0fe7498dce5545c21fcbc669a04738440f9", + "id": "83b38ef3992d3ab5884eb9b7bdfd37d7f937134f238e0810aa807ec6c78bbd34", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AHiUknBbPYinqfG6pGkgJnLWXyH5SdiAwu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022068147f79aeecf424c969d66138b8cfeb1a543bc833d0c1dcaea2b361ca67088202205af3b0111769ab1ad9a03f98c25b3df6db75e352c973cd6ac2a5adb9a528af23", + "id": "8fcf8c21aaa30f9a9cbe2e8f17ec58e811e52a7cb71ddd76a824560dd5ed26c2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AGpULTAeC1WWDY8PFZjkzrbhwUqnksCTKA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ef2a3557376455e0d25cf3c951db49394f7b211503b42b57a239be0eb76b41a402203406584ba5c6b3f1c791f1bd54781c27cdc2e4f794b1c187e3ba05d8710559bf", + "id": "7c09fc8b85c9a9b231aaa24e4ba3149f2da51dba3693bdda7ef1dc584b6e2094", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AWhFiapndC1wvpb8o2cxWak84oFu4NBRMo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009a5bed09102a91a9355bec42813eb86645d93ef79c7eb0b26db2bd155008678902207b8ce7fb53a59078b53581a81ded88b791737d1caa5554c16a05a9c4ce15034d", + "id": "0288e9d30d96bde32799db384292c36aef36840fe738d86431cf001180e171c1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AJUmYRUYf6hksjU2eWExtQZuxf7ZCACSzt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dfea58e5ce12a20be120decac2941a279eef12189d7a5491856278c7d18edd8e02202aba95df77908c8f46353fb1c4bdbbb1d68ad510dfdb082fb7c7f74e3f4f573f", + "id": "eba13a92a631ea574f1e478f8d5612d3af6ce7f6d610b669a7b46b30d878cc7d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AY87NwPwRs4tYVYHznKKoqDWLqCUHrErYg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210098df44bd72c80c3ee825909494f2a973484d1b9d806e8b2d6359fd89afe5714b022002b29eea85c6bac9267d37d0ddc0f74a89c5833cc15f5b8cb5afc98355b6eb4a", + "id": "74d30f27ca09703402c3f09089ee5c4cb8548625974a0416f5efcff722984606", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "ASmTzH5eR9V2nkrU7E8J7G8Mu8RTiQstJe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008f15851112cd2825da273a761843bfe8d772c88da8e8108835f18758758e3bb7022040497db4055ffa5c14c2a2f3fb000cdbeddd5a8fab8a9066e55b2beddb34a790", + "id": "850b70f669c62b31665190b27391ec3dff00222e4e86757e04bb35cc01076357", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 86600000000, + "fee": 0, + "recipientId": "AQqd4UCBGeKi4kJZyjoqPB62W7Gqqi41YL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022065e858357c4f598f1cf4c26e20e52b245fe92c2c58fb66c986106800e106d02e0220071dfa1c61fac54e79288d6b59f7b15c7271269e7cfb4c93a76bd0a7c6858011", + "id": "dc1dcfabdef49122d646d50d52fe9b558803df411c765ccf04fc4d86cc084d9e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 87141991973, + "fee": 0, + "recipientId": "AR8hYbKPJYajTNZ9pnpHjWrm7CFqSFaGHL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c68c155894dc39eff0136d6aa9d7046964f2abbff63548e669298b937a2554dd0220669383cedd736fc3524a286ca3480001e17e6fdabe616b0290d151bed94361b5", + "id": "1697ab9993ff077aecedf05e5897d1366435f757709d0d4b8ba163cfbe64192f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 88300000000, + "fee": 0, + "recipientId": "Acd1WttVcq32Xv7agoi1KnoQZ3uhS3xx5D", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a6e73937fdb2867650c86e5ab5afe66407d79f38ccd10e9af6e6b9210852bd96022064e9f36604a80e8ebd292143d6dbb4dcde5fa48567c1389ab448a87b57a7edd3", + "id": "129f59b0d566a5081114b4a4a287e8d352baa05b6ecdf8efff73fd0661d92da5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 89405066332, + "fee": 0, + "recipientId": "AGzvuwhRjpXsCiF1acJxFnnXgNrMd8ekWe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205dd8167fcd2a31bff75cb1a6a98adc1490bdd1c8a073929aa05bb56ef3cd199b02202d18b11bbb36c91fde4ae756b4cf7dba8197433c8723835ba91b1f4424a40a32", + "id": "87618b8944a3032bb912cbece834559d1f043e7d7255705c6abda7bda031f445", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 89514072770, + "fee": 0, + "recipientId": "Acn1izrpJYNCFrqFwFQhKXgXHHyxpGAVTw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207a5fdefe87c639533e3a5ab01f52624f4d1b468ab88eedab0e523cac50513e1e0220388fe25747ea2764ca18cecd4ef8c3ddfdfd2a689effe830b22f336d1db292be", + "id": "2eb0bcffeb0067aa69f8438afdf76f91969faed45f26cd0c641fc0b11695145a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90000000000, + "fee": 0, + "recipientId": "AG1bTaALP6A4Lh7Z3pDDehv8saCFpf3RZQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204f0e2ff8ab6eb771ce67474134ca7a8b59f5d0fb421730fdd306803a548f867d022029256ce0d0c018660a1c3a9f38a7c627a07247a73b2058ae52a000fc06781267", + "id": "591c06ac993f8d1454caf4553330213b9fa246c7b8e761077768907b7f136dd4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90000000000, + "fee": 0, + "recipientId": "Aaq9zxgsq5heY5uGvHWpUanFsLp1aFYtzs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c537c102f6d3e77742203961dd7718f4441db6f9c73e6dc17c139c7639707592022049a206a019ec362981d3377b6172d499de98a4e603b2c6b6772e676f3161ce7d", + "id": "c66078f962373fe9663a4a89460d2cb8694aee100ef510c511376b64a6129d14", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90000000000, + "fee": 0, + "recipientId": "APdt2CLhn79TsuFb148bXN4yRWDtxXByi9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be4c6dd3e6266e8f3c488f7a70e43547b289a8a838af519fc0e3718270133a7d02202b438a20931ed53c67c4122f3bc8543c5f51fbfdd918e87e2da4d0aaa9becaca", + "id": "fd445aafceecef71b3bf57c4f047fda3759aaa279f5bd7108a5ef27538b11002", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90000000000, + "fee": 0, + "recipientId": "AcAs2VGLLA3CTwB4zzHBnbzrUpFU94qpo4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c21d6ea2c66bc26ddbcb250dcfe904c10684eaf8a30aef233aec5b64d3ec9687022011396be43ba72cd7cf2f5001e619fc4856d972745d012194b3d951834562f802", + "id": "0963c73ce61593cb65e2faaba4c803783a37198d3b93ea54323d3d52615d45b7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90000000000, + "fee": 0, + "recipientId": "AHvDiW6VVXRHhGLkMpuR8ZX5auBZPHK4AB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e333d51da7708efc3ab80ad3e510482ddfd87f2193b1d61939d38d5092fc5be402207bce382e3990b99c53e0c8e5dc3af4f948a7f511e514684e0f59c37aa0ec54a2", + "id": "f2d908530b25ea4f3cbce6547e4aaf248e18fe149f190d598f775fe26458e60e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90100000000, + "fee": 0, + "recipientId": "AJ43cE4rT2RoUAv3LfkDzYYG6podChvrUQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f4b85a3222ca9638fc1b8c5a64e4e23aec2cc4ccd8d957cd1ad0747cd430fe910220716428e2f6d7289bea53eb029006d1a1472cdf4661c0e55eb9b04ebcfde5958f", + "id": "a533655b53cfeff54fa341c2d9cb376e71c70f2b7625f798a66e6e1e0ca1c505", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 90882035337, + "fee": 0, + "recipientId": "ARQiX23neMcAX7SPfyauwbixNhfWF9DF7c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022018c324014c8bdd445bd2025fe527f52cc668886705b068837b9d86cc079ff215022075646fa0b7eee5be5355c1630eacc5bf4b98b1653db006d1cbfd41e07c156218", + "id": "4111147e598f03fce985dfef561a5205500865a7adaf07ebef15a8035b070726", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 91944813749, + "fee": 0, + "recipientId": "AZRqkB1K4ybcsZXL5ZV6sYfcqYD2bRYwzW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220794d5cfc737927e6e6e2a628298466bda26bebacc349fa3b75cae81c636bdfcb0220463b4ef0055b4bad1150add80f192999486d91c4ccfdb07da77bc9100844271c", + "id": "8e3e472a029d3c34d9360a92e08f71ecadbe5c2290fcd295d691b35444510626", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 92749389717, + "fee": 0, + "recipientId": "AXiaU4ksGV7AZHLSPPMM5Ey1wbEwEXoV2m", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203436a912a9c53191957842d0d04401a5b33b3255bafecea63ffb703fe36bc4a402204bdd3dc39ea956d43a909b9e5ad8c71909bb5291238ef2b0ff7bfea8cecce5c1", + "id": "6d4a7e3b265f562969594ee5ada3abf8a1a3028a324803a7d8566b294ace73f6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 93006402914, + "fee": 0, + "recipientId": "AXZLHoMLqpEVAfeP1qcdtprba9GBoK8rU1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022074f4c6f3bc573b7e6ad33da49b2f429d8bd93d2947c8befb2eef024e24b885ff022035902bcc93a8f39dad93fa36d0c378378517d50ed2bfe1b910d8b6bf2712b3c6", + "id": "69adc07393e5f7e41408f665571ccdf56ed41e8414002df3a5ee78f83d4e51a3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 94906857132, + "fee": 0, + "recipientId": "AGwRjNYG47ycvkzKjM5f6QrzcHoU8MNRHT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f31598c11e21ad6c472aca50ae8c9e762bf7b9dfb720bf81e4edeb0c94a0863302207172d053daf3cca142027b29c62cf8ad23f2bcfe856d9ae9bee82f2398bfcd38", + "id": "c311c2d17e5882af306b820209a4cccc28f382dc7861e8e59d39b7a5e023502e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 95300000000, + "fee": 0, + "recipientId": "AY26UuQoCQTQA3Yaqf5Khwf1dWK79AkfuQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205a0874f7f7aef5aa5eb650196cf1ee1739a3c0d3be23fcefb43cb63efee96f3602203aa0715dd7b00a84926f6e7081840adc97f9dc8dff542373b614bb9a20912074", + "id": "493a1e157bbab8a11059fd93f54cb3015076628ab11b09a1f6768b233d8c1454", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 95300000000, + "fee": 0, + "recipientId": "AFwj9n5ARhWX6oWNbTEeR9bbX3o7xBikXD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220483099ced6fd44669d578eab70d4630660597f0df1a0fb734a62c72cb2581cf202206636e3f8066fc4857d558eeb5a3502b3aa2dc25b2f51f3d44aba967650479988", + "id": "a3273ca38ac6a49f276e658b11155fcd151f5745ce220bbf63373cc8971d784b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 95300000000, + "fee": 0, + "recipientId": "AU54w4okTRtFbFa19MC79VBL9B3QW45trR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022055aefacb9cb4500f4573d6ab8e50e724970b2640b8a01cf071c4f0bd7076ca6a0220474772c7c8f44a38f98a1fd4be1cdd65120bbd3c2404c91f21fe5331f2f429c1", + "id": "de221e24f05801bb0c5681342127472e05e716852392d8d60733811779a0a142", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 95300000000, + "fee": 0, + "recipientId": "Adap1889XDiKC1bMLuLrSKzsabXmTuBXjR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220083115c84effaa090c3b79b7e7036a9bf2d9dc8e5818aad77446445f17ac8e41022016a2dc3addd3d82605c5b20e27f89019dba0af6bb91cbb609f4f3be89f463b4b", + "id": "97aaec895cc684d562baab61c97def9a651cddd63b1b8fdea8ca846a86eb595e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 95559787156, + "fee": 0, + "recipientId": "AayAdq2K47PQiS7jMknrMwmQZujeEp5Fic", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200993e9038199932d4f9ea36724f89ac970875ce335ec578abd5792c24c4cce170220048ac8441077ce7e8eebb2fb0d22d4216f0ada9a0f8a3bff22e713f2d20b2875", + "id": "28103dfb6b5efe7dd7f5f880df2b3b910a96c4ba733c7bec80040aaf73b9a754", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 95612193593, + "fee": 0, + "recipientId": "AWgoLa9CgxHtUPAHqFvMpxvvFFK2iyBxUK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b71039017534404cadacb04535b1da38415411d5a2f6f5b511131c6a7d48843e02203504cabf89ca95390944620ed9398261da2a0a825e38d26ed5ce0c94bf58469c", + "id": "2a4c8eb73ba5fda2120f2ab91c7806ac9b603ca1a2b2d8301d676fda113ac29c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AbD1qLeYWasvZH4PX4j2h9bXFXJxAdhAuy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203d888caf166966ca8a3559a40b6e8a7ac840048ef9469dc143bb1f9be26acbf302201d256b315dcbca537eaddc86c56850354726e67148088e38ea6763c8f774e2eb", + "id": "0725b2165c79199d1cb3087ce9f6989d0781c38bb4fd3bb054e9f42cf577532d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AWffb5DY3ceB9DcHknBsLSmZ7bQCX9BgZa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200a3142e959218f0b3b7daf341222c3d2ceb5be21817e5c97bac96f964823faa70220086698050a3d8030bb6a1f01d4000297561b0a6204d58f8e4795b58a6b1448bc", + "id": "50f6375cdb90d9bf764f1544c5c85daaac335148e7c5b9706fad6564711eb832", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AJyxXb67KmdzRibbRSp55tC1NHcSXzNQmn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201bc4e3f5e8bae4e7bd81da4e8d4d3e8a1f9c607c36e39e442235b133e3c350dc02200cdab848881bcfd15adf3f38afba7582753e9824cded8a96c35de855ed394c6d", + "id": "41cff0cb50f6902d42cc455467415829d66bc857002b904b63b959fdcf028215", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AYo5WNLUCNSRKkvxxMs5hHCNxme7xQ12ZK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205958c081b5336400e4a7a834a33cb8b5b05b81cfd7fd435b6f2c2109850db75d02205ac3c51a116db91e925e26da94efb2143e3e457669ebed1bd7464c9b1306099b", + "id": "ec651028a9b2b6924dd11472761295fad83f3738d2b1ca5867b32a867e17d946", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AZFKF8mKcoFVz6nG2zvbFxQXfumsXK5a2J", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ffabd89a120c2ed7c03c125c1da112043f5dd98cc99f205ae87488f422278b87022054bc18f5d6c732e8a2132086bb9961d9b3313bbfde4f428cca2f61db934275cf", + "id": "6dc4ec6abe9f5b39e3624841432e9c9af338edc2d4214468029d204d15bd50af", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "Acye9MMv7bbhsBoN1T7TuiCKwPFprnoJgk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f2dcee2429c1d6ed797c1d8825890474a4770f5f18774575308946bafb3b4c2502201ad6bd91169df28bb2fa6c827e8cecf0696d87136de193bcacae2068d0af83e4", + "id": "ddd07d35e2e0d2b1eb9d1fe7297051c22da9762283b767b6f8377d12959328fa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "ARWYVBZxnuxnp8JQp96fSZHZEw3Ea2Zd3X", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022007e6b2cbc4a6bf18e2b310325e1d7a56e0d455cd9afc6b7bffe419a9e70b710b02205faf2e91e740f331c04ca133da2e6408fa7f3ae4e5328b8ed6befabc8f7adef5", + "id": "52d56ac56c6e2363f94d1a953a908ba3d841bd193c4af65799f652044c66bde2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AdPV4RprkYHnm1YajzZYcQ7QjVLwyP9HrS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203827a7141256877ba780e3b36875bb9af64f7f4fb3d9bbd4e9705d25dada25a7022027a1c9324c162cdc510aa1130099435d7d5569737249c3458f496577482a297b", + "id": "7cd4765b692240de26ec4341109869564754a541571ac191a1ecc960220cdc6f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97000000000, + "fee": 0, + "recipientId": "AGNE5beBRiKtzAGxCGHPUEzSTbYNmB4x65", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220639fd4f4f25e365b6cdb6ba4cd631a93cd826376a07ad941aff9c83eacb438b3022060a52e0cfa6f7b4b0048d30deff592b185a7132439031c9f84aa5eab945413e6", + "id": "aff7235b112fbaeb9bd31ed8ddd827a95d735d74cfdbec373727031a6e752d07", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97297237832, + "fee": 0, + "recipientId": "AWxt9gGVbxQ7cX7o9DBniXLA3FNnAzo5Yc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dda67cc7adf9bf59523c49e3862a569502c02b69d4183681ba73fca12720e14f02202744c8bed417d5bb28499eb7bd3d353041c32d86247d9152090925395642f2eb", + "id": "08977af886121fdadc51a53156c4d920998d236560d5ba5518eae27b0cb75460", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97764047435, + "fee": 0, + "recipientId": "AXduXps2TeHn7AvTiJW6m9qqNCtrPAXD3H", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022033d10a0876ec4a4dc66e809ab4a852ad48cc688c68891ca5934b78550420e7ae02201798c11a5dcd1c21f873f0bb299a86d363895ea0e6d4e7a1137327498fa69256", + "id": "9d74cfa57b58e0470fdb88a9eb944096e1c749a4196820575b99e948c125caca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 98045028617, + "fee": 0, + "recipientId": "AWYjUa8Y5UdaZKtit3Va899xqZDdXxsRaS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c60266fade798d0a234c8e8d8fbdfe5f73fb015a29f2497acf95e78f1fb5e192022034c1a2debfaaf6068d46e70e42eb606082438aa53eeb75ae1d84f84b6cde0353", + "id": "828319edbb770b68aa5aa91afd76c02f76308f17f4a1c36523fb2817d1bc1e28", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 98100000000, + "fee": 0, + "recipientId": "AUzivjaUXHG8ivXWuj6evpdL2TjszQmVWs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022008f1b85ead0a3a3328f43200c0c532a2a539715fbf4856e084dea1e7498a096402201e0daee01c06927dde071f5f5cc7e6fe1eeab7291cf4f7a0f330833c535d2deb", + "id": "fa1085f0ac80d410ef9530aba0af26ae0d652677f5e90195bf2184be93dfdf71", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 99262036541, + "fee": 0, + "recipientId": "ANGen8SGYqiBR8TFgtawL57qxbNZRN1hrt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207557798214dc6bd35d8dc197714dc93287dbd45edd7ed832991635e5f20cf9a202207ca660d57db42578e31b0622648439ddf0219a862579b1c933d5987b76ce53c5", + "id": "f46f63e2877adbb32ffac6af90c5cbc79dc19ca5592a0579d395739d249a22a1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "APSNQNbPQabUiMqxLxjhEyTkeh27nWQDu8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009b295629afb30454c67ebce29203ec8b3f684f194fddb167bc4ef9d3739a300902202da7e99a48430663805db9cef9cc32915ad694ee75b5573a5be7d7d7d688afa4", + "id": "423abfb5a05784af1225894bc22e224dac42d421e90bb4227c945c35ccb9aa54", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "Acj8SdBVCKnaCUFAydJ2Ggk6E7cfiRP1kJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009d471ec487447ee43e43ecca799d7d1eab2528d75a1f98a35d9f1c777ebbfcf5022020d40ea9d1d3903bf5ee4e2f67f97b7d62fdfeba622ca8e72e67622c9dbdf8ee", + "id": "0977be517acbf17a25ebbe3c2568a91667b5bbc05c6deea85d61b2f1154440b9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "ATz8duPjUnEL5oXZeD59rLCmtvHEcbui6i", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200de765be6c4d07836d73dc78eafed1a80fd2e91e3217e7bd104b6b084b09d78802207b244d85ea74fb2248bbd4c265a9e88a5b6a55ac6126f1545f839d2c41204f95", + "id": "106903e0f03337c08dc2d9af7c17263b53376474f56134b76357113a4e92b3e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AUxTza9YeaMXKqgU2FXp6mr6kGyRNFACRF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220697444d31def970f5a73bafad9d3c0865cfa82a8f200d95913a6953314b7b49102207f70b0f71f2177add874eafa7ddbac1c3f5162f1f410320b06cd9f312fa5cddb", + "id": "e2b78d5f015620390fc6dc0bb39dfe1473461281c1f2de2dc47668959cb705fe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AdbbGesHxgSbb5S9BFVvvmgrLbz6vMwtti", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ff6e1f7efbd6cf34bd7e27dde486d29e6429d28f0407a2cb86b9d3b2430cf22802200f64f32c92f87a8591e674426c34e43534c3afae30c62f08b6492f53554e99f3", + "id": "900cc87a9f10d54378456a777b72168fa9a8e44d256d8c46c362a1e068b8a3ec", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "ARRdKZqLoEz3BqkspqPFnjDMXUWHiK7rUf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008ac2f93dca4a6e81886d0cd48cd8d05fe612364e9382f45eadce42e3051cb0da0220349b5ef16d0dd502d887fbd2caa4d666cf0080f55a6c8bb92b99819e15cb148b", + "id": "48bdcc189c2fe7e1796fe78cb49f18b99187feae93e80d3e014d30101c067f84", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AM14wbgpGVFcR6EeyFrZ98iBSGQdKGwoNR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f56b28811f377035b6f5ef6597f622a8f45d0ab7ba46d9cf8b73367dc2ba72c4022021bc53a68673a43fbc49c98b19708527f61269f16bfa15571a74b468dde373e2", + "id": "1c0411c1f4e0cccf85034f50469939ef994740903f65ec5c51d3ea04a0d776d8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "APwycJtjaT8LaXEwN6DmyanzBWMGLCDffz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022030e5363191d9681297a0b85fda62a95ed499f3b6ec18f7dfe115371456367bb5022018807046a5e802ed222f7b2f87e583942782a7a3f3241592f9d7bed5d5d88784", + "id": "f0825d84e64b56bf8f388530cd18cdd4909be54d364ff9703db24cdacece080b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AFwTR2Q1TyWgpYvnUD5AKZ3KtEhTZnjSen", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220767d49d3c2497cac443b78c806287b68deffea0b7bbbeb171335997b40a10d7c02201a7e30b1d7ac9115d98589b3b54c16e19e7466b8ccc4394b488cb2637315af0e", + "id": "0dd5220da9ca8ff958bbc8a4040a0502d8ee757ddde806625c00f4a7cdcdddda", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AWWbW4iYXRvXzXAG4DVNw13kxGyZXkXJZe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e00353541efedb21d3bd42e33963056ec65f0a79880f8a3a5f991103d8f64b3d02202f324a1de8c96fa2fbc41d9c3cd544e3e25bd84de68013c38696115a497a12cb", + "id": "3fe581b3aa87b6cffddcb98d27f62ef83138e2e78fcf3feea8a44db4575f89d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AZcPTCo5A8M36wCFvDvPz3H2cBBJo9p8iq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cc27cbdedfcb771e9d222bb6cb13a71d7aec8e36fd433ea04942633c5d5e84a20220564dacdf36c5d0abaacfb62c445c8fd1d046a794339f38a05b8f40ab1d338d2c", + "id": "7682c2fa3c74fc40d917290a665ca4412fe74b737cd464c908850ee631af679e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "ARoVFy8w9CsSBazrXnigzRZVrrsVhUzfLs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022072790cfc15458125cf1fe54e85d58955797d3f4409f99fe74d3405742fa72bda022013075f4dd0ec2054b42eabc9d41d4cbec07d885bf1650f75ef3936826dc2d1b4", + "id": "f20634bad0858d0e9b1fae15b9608e8b6c0eb8715adba0977e0f17cc20dbee99", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AaerL99dZfwZL7yghV9k8Ri5Hhpi9YPsYp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e67003e1c964db17bf2bbb00e6a4c60ca12e46753d36785b13041a136bef4ebf02207846bf567a2bd72bee3289a321e58da645d3a64dd9030d0f52028de50efac7b8", + "id": "8aae4664984ac30d28b171541c9a41dccabe558089b073d8236171c40456c4a0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100000000000, + "fee": 0, + "recipientId": "AYA1abqExAqcWxP4qHaMNN6MdwSVAzz23e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205e46b3935995f1edd2797f8a4ecd5181cf16893d4f955747824e0cbd7effae40022012f11701804587a5c2d9bdd8295fbf67524bce5a26ac6758bb7d43ba6975ecd0", + "id": "fb4d5ab6087839d6bea3c4b93d91a5654cb556023c0c2648e3ddc47268b29447", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100023851656, + "fee": 0, + "recipientId": "AG825v123YvZQuL4vbe7wNxvdyTewt9gGm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009d37379a461696e7b42faf48419ac770ead1768095ff38997eee98fc01bccc3802203272ec96815df48dc692ff686952e04053d60f14c0cddd821f0bfca775034b27", + "id": "aca358d024faa5f4fa68b7943ccace6bbc5a9ba2bfcc614445b7a1b4e96be417", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100749519793, + "fee": 0, + "recipientId": "AStWDeuQ3NTP619nCiyxbYbnyHTnR6cxb3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022046f3e87a0851d6feaae6224127051c783d08efc892aa52118f89dab6f11f02310220030a37ce70dcc5b4554397934d67ea888f49c7ad825a337a5540f8fae99cca44", + "id": "b92dcea5e20cb9348374898f686f4d2e2b6ecfaceefd6a0ca637bc61c6a2bf46", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100933159396, + "fee": 0, + "recipientId": "ANV8jYBCQdPscN31ES2NDFQ5Xe87zUrFzX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022000ac1e8b3e471505f8476f98ea31920128876a804d22ee446af4ddf210442cfd02203032930c4d6d6646d3d4140ff89c48b4e58374770f6be9169d04c1099a39d216", + "id": "c57cb34e723d065e12f74b33c2ba5bcde69763ef191505ba9cb38af0a769360c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 101680182605, + "fee": 0, + "recipientId": "AUQroQ4if3dKyFwgv6ESDM8MHyJDe8MVLP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022025f1efed2ab491fe87764de2cca46aeebe3e31e558210001798d86c831dd69fc022059dd3257cbb318ab88d7f21baa2cf56a975582d578a3c67cc188ce46793a2d2b", + "id": "f1d975e6feb911d060c7ee86cebf9f56ea0d3001d8d1e9813d24c0fd1791aeb4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 101830636768, + "fee": 0, + "recipientId": "AR58rt33ihFKG56DXMVdKoidzNm4C8kCnF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a8e1e2102f75026fdf1cdf3bd8a737a673315d48ccf8b28730235ac9f78dd2ed02205d5a50aefab9c7817d6769a64c83b19b96df69769a735a3051c9888eb4844ce7", + "id": "84ada517a25f66b23c3127830b7d77948fab1c1fd2e643f4166384c1a6af9bfe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 102744627447, + "fee": 0, + "recipientId": "AY7JH5b3GdHv1ZcbFXW3c19ovA5k3jYtDc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dc91d5af8df4ce928414162c63d64452bc1a05ecf20fca159cc374deab7401ea0220749733c55f0532b454f3d401d41bc3bb79fec7fef8b439f54c34c17fd5865a1d", + "id": "edbb728bb5f8ccf9384ccd41be5689507179d05d60f72188c419d5163d76b6cd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103054877463, + "fee": 0, + "recipientId": "AHRjCLd18MVx4MevXGsygteD41pjjEmae5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220223d32f9daa36a1d0c96c35d6825922ac18fd23cf9e69f640f707c27a49f9a4802206e4132f40a65ceabff49666671a7f8d1d22848e06ff91c31a42cb6cc38f0d9b9", + "id": "63a23da52548b2b0f932340589409d453f3d46656f78dea4d8688d926013f84d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103054877463, + "fee": 0, + "recipientId": "AYwziM1FFZdiYJFpLjSDBQUnJD5gQG3T6U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ef3d526e72b7021dfdaf9b7706c88ad8bd985f453754a116254d13b888e127140220259eebf0c8aa21adee9b573ceb4bc1b4597c72cf6a18cf1617cf645eb169dd4d", + "id": "915baba7089f762e6ff2398ad391de54f7285f0b155a277d88e5a9bd049139b8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103054877463, + "fee": 0, + "recipientId": "AFygDabDnSBm2M1iDWofGNHPa6ZeCEeqj4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009fb6a8eeca7df3b18fa9d1db1a1a4c1c63fb36dc5f71396c9f6d21070e7fe8d90220426ff00438c59643cf1ccd01575832f0a375d1217c43cd816538f3968c643fb1", + "id": "886d32c7624d952ff3db788984073b74e5dc6bc027ef926d1139b0a512dc3f2b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103054877463, + "fee": 0, + "recipientId": "AMimZDyN8vdDp96CHVLtWkhtvmERvTmnnT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd942aca86527ff6011d230a0ad9626468d361b91f44ad22e32c0774627b0cf90220410fe8a28dec6354408b5da8bb3b77f5f5a4e2b491dabf5177876a8b29928c97", + "id": "b7f7c997bc93d99f92b458affba8081b15e2349f35176e2100ad38bd859bcdf0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103054877463, + "fee": 0, + "recipientId": "APdPdgkVCNJqQz1xP4qqzRY6K2tjybdH6V", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bdb4e44659fffbe283616db6d45315d3abd1e4e559bbf323629b9efc4ed81b080220069b6e53fd81cea49c8136ba9ae23e00258ab6a2d30130d1b31784a21b669a96", + "id": "ffaa043601b679834ce66deff3b7ff00046acd8bb3f573ac6610165ef087879a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103900000000, + "fee": 0, + "recipientId": "AZiQBaR5TwSH34NXrdrvErMX3aiYZWFxo8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c87c4c23d0a4432c99df14afd1c88bdda73f3a156ad89a247ea662080b7214dd022055506cba670851b036bb39e638986879199b28312fdf356a685bdd1b17003708", + "id": "146b10f76eab1b6783af1c108c35df9bafa3c6ba25b72ac6597d9fc57c7cb215", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103900000000, + "fee": 0, + "recipientId": "AZo1U1VRipZ4aWx8LYfEXmyEBcFTs2St36", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022040317a8ac4fe1308c10fdb2ccf695db0d4374cc57ce5b012e7d37766a7619ec70220014d508f6dda12b5b9ff095e6d53fa77bdaeeb04713de273725d09b55ca22251", + "id": "549b4107541aaa18058574d381323196285df6645476b9dd4e4f791ea4c49106", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103900000000, + "fee": 0, + "recipientId": "Af4CtTPXJ4Wkgrsw1zsvmCoEUS1AksAyEk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203e6c4eb1fa7b5311f04bc7cdc38445b9b737592b9eaf0df143998cf5ce66eeec02204ee48dfa39a93b9afb978afdb4b2de537dbbeaf1c82e31059d344be867378cbd", + "id": "3fe8bb50ef8e7f6346f7447e934e9cf8e51d981ab151698ac258cdf03c72b8ac", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103900000000, + "fee": 0, + "recipientId": "AV1MipTPz9KkjJKqSfagNiQkpCDACG6A45", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009c3986801d041cc233bbd394cac166ddcb2c55482aca48a85a59c130c55cd8d50220164eb74f9ef23f5a6307756fe57ff385258a9fbdeecb5525802f79cf5dee8956", + "id": "54fc495250ae8a95f231553ba952f8ff502aba015ad586f03f68bd6fceaa7606", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 103900000000, + "fee": 0, + "recipientId": "AdFF3NRiwLpVuypouVth7HKk5Ca4xejtmq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100efce8d12b523214f79402697597729647211ba94ef27ae2743af9d6ff0985da602202b75b8e0a0a655a8863b39940726b51b31566f98ddb51b2b17735a7970596edf", + "id": "436256751f2e97b12d0a99f725a71aaa4b4ace4644b4087298592e0a5cafda7d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 104570390367, + "fee": 0, + "recipientId": "AXowRmmMSAB7GFCy8XPpNLu7E23B7DmkPa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e0b952d2bfc38186a4ca9ca51607f44ef64a3a0e58836171f1668f0f47fc10b5022071d9e89774682c5cb84897d5c406aab779527e759ddfac473c900add865e420c", + "id": "ef7a402d8c58133af24b24b5ec33821e0b443e680144a20b9ae83ac85ea2d16b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 104811009865, + "fee": 0, + "recipientId": "AJNitjA6dbbiz5vDaQkaKSXaNiktVFmopp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100aef19847d686fa0e802b7eccdf3e302fd9bc3aacc87f6ed76ee2ef0d5abd0c570220248a00f81756031ad2b87a6bb367e7e6cedc11b4412ee3998db66972edd35166", + "id": "1d956208a3979f3224a4dd347109102afae6463b060bd94957440442904988fd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 105000000000, + "fee": 0, + "recipientId": "ALrkZgaD6BBZCLx1FaUvcFRL9BkTsjXa6P", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eb10d7c5fd4b9c27aae44cccefec7315600dbabf3cbb180fc66e996ea2856783022037c60b02b33ac06604cc39077d11372ade6e1a66aa89f99c43b6fd0f0fcd8ee3", + "id": "1d10aadc7d67af4ea389a2f0163fc174f261d71ca6c80c2b326b674858948ed2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 105000000000, + "fee": 0, + "recipientId": "AZnonaWAZjaGFbku3DLoeGwgg8PCy41sMj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210091235be3645f78fa9d77d45882767b7280e779f3532defcdf0b71eeebc73bf63022050c9631aedf3c42231ed1fb9f385763c370ff1c9faa6c14ce07d14500fab3d0f", + "id": "a65fe81748277af637a844a6a21b8cd57f4d1dcbe6aa58a553a94cfcf0ee2650", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 105000000000, + "fee": 0, + "recipientId": "AbCoHPSKn16NSKL58h81seENSohU6xAT9w", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204ee2fa79681c3c86b4107e239596087b33a0fb6108e2bfe286598618945c859902203a32f45dd2538f1e49eea075fbda05db26f5c9cfb2b3d1f4aa85117bf4a92149", + "id": "306de8162df8007e47f95095c1386c4f838d32bd6072ea1e740e27720649058d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 105000000000, + "fee": 0, + "recipientId": "AMng2ZJC3Gh2ykVUAGUeKB2q16WnaqK9hF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204d0818688736ad3bb25e21e4af2716b2038a3691e2cc7f27bda2df761df4c1c802201c2fd04143b52f2b5e5bc1d3f8c17cb038b90eebd5282ac3877012a874f8fa95", + "id": "1a64cc2367fc2d05a9fc862cbb99be77166117d5ba2ec25907126561b2d9e1ed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 105381567713, + "fee": 0, + "recipientId": "ANzYwcwGfMg15SgWMkGuYPxjePuXLtvuEG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220750181471cd14e07434ff8f59022127b7ebb36b643aaa1851142144fd6d0bfaf0220268280752dd52b66eda35bb6384e6148135461ae865154b175b36162860d71d6", + "id": "e4f2db2efffd43d8a99a2df742cc93ba40d3e97cc4e9cb421f9ad3c85a1d5e19", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 105714987355, + "fee": 0, + "recipientId": "AcJFXLL7uS4i8x6Jg2hZQftgirzmFt2X1t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200c0d4ba6f603d8dc3b535fb8b442fd1c92c2d7deca82c1aff622981874563b0b022043f3c8a59e042b847b1359f1a28a34b44fccaa042c41947a1565ec2d7e719167", + "id": "a85f33a959089b825b8453138de87a63137c1fc61ab1038f3e8202d761133c31", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 106221504166, + "fee": 0, + "recipientId": "APk1d1oci5TiANq2XcdQjy8C8iLwmtTf1G", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206afbd9bc04c90409a31228c58e89f493ce384d7dd97a0b97412a4725d7358ccc022067b1f28540632ad989bd7458fdcd23e5897615e9d9017289850277034114888a", + "id": "b9cf7363f5730e0b1ac4ce9114ef5eee22797016bb718354940fcb53cab874b2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 107239514002, + "fee": 0, + "recipientId": "AGLvTbXCtBAdCD7bvAi8PBSSvRexEv1upC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ddf53eb746d313d24df80c2edb7a3d01b533a9691d05d4c863afe390ee5a82402204de5576afd6f9a54ad7f106cdc71691acbe16e206914e9da123709f9ab78ef85", + "id": "84b3f5981b62693819aaed660e3eed3e5f13042449234ab73a5870e760a45c7d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 107692346948, + "fee": 0, + "recipientId": "AR8jXLCoRWnnkaD78YxajVcFsTv7eVutRb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022077094180476f993bcc0da365d9f9667c8724c5d07402689ece5f870af7e63b2f02200faa17f80991d65bc3e8a2593eb2ccbd7b12fda29dc3fb856ae0d222bd56113d", + "id": "dd832724781a028d0427b13d14813c82d8e949fa488e9885c872f1d7e6176642", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 109100000000, + "fee": 0, + "recipientId": "Ae8ZPCEgJEUMkR8oD1Pp9eDocsGE2MRtiZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022040ea35b1906200719f7b79139f0bc3dcfd2d1f34b19c3e75a4fdd1e3ca84b60b022008522e9eb7dde5ec65fecddb318added37afed341c66d4c3f938cdb539bab0c3", + "id": "ae1127f0c65bc53762bb7b5e79c3dd2f0da85fb201afc5bb1577b1fcf371c31c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 109100000000, + "fee": 0, + "recipientId": "ASoz5DXAEBaQy8H5KrYWH6bzYkJ3zHVSsx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b4b63ea88288f994776b80c78ecb41793cf70fbf72e3038acf73099c5677be9d02200358cad66ae2d98a08c43be0c73fe88a6ee869be49d0012e394db3d14659c43c", + "id": "f2d28d17f885481487547831f316bcf701a1be0d2263403740dc0516293384c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 109100000000, + "fee": 0, + "recipientId": "AaPUG2PrNDy6UVUBkSwPuKvKh2DSZdnDqe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d7f47c01e28bec5afbf49a5cbfd68f4a911a4e840b624e13abfa4209c2e67c102201fcb385bc4fd9b55c687e5a253b2f181a658b7fc462d8830e63deee0c82967d7", + "id": "0974483b28113dc2ba87c4e8caa564f71b5a4dd10167252e66124b02fbddd7d9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 109100000000, + "fee": 0, + "recipientId": "Aek4aTvvdA3hUVUjhgkDtPQF5E2iBuAVP5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a05c9fc68f17c57f5adfc88c3bef13e616149a53b65a6a97c95d8f750a3251ad02203cd2aeed88591b244df1c05342d79fc7a5c0da989d5b024bb4c72582b28333ea", + "id": "cb54ba7a6a747c5d20e66648e3a7d12c6e5193d0ec1daba2dbae6b8bda726165", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 110800000000, + "fee": 0, + "recipientId": "AL32Ure9XiVh6emxgHdLQPRbLbr5otNqDc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b8d1cd4c228ea834d371edd3ee9c59053d831c124edc7588cb358aaf0639f93102200cbfb5d6f0cb5498709897a24af743cc1a80a66a948d411c9d71ef18394872ee", + "id": "4683c51754c198ccaefd949f1becfa8dc36383bdf4f43b9a5ac3df0e848cbb01", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 111541749725, + "fee": 0, + "recipientId": "Af3iDQ3qYG53LHVXgGbQFXkffa2pyjdEFj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c57f7ddf68a0d08feb91bc9d67e9b246bdff6d8fc3dc07e3524cde453ad60bb9022007a08e49417b03c201aae30e9c6b12360c1e5e92b7ea5e72cc3b257d48578f54", + "id": "df3260c10be2b53c385cbe025e84be776d6f9cc5bbced87a110ba3fee4ada362", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 112600000000, + "fee": 0, + "recipientId": "AU1k68nmfcpAkTkday2TZFoKuttD6eh7JL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3043022004abba6942eb051d80c8faea823133159c8207251f82bae328b0227b77f92097021f1580e0ff6214cbec6d97059469e5f498b39c25873c889870d0450b1b892be5", + "id": "c4b480f9254eb4290b7acc61e2e85bd2131785e482f6e617b8c3a6084773760b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 112600000000, + "fee": 0, + "recipientId": "AS3SgCu4BQ4VxfdJyu4HKNVHZGTQBP6bTB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bd132234f55b6c486d34b3fb5b1b52558d45938ffbe0a02e2ee06ba9fe7673e802200f61ad1ae7b20468c496c0526bf726546eae7907387f72f85245e0c7fd9888a5", + "id": "66b7e7ae47d5663ceef394c8cec8ddd1851bb4034c5ad133c1b4f6d2b18b43a1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 112600000000, + "fee": 0, + "recipientId": "AWepJMLX9wD9nm8RqagQasNfypVX8n2wLF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201dbf3a7b09b8a4e71db49504e3cd5bf8864a877218bffab4dbce5a50b199cd1e0220452ebc9b95206fea3629f11abc6a40a6be3c0738bf496ab2215c7033bba185a3", + "id": "a94b2af091d991840007d2644ece123f0f42d18276237f163935d2df2e075685", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 112600000000, + "fee": 0, + "recipientId": "AXBWn3Kf2yEy16mYKrwnTQsRFL3geunAxh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220663a6cd1bef053f3376b19778d7256d08a34a8d8177e9fc36b3460cacea75c4602205b50dbdf88972c8868d15934c2a466f7cc6b82234002cd08391ffd13d173107b", + "id": "068b774d4990385eca9fb6e1ac6726cf6eaf99fd3687b35271fe407b33e9a784", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 112600000000, + "fee": 0, + "recipientId": "AYtpgFgBb7FJL88VBShq1GtBLoGnYBzLe2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201d96f2acded9f3d609618ce34f387c2bc46402e7e14c7bca2592aae1e00562800220593d7ee4b3f97caff848c38b497140312855d9df2f007b8432c69b43182462e8", + "id": "df619578c2c85a4e2a3f9f41c7afd5992439c5a60f8d17eb3ec7a9ce8b23c96a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 114624967068, + "fee": 0, + "recipientId": "APR5muHZocUvsHqfXt3Y3QDuYLHHgiRnov", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa7873fc955e0c2de2a6591ecf38fd0252b4b25f83c92b6eca220dca2c593e7b022007b86133cb60ece69909502ae21d7a5a0fffba04ae88522ffe15bebeba2882b0", + "id": "877ce4b295db21844c7dfdbaaa08571c2a376400d60cdabc9af1eac7fa2b591e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 115000000000, + "fee": 0, + "recipientId": "AQVCqqgTRUDR5NMaBBDNxgdpwxkZbtqGxs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202927f829fea3a19b90b2144407b9a268daa489ab146c08106be90c4273220b960220220f2de203a334b320f9852c527e55b8f595d3175e6b78c2650c27487bcde63d", + "id": "0674ef83667bbf5c4dddc21c122777e76af7abb6ff73337b7c1f87231573e3ea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 116000000000, + "fee": 0, + "recipientId": "AWEHZFuvK2sucehMNho7bFgs5Kct9MX9cM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022023e9e21d3126edf7419081dc3d8ca781cf08b4f7a6250536859c4161d848799802207e4a553cc1b629c4727b4c3fef627e8fb592e839c80997dbfe631a08313f84b9", + "id": "aaacbb16a6a80d35c49796dd24c5ec944fa8e78323f7df908fee895a651b59a1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 116329005231, + "fee": 0, + "recipientId": "AMuZTwvJTd24XdckcZAi7Qiy3pMoitZLHV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220523323f28caa94c93362d327129e8d3c4088f9c577a77e9c905464c5ebd04b1b0220705fe7ff0d17f52e3370d10be916af09ef1883614c76eec9899adca018888445", + "id": "7301a48380a2745cfefa1cdf1a2ecc9edbf70e619ef97440092bf80e8c392440", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 116897017953, + "fee": 0, + "recipientId": "AdFmnvX2noJxbAqXZvKKCRV8xewsq2L8mU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a61b9e09531ab70652d5a188994c291247b3be964960448ba1fd0d21668d7f2302204d856caa8fa3ff06e8b91c69f5dcabc7c60e57d8b342205ce03233682b109c06", + "id": "55242c764f2a4cd791545e8b71cf09b205b99663760874500a814a942070939e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 118387830724, + "fee": 0, + "recipientId": "AGsD1iKAnYqa7q41f987DzMS9kVmuvMB4p", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b982e36000e67d68c16636c2f869d138f69612e2a2452584831d807bfd7e2b69022061eebe290b68359322fda287642ca41864a581bf666b74f998796b7abf418c54", + "id": "9c19c864bedeae508eadfb014a1716c8f6af01bffa05bde44a35e9400fb441ac", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 120000000000, + "fee": 0, + "recipientId": "AbToFt8MRXR3BmKcC6EpnEPWaNUMQzr1hz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d94cf00a4b6aef15f3243dcb3d68afbad70558b3928f096622eba887da94af4e02202d306ded93a5b9e21cfa7ed9db16dae8b78ce51586f4c6be5dd0ff69c379fdcc", + "id": "fa35c7b607c02653d4c3856476100bdb65b0af57df0864b0173da3fd370d1bf7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 120000000000, + "fee": 0, + "recipientId": "AHWWoXDCa9RSeB5d9BCS2krPh6TzxUa9b5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100af2fbcda194d173772c91a90ecea2ba0ae32aa24dd7a119a5cec4b92dd348a93022073dace8e3c3fb62051bccda308c1e80650e39ddf6db74cfbb1ff554fe5dfe773", + "id": "d96cef527759049343a6ceb34e973a6e873cd89ffdc0750dc94a9b8b11602c38", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 120000000000, + "fee": 0, + "recipientId": "ATqYMZ7zmQy5f3DFrPKaMZjiPVpGAjCWae", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220176a3dafb595afac283608fecb966d35be5f4ea746ee54a61cc837732b368f3d02201df396e59e4947dac9c31c0f6e3596fa366b3d6a1b537a04a3d39bd5b2705911", + "id": "942882235103abaf544d21ea54b7cd466fb9f0eccf13db858af4cf254444cc0f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 120100000000, + "fee": 0, + "recipientId": "AeEasb3UFnht7FJiYqFi7csnAavnTwxUHG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30430220608fd2db9a7ab9160e8071694506c793a63ef0b3a70970488358a661e80e11eb021f6ac85f5fb747484f5cfc189b8cb75e2ae8a013b9f9f2f37b96ccd1a5e21f01", + "id": "5b51d62552fa2d44563a09c0e6b1e30610ace3fb9721fa24499cc19d6252f8a3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AR979mGe91DMX9dUPsKvEHhru5hc2aMqn2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d97b547f60feb499b52f9b613366eeaba66f8f2675d7e1faba52dee39e3142810220617a00281f0c066eba13a80da45ace50b6d94766a50d0b975563c2e1617cb1cf", + "id": "03676f15ae81acf829753f2b3ac30cee6d4749a508ae32cf104124b9a4f612c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "Ad6npQratXMq87v27PgmpKfK9aMvmWTjSs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022065eeb3aeec85e090ec09e772ed5d036d0e7521f09db0310e75622c0bb8e96c87022061e5b3ed0e12e3077522abcc5d15cb07a2d261d6fec0e59622700ec4466c89ec", + "id": "1e2ea66e2f12b91bb85d384b483f8a81c349adab4dfd2d20fb1d26346e4ceed3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AMeyqtTonCYWt1mPJwd1K2FpnHcPjCVznH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b0d13c6cfd44a8e186e659c23a2af522cec69a41b8e28ea6a6131bd12795216402200a0a212cda71ddcc26629b53d7f1d6e455b99e8fe4bc8f4e3ddc29839316a82d", + "id": "2f27ad44ec7b4c1b612fb27a23a4d77e9309461f72c5613c1ccf60db7601e833", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "ALvhge1cucXrjdpnMozCDaWM3C6gm2Xm33", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022059d6b72bf48d119e8e18924861ccd754aee7e71d7d3010daeba835d2b33daf8e02203f0dfc75e84aba60c389eeaa7b8cb704c5768e58d0c3e066004ca01b2015120e", + "id": "b46a24fae79c4b7b18b368b357785eed4efa908b0170fc0c955365d9a8764b31", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "Ac427bp1DynWsNGXCCYwWXseyUCYMWr2CA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100df366d759e02b5345ca59b52de2bcab1906b78729c65ded10fe89c6e0daed4c602205821d77a90716e943ddf58c31392f902c7777e3dbe69bdec5198bb2796c17a4b", + "id": "f5c95f62953e9c219850a8ae44c468d3a61fabcaac19446d99aefdf72714f32e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "ALHBQuNjrFx3i3PgCz1QdkWgAEwX8zQ7zy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200c9fa69d5adfc7f6c1a6dda8dbcaed58135738eb7e20975bf457cf288a4412fc02207be43c5bbf745b851bec3b076291f3800c54388da527b24853363b0c8144f0da", + "id": "190f5a757ecd8332701afd35805f0ab1ade99cd1aa3a9169b0fb9f48148e6cae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "ASn5uzMgJxu2bu54HGiKE8D64ZpkhXrPcX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bfc41e8af18b4505ef221990a17ae16c2be5e9ad554785523fbcbfd5393f006f022017978893e82ae7232da47b6ef37334bfa255c94cc19e9dcc8de51659b4bc47b9", + "id": "2e80037c72f52d9caa96bc27fa934e662be2497852c6162a50ea01b2487c5b4f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AYmpwBT59ZpMKJCUcsKxjCUBYfGb3i4r7P", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200b5a383750451bbfd892a9c444c41a029efe9fdddb40acf604851b6cd623ba320220129abddee1f38206fe9b688715471242f10ddfe99fff3717bbb0e17b5a43c071", + "id": "388d78941ad3978004c95a6507737c08e6b2840f1de9e054c215a1c800827c16", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "APaWSMFzp9gNr2RRqwdGzMdKrf5rwa3dNV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c69b404bda50b119d33763cdb23e3b69bdcc387668458dd33253955247763c5802204fc1cd9a805cca13f1c62bc706393a2f2e839a696afadcf528ca93177aaba237", + "id": "1b24b82d490655834aeacee9d6af2ec815bead657ff6405bd6cb29ab19faf737", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "ANo6dapr6rCMDTKjwRzZgWUtQgbbQ1Nnz4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fd106e25de0bd3b3c1e6cd3dd1f6b49968e482fb015f00556ed60a3853c2d576022050e02fdee6b72fa4c6027c17f8485e395391b9a342a6e212b064c0cc27a47aec", + "id": "96834d445ffb6224915b81236804f1a6f4e5231553b4019d5b9a9806afa632ed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AWA6TybTgFiesGDtCasa68Jt15QP2Auvju", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203bc67263cf58ec7b90c524dd694b06184f705dc7a206b1e1a1a8fa1dd29cbaf1022049410d87332f9108ec487dcc2d4fcaf52ed36867b05e4b4f14deb94006e94802", + "id": "176a70f81ae2c02db9e422651bfb9fe7430639d86144b94a4f28585533403cca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "Ac97FP6Zt5mUcz8hRWRy8QgNRsPCxs7BaL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202d1d32121b5e7ec0fcabcf04ddf8da303b4e3b5ffb55ab9ec037509f6d4df29902201526e223d63a19013b7e44fe44879ecaea07baf2c45ddf13c1234ed9c5163475", + "id": "7b50f54fb15fb3cb7c7c2e5b1ca1d241c37d3c10db990db87534ce366ea94d3f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AJEX11r4ECKF3r14KxNMwzLT29omPCtGHA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022035208eb90354b15269c5b1a97197ef357799111600bbd313afacce965838eae10220036a346f41e67380824086cbda241e058dab979994c171be6b9f633ef4c0300a", + "id": "35242f8882db3e61b508761316879c32fcee9a5677bbbf23c76c731cd68b2cd8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AQ8PPGdBYLGPAiUjpo64YAWCyPzsUEEBvM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022025cbb2d13fbc38c3de8cd8f134c41eccee79f133e78c83091888e4e1460f998402201abaf01c324594b4a3e2ebacf46c80ae6609ccb8dbc915304d0bb907a08a7869", + "id": "0889d189eb144fca050638e158ba32932733b39352998d2cc9307b4cfd5ce2fb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AKhaX5DPoz59va7cMYhcUsBEGLw73S7524", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205c29cd985cc57d5dfb9cc47619030cc11860ade66201f73db301a411dcf8769802205329981e063b8f671cf6584bdf65f6d9ee12d304d4b167399d913e1e3fa3a8db", + "id": "2edd2baac328384231be9165095f0553378679aea26eba5b149e3a54303f5fae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AN3u3wzJPUEbKkjRTW7JGnZwHj2U8ir1s6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e2cb4fb8a510d122ff54873ae69f3ed0e05e5218f16caf3e10c719a46c1f493e02201c2a6640e6904f68a9ef5375bf4d3e0a568a558a088d68087071f09f34206d61", + "id": "85d4a553ed41bc54fd93c81ec5021a23088be6f50aba0055270e382acefd98cf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AVL35iLjeDYJFrV3YK5kPHHijJcHbzGqv7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200acc365ced6b1cbff5d6d586f373ca5f9a193a20de02ed784c2ed0969bd33a27022033ed50bc892f08d837f3e38ec8c3d4b2cff4ad2ba148712667870d7615047b80", + "id": "122ae3a92b89b6c5cb1234075b7e5415c0b284c4ccdb66a480cdc47a6f45591b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121200000000, + "fee": 0, + "recipientId": "AYxAEgAvXoifkzMsN9P8nmDGaErUnGz6vE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022079f8b805e20edaa4f0ee8e1a1136ced571a1a964c49f678ea4ac945c77aa40ad02205f03efae06c98fe37f8beb0e730a0f0b6cb5473c70fe0f5f03a33e71699b4816", + "id": "9d1bb9a20498867f18e4cd28d56b1b6d0330c78da178d3ec2a9ea776d657300f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 121482157847, + "fee": 0, + "recipientId": "AXdk1YaWraKLT1he2N3LW4aBaZLyAiyiMW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022036f7275a99c91e73aceb5d904bdd1aff19007fe45aa3c1d926170e9883134730022074df6932914e893362a8f6473bf32e1eca016efe823a02bb9fa3d9ab7f64c6a0", + "id": "6676dc925e0257740d79d42f4963e30cf1451dfe23c282732f9b2a0917e7fda0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 122958047810, + "fee": 0, + "recipientId": "ALHwE12mF1DbBNZCTxcPRB6x1om2bRnQxG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201484a2b3a9bf798e204b01eeed2d235e3dfb11f9710bcaa5c78eb984c5a4cb2102200cdb0f5f44d445cd495f20b75707927abb3115b84fa1c95e12879f36e5d962c2", + "id": "50e3e17e80a61446fb6a9dd016d3ecd76d22014721416802fbb8ec212f732255", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 123284431502, + "fee": 0, + "recipientId": "APMwQmexasWJwpAyLZoDFYWFSGUamtNK1J", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220310d6c899dba99be0221deed8077220becb9da79118276ec3cab9226988e9b72022048bb5ea38ced208e1cb6d065248a390ecafcfc6aefd19b8f439c515132768603", + "id": "817ab6d80dd709d55baf1d885b47d8f83dbfe0009f8777dff77500745797fa3a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 123492648017, + "fee": 0, + "recipientId": "Ac33gzmvjrrbo75WJ6WVuuMYchi2oSG2cZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022011862523ab035e2600fc17663721256caeb4b54de512cbb7f22064bfeb6f306b0220586576a37ee87ce14575e8e440aae35c6b370bf8ebbb3d220337395e4da3a127", + "id": "fc0ba615203b330acb7946235f7f4bf01739cf6847e07be0d23d5218ac106d03", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 123626298069, + "fee": 0, + "recipientId": "AePe8p3H3wqiUUJ7N63aR4tSiSkxnA8Li9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022023c78ea21b06e95bbe80641f27a39ded89ec17a2ee2c8f5be89135ac4098a464022058ee31b7830ed287622216e2c47c34c6fb1929b72917b3f95de4c3e1cab45083", + "id": "5776ccefa14dca3370b26abed21365f1ffe65228a6bf5b25631cd39c461d14ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 124700000000, + "fee": 0, + "recipientId": "ALL3T4hBL5Hy6N1ekevdHuK7c8SCoiKmSy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100da7c3cc5cd62b428639c066eaf88fcf3fcd60b69b3a6988d1059868aced0a0b602204a2f5da3d7958c04d3edfd60bed8ce37a862ca10238c5175d49f8d994f4d0462", + "id": "b44e7fe2d2a0491576054ce4b6df112eb422691517c896ec9000e642d057eee6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 124700000000, + "fee": 0, + "recipientId": "AcM8Mm1xZLsaQ3C1UrpKBGETTWL42n74o3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022051d19bcc61978325c50456ad68fcba62f75356d48611b6922b7b3386ecd1febe02204e16018952ec4bd29530eca80eb7d1485b279595524a69c77a199b7795bf52b5", + "id": "dd34a901ab4d46ed7f9e4b0d9e4ef54e36e412d1e0169c63fd5e6479c9f49eac", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 124700000000, + "fee": 0, + "recipientId": "AG9kM2jLaRKeTdRiDBnmHKFvEodwxkyns7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ed04ff01b76d87858b06a9b4ac1fbbf452f237f3c89291ccc2dabf2ca2eae7650220307fbb2d586cf040baedd8a96b0745e7c5bff914ec9a36c6a9ecc1db28edb430", + "id": "f69027b2324e43c68a23e9a62a24689315571342b63651380d620e0d747cd561", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 124700000000, + "fee": 0, + "recipientId": "AbJe8Q4PeWXGd2hurMguwVe3BLaDHfWRcR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206c292d5bc621a09520136d3763e2ea99994c92164f083317ac7d4d84ad67c9a7022064fce43058f8d19cdca3d633fcc34429086211c6947d4deedec92a96f5d72e7e", + "id": "d6b27e61b96f4a4e503454f052fbf313559f9317694aaf0e3b17f9c12a84ffcc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 124700000000, + "fee": 0, + "recipientId": "AKm4VdRbpqQRxNUatCuHz6SaRGTatMGUsD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dec71fb87031a989bc6dc3f048ee308dbc97ec349a45861efae3459b04424b2d02207a5e5f4a089bd56368ce0cf6a83db247e9a8b215c0aed867dc83141ddd81894d", + "id": "f3cc88247afdfd0a8cab61e339840363c7353287cb76043acaa613f1ee5e8555", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 125174485431, + "fee": 0, + "recipientId": "AUYnvSaH7v8kU891WuBGDA8XraJnqfPft7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210086f778878fc1586963bd8994419927a9a1f6c88f899c5f4f19840b2c42dc559c02207bde1d3bcc0f1faf3143b3845f6671a90792bb2e3ea1b041ac9d80a5daa02615", + "id": "2801d176b91ba63de86b0332a26346bc1e4c7a8a3a967473e18c09e559965121", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 129540204214, + "fee": 0, + "recipientId": "AbnX6Wyoz3d1LLBdY4pkYmRXvkv7qq57Nw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200d2330b9edc3faf556ae66e1c689237bb630dea023ccaf3037dd706e7a9e34b702203ff844c9710095e75fab94dee0d6f15a2c233a4c05e37a593008ff36bffbbea4", + "id": "0a1e802ba4d8543ca30911f77fc551ef666486b36678ef2132ecbfee416331e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 130000000000, + "fee": 0, + "recipientId": "AYxWmuZJwzYkjUarmquFoHjG64ityMAwTm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a55652d4a247e1e536a05ac2233c5cedcbeccf6e8b8e701af2bdb07b42a72fee022058737e0837e904be3c969f81e9622890bf7593604e8ef6ee19aa592ecaed0088", + "id": "3efe48cfa53baaacd2e25059d104d656493d9fd13eb2e8acb508db55db7417dd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 130000000000, + "fee": 0, + "recipientId": "AFyuiPD8r62cdnrrAb8PHdiom3ZgUZsFQb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009f3ec0907177655f8d54eb4eba7f2c96a3e86f239a914401174b45319c482c99022051c830003a1111109f2f726fb888107ec30c5faca8f54a34a078c4831e62ca0b", + "id": "7a58e80d25d0d063bfa357729ce25b66b833107b74beb953899a4724e981d47b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 130884887397, + "fee": 0, + "recipientId": "AGDxH1FYXf3XrL6nJ9qhwmGeri3M8hg7dM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201cbd29a31f62bb30c95fda9d78c8d860fcac3939f39fb7b5638257d429bbc69702206cec558ca827f47711b6b0685b37a571487840d013dae31e95dcaccc31c0d82d", + "id": "218113406ee93b29cc9cd8ebd40d51df6eb6b37dab860641087fb2990dda7d88", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 132401763390, + "fee": 0, + "recipientId": "AdFxLwoJ8JVWW3zpPWkvcpTjbVtawPhh1L", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202cd5cd7114a86e5ea04746d2a44b1daf598749885f03cb8b69dbcb95e8f7591e0220062c9951b5f1476594c34f7bf9a47bb637ea83e843e46bc070ef8f80fd20112b", + "id": "21589db4eafcfe26a65312b727f705ba20dd4403d8ee7fc09dfe4722005251a9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133300000000, + "fee": 0, + "recipientId": "AVzze8R7jrAugNCcUDpS6gvtH3jLTBApfK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e575b4d782d64bfd7b3a5208a9b0b36baae254418f704ff71a519be9e33f4504022011766b87c099d21616072efcb5916ae72965886276837b08e8737ec00d20f691", + "id": "f0d6338a6ae19444416bd44a0a194c4dd6043950bd0488187c2ae522f0c9cec5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133300000000, + "fee": 0, + "recipientId": "AHiw6LhXUDksaQkQ5YEys4bR3KQUpShuhK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220336d5cd425f12d9aba380a3112d53d3d7c8f64adb9ecef94f69e6b42afec87db0220521a58fd1b62eb89abaf64b5ba8e79493ff4b146fb9877ade991119b4d5096f1", + "id": "01968fa393539a33b61a61e9b27f6c666dfc9a75618c5515d4fc0ae3e5e2f7bc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "Ac9Z3Yq7xJeE2Vt5QcRPDmuaTS1FtypjyC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c041ef867527f600f1c23510313ea3765322127147262b2137a7fd6ff8e8ea6002205d20cf3486dc1e2604b447f5cf48b4f1463c0fb8e2a5136d4ef43e5017355bbe", + "id": "fd69e79e125889c7e18e4ee6f3b9b6a6833185dc560cb07a24f76523911d9e87", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AeWazLDW2wcTR5tDFoThm96tZFVKvyg3Xw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210094396bc42de431678812ed874f45e95dca0c9a28a3e67a48530f271fe1366c6202201aab946ee2c25becde260943d413a6b70e800ab2ec02bacfe6cff415c439a7eb", + "id": "99cbdf7999737e6e3ab6f3db168106e630e99e78c7c7f6610acc9cb77d9b74f1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AYKHBz1zZ1dQjpfBu7jJforx7wqX3MSw45", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022066f06afd81dd1ae22a41c79ae0f8592333f9a1f40d0dc09a91fc81556179ef6802201c0fb882cd5d81ef77ba6b1bca9ce4a52143fe2cfe9c06d0c4d1b6737d27b42c", + "id": "ac75980be3c16d88e18006a739c0405ba62ff7e4573297509c22799f36648090", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AGytj4HN26YqbqoYy8bVyAkHr5DBKqck5S", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022000cb76109a01edf4df270e3a942744877f14f08c462e21f48a97e8b401d7e0e00220497ef0efaa27f2b0f98ded7c797f897411629d2500eb9541e8450e2ec4a85b75", + "id": "dcb55ec4448b657e784ffce0a845657369bdcd31ea3118c6852f3a267858dbf5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AKv5bQtqcrS1NwWM744ApRXLGzxq8th83Q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022000f10c1b56695f77a65301a9c63d16f2c6a984a921b4c43a1087e41362db454502203ee4bb6a6f6fec5cd685966c65e032b9dff460102e35423f16d677a98d4d9a77", + "id": "da79fa8dda0f553b25b8cf289fa917a8f2b3154d16754749c0beb2c0fd541909", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AW3MKtfA8qu3WyuWf3WzZsXyNJj4T9t9d9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009e8e61957950103cb3dfc4dad5e18efe80e1f1e4974d1c1c079122ec080087cf0220209c4741057ef4819de778e3353b58a0a9fbd45013775606e3eaf16f91b4c22e", + "id": "880ffeb2284de4b16dd38499fc6fd15320071d08a0d19b1837a1973284b44795", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "Abe1tPmDJ7a34syYde7XCkdUfAGjPmhNyD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206ee7dc450b5b1e60271ca2172e1e1c26b23d6b9e163bb8ef819e4d7a2cc3937402200b5b7687c82a928ce9856cc9d452ca71e762053135ebd791bf09e1ecf2f1d94f", + "id": "436a0eb389e7ebb2ffb7215e10fcfb90b707f9a3fe08b46b4a08e53919106bfa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AaCJwF1eVDGA3Jy6MmQ8emMB5LKkPFhqPF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c319ac615e8d54a36c919e40e7a6a77e534abbd952abded7533e11a1bcfbac2902203e9a945df2c06505823ea920839528d37fe946ec8cba4c75e1c2bc035d1e0624", + "id": "65451621e425a1118c592029a9b6f47f4635937da9640edbbe2aa7e1531b032e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AXYh1dUhBWumWF3Saj7F8UH46TqKoUR4oq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210099fbf3734fcf70371ee259534f82e2ef84a37b9000b254f7bef53e812afee3a202201c0aed05b097804fc2e6103c996b323cd0d1a634d2bf08560d1473cb19c98583", + "id": "0e8d05444ae15dbb7cbfaf6ea4d03733dfdcad333267f9e7e7ed18f5975bc168", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AFzhTRcF1mdD8bswGM4kjyiAbvHFZ3utDf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a40e1a906abb1198a9e6749f139169488103f6a089384414c228a375c3697c28022045e6829b51d7f3f0ffb9e6f27132aabb8c13c5612d9cf98b2bc65736d3bb7d6a", + "id": "53b7a5d15d2e6c5d4b977280ec050e8bde87040748e5b25901459f139c9afe46", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AHj3b7vTcrvS81U3xpWT62G6C4jVBpXB9z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a226e5b1d830f56939fd2b860c004c54e6f49260ff1400d21509c2e1f6c7404d02201997e86e6cb516c88bf6ffc7ee6cafb855783954fb8e131bd65561fbbea8246b", + "id": "7bf54e1e48b45d11b86e06636fbc78f412d46ae1e71b2dd24dc3474aa6290dee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 133400000000, + "fee": 0, + "recipientId": "AUjtjkgdiyEpuByP1uajKuT2HSeb68iq2K", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220415974bfe952d18dc3a26a966b9d7c63993568f834a3bed5c4bd08784331230b022029b654a8a4b0da40821d6a63dcf40fb325b58579041008ce1593aa8f1b785d09", + "id": "cc3fd2a9da7d622e24a186044bbe5479ce94cf75be168d9e2ce5a950853648b1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 134701272086, + "fee": 0, + "recipientId": "AGyWeMScLefasU8tiewWRkHBeqvSeaAGii", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e75d0bf3d62eea3631a95d6bf61055b0006c0ca0f92007b3b178e4ae82bcf5de022065d1c1c26883926792929c9945fd6fe6cd1054ca716113d146598c669f8b7db9", + "id": "2b80ef415bf5e28b1cc9b5fdd407c3005c498e6ed0d779defb5386f12a145b05", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 135000000000, + "fee": 0, + "recipientId": "ANZr3b2hPsGwTtVgNxpvMkyZZ6Vg9UZkrx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220774c16ee61fb32555f68f77aa42d26f4df6689af47415876105fcc44049d00f0022026855b61fc8975aa07b518e0b4043f63f6e4e10936b25266754dd50f4d63c014", + "id": "70a85c890f419243809d0fefe37b7e6a03f73d859c8cda6f59cd34d1b7d67a5d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 135100000000, + "fee": 0, + "recipientId": "AHY7L1v8LzmNke19ArEgzN3tocigVaAzLX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204ea6f57c6dad7869ddae0d8bd270a4f3ac278bf5887bdcf587ff4f083013e322022032a61c4901116e5714560a5ade681f74b41cb9947426384ac2db55df4004131c", + "id": "a5bb75d5aa8aad7955555060d8d43eaf3ddf39c0b7e43235466870752a8fc283", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 137900000000, + "fee": 0, + "recipientId": "ANAL6hHrK34Jy7v3YWGodsJcb3Egau8UcW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d12c766b40589e3c976e03c5a0ed64a8e44c32d3f0b1366051dd37bd9c6043520220455b383091894c83178c0ff07a45108abf5d442fa44d2dee6adc8b8cf884e018", + "id": "95fb3162c8f7beb2ab5f6fb61666094226db627b460f0d5ed45a76483064e6d0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 138600000000, + "fee": 0, + "recipientId": "AUArTLyiUVrf9h8N5fM4gtv3yeaT7sD7pt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e4f07b7c14b4136b62ba30d4dbb23ef7c34d0b5aa574b528fc71975134700fee022018350059d6011c7c4310aae232b683b8bdfec4f366cb952c8a71573080eeeb73", + "id": "cce0cd6d09ba3eddc5acde554e079cda7dd1a370a2ad2136bee3da660a7bb6b0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 140300000000, + "fee": 0, + "recipientId": "AKbaSMZ36bgW2jL63Z2RYe221JvEKBJRpK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e1e2cec556612a736da0487ae900c979093566cb9d461ee5f97c1cb8913521d022059f3e13018c3779000de42972f514a49cd359f1681a8f389d203483c95defd9e", + "id": "77de4c98b0eb2024ba7df54f734d9e0d86efe0250eb4c38921be96039a92273d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 140332554565, + "fee": 0, + "recipientId": "AS4JC75Lv8avQHSDT8aRu4qWLLzy7B8YnJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220220c567e49b35270e9ad3f4618ce46a1d9b86d8bac6f51f77a561414b1c589b10220287e615bbcf9366ae1cc249f5652cffe72e61bc76dd6221bfb0b9751b359ed63", + "id": "b45e18990b9f754737ea2442fb4ce19f020f9ff1ac5bc083d69e5e24810d0e34", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 140332554565, + "fee": 0, + "recipientId": "AU1Bz7CJhiNYneNYEuBCXPY9WQBwCxCFSQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022030a79416ae581e874e3f85ad8fac4d2c0ac39d7287c92e807452122a4b18977702205b3d6fc1ccddb6741330366926fd70ed89f160eef85bd2dc161e73984bf7c1e7", + "id": "627b1183a9f109a94542d761904f247ee43527810bb5b32c70c6089121223f1b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 141913175258, + "fee": 0, + "recipientId": "AWSYgoRKRpXfXbA3WpJ8eegTky4SdT6dYi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d8aa4af8d9f52b9b1bac3b38217220c0aee778de34fa652dbc9d2df182540c840220687ddcebd64e8b0b02aa4e18ba738aba219e0a66157d373e840ffdcb4e3d5093", + "id": "fe7f90ba3d98988a846a2f943017bd03c475625ec021bdf13799f8cf7ed70b89", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 142400383746, + "fee": 0, + "recipientId": "AHomeJ2mZdS2HdBnfpznUGWKMdnGE2ZyBi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ebc13c20a25aebab60d043e3983faeac70aea83008e8531afcd3b3aa986395e002207e37320b8a5085c0fcabdc8da57c7f67482b46847251beb21ced9096858d67a2", + "id": "73f0d8477a0a9691839f121b8a28b86f9c3a0283e60b5dcb57a74256ddc19c1e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 142597922946, + "fee": 0, + "recipientId": "AbCmk7JqsA2oNHjSxs33hwEwm7i9ZGfNTc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ff68a3af0a8400a3f5bc2fa933df079aeae3cd8a33df0e6d06883b8bae604f3902200618c92f102ae967f90b5ab0bf2f17d561501fa6b0e42069cf958f58f10131b9", + "id": "1b8a6a9805b7c3a9185eb8692d93bf0130f0c090decfab0f25f33748465c34fd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 143671138618, + "fee": 0, + "recipientId": "AKpcMURctYwUa7NFWwQbHMPShAmGvZxTDk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203de99b3f8f7fb93625f52d07e75e9735674757e5728a0833e282e21887c399ff0220665f3516be9e94ea507964890dd5d1de7d0829143401ef78439503742444cb45", + "id": "ef58d87117fe3cfb73ed87681ef9df9144ef889a2027ad85f341809548a7d69b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 144747007068, + "fee": 0, + "recipientId": "ATw5aBDeZzzxdz1VhJH7zjYUS5c5m3wWcz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022016e3810651bdd50d22b4240aaeaed98bc01b383990a18711671e27fc1c2bfe35022027628ae55345065e5f4e21b462408a9845cfe2b3d9666506017fda87ac6f7e07", + "id": "d068d9b75cf07209d53076886c817de5d449938fe3fcafd31828ea400f4fd44e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145130623474, + "fee": 0, + "recipientId": "AeReN9UHpbqNqA1zRP3My3iQYYw4vBQ85h", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022076fa46b96abcb0ac7b0b0b9b5c8870269edaa7b4a3ae4fe75585837c73c1194d022004e5e214749129ecef9fcbafd06325ec4645816366431199253fe349af14bf86", + "id": "a784b1ea1393c00ac0c8e58fa19b9e137f1e8611a4e8f81f65660dd36b4f3882", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AVgLdwCZLBUVRr84tPzwgCPLcdDRRTvkMd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206d398c5a3c63f66e37216a297e442d8eb490c67bf99cc0a98c5680d9ddbe2933022048170d0c7dbe5ef0a30baee4c669579c1c414fc12cca94af65adcd40e90e2d39", + "id": "0d36bc9b909f51dd5bed75e5022f632bdf8707268e74aba53f8d7400849b49a4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AVroDA7WnnpRhsAy9187e7mRs3c9p5ADoF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e8248c25c74669b3074a5efbdab29b715d4d796bf1b139917634cd27b72b1280220184b4d05880cabc910f372c43c13fe5f3db2540174993467dadd0bcab4c84873", + "id": "6277e6f92b5b75671e07fd26b28a16a82038447cd9ae5dbc4a8a525ea2969204", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AdQNZMDMYsqUhuZ3F5Yy5FvUsdRDSupxEP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009b106d35d40c34def24a3af078288ce38a5c42374086bf13c1d43a354abd4ece02203797da879995e88f71e7a0d04b65ab17f80d073d0cd015bec58f1f4a5bac25a6", + "id": "a76db685b5fc59e38d3e25f7c43cadca9f35dbb079c8b5b73bc8da3066b501cc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AbWS5yJ1gwx1GhpaTYDQvJSy3bCFbA2vpo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e1ff4b27c8fb80dc2427f493822b570600fab317cec5a5cb53a58e10769b6b202206a7dc2cdb7785ae00ab6240db3da4abcff95042a33f5c620e42af976925f2c73", + "id": "560771be8dc4d0c21922a7a57b7fb66a9b20803ac8051150cadb54816086eff4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AXfFia87jEauZWU6DJHRbECKk44EXSQmbY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d1ff7a6ec56fbd8fa592c687ba088f198d0752abc667ef957dd2c2e40d9589c70220218d36c822d70d25b1fe3c0ecc1efe85671b09d16765a93cd12c20b45f1fa6df", + "id": "63d81828f4c3e1455eb6a1aa99ef36b241b5fa9dd7c5cf86c92dc8781df88b92", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AJCWEe6hRQRiCC1gLdS2TUSbEwUPHxMJcn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205b1550775c4cf879ec7500f8eb3880a3ec9173db41a7e8e249db14dad79a0d9902200cd06919a283c00dca6cbc647f1f09398dc377c8a4819db6ec20458c55a4affa", + "id": "ec03b808b1f705553a333f882008acb5330f3fbfb87eeb64b7879f4e3e9e013c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AM3Tdmm7PajytJqccZvjBmK2aNzfkmVNay", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cb1a88bc254deed2de2aaaa7165c62d8ac24599cb716cf294fa7650d731c7cb8022040c5516b3945d87eb5bc4101ad5fd8e28c04a86a2475617d43611793c2e7619f", + "id": "58ea9d0e7a38ac86f105406e615eb08a288053f6be10db870a126b4d5b1a7558", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AY1tE8UrGCykVngKVmmjZfes21arcLLyzn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d0b69b396392afd07b737fc4aab7262c5e693a61b4869afe53696e1d72c159d502200de0cb71068602b2552baec25e1d3421c4d59526c01a99bc3475abec5f63bd1c", + "id": "2f2c0ef7895193de3e7405dfda9ded41aebf2aa0143a3870f879800ff3af4a6a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "APAFtJderG8gJDUr6avxr4thm5a2hrYk6q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d43e939b9b3789b1673ff939519fbcdbfcb6746688d1309318b2bd12f839c8e022009e186c21fb9d2963e2c01d863058aee90c9eed7349d3243c900d606cd7f5163", + "id": "d9e039153b8d06ab7b1e3fe64eca9bb917bd7a7c848eeaeda9f8d2b28d24d5e4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AMFSfB5sy97Dr17fqAF64G93pxkSn6mGqH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022005a7f2529d9264b887d323228132518980dc91ea2d0cd5bfed098b8f05b31acd0220676c8d86fe6be5a7fc90ef3ac2db26674377c33188b694e5106c36850fcdd5d3", + "id": "1f7ff73bd0c0b6c42a73499dc8948ffc23a5affa758ca17425d60d03f0e3c412", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 145500000000, + "fee": 0, + "recipientId": "AQ5kzJMSeEEr14YkwoTTGpjj8odwQj121R", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ba6df9e0ac6c1db0a889bcd3c30460b2d5460ddffb7867652dd4e0fb296d9bb70220039f2b0799cf13b3f9e5f4f950afb53c12c72ae1e6ccc15b6c86aaba0febd322", + "id": "39afbf86f83389014c2d00c1b56c215278a7d12f631a10e24615b4c69aab3dac", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 146600000000, + "fee": 0, + "recipientId": "AY3P1mc5878sCMkCFmJdEBhMXjzZivm4rd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c563a228728d67c90cc13501efe0dafd97919ecec25afc4e56c48c9289f0d85a02205c91587eb8001188c7e291e0c851feed13f6960d1828b7fdc6303fa49eb08b41", + "id": "a1ab69f665a37b685d9219b051428f1e9930a4e8bd908623085b1cc5a85a7a37", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 146647519521, + "fee": 0, + "recipientId": "AVwxCqG9GamW79Xb96S7ipWUpxLgR3shoe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009d426ec91319147e8ce9d95e7f4a2bb2ef20ac1a93c4b9a79e0a45b555aac9840220470cc4cd8993fe92f3e41ac71027ea68d6dc5e873a4f5f4042d2f6e03fea61f2", + "id": "6b19fd28e4fac7c60537e63d10d0bfeb1ac28eb768db7c05a0eacb22ed0c945a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 146868042106, + "fee": 0, + "recipientId": "AUALn8exeu8kXARs5HVxay2r8pmSmxMVdq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022038a92d978af95b79d68342d7da3d86e0de7ec10fe793b87e15e889f962f78bfd02207095fdc539244f346eb46a50d00a5a692b0687ff1d6d4597ebc7f2f92c7b3aeb", + "id": "d3d03e03d5bba603521e8be851fbb870e25dee280cf5a1f6fc7a025d84314a10", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 146868042106, + "fee": 0, + "recipientId": "AUDTxFyejQZcR5bNSXBkoQocuoquYfChG9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c460859d8d9d4a18b8c336b5345adf8023234c90a0d2f86836a39ac3b1baacc9022068265214028204e47325d2f157d27c6ac20c0f43276d6df706d4492687b13e23", + "id": "04f2135cee1a37d6854cd0d10c8db0bcc83fd8176833428f8e5cb4b891aeaee5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 146868042106, + "fee": 0, + "recipientId": "ATxUGRxnCNHpafcsTQAimxEygTJEb3syCy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008b9f09ada3004e0b966f2a208bb2c938b797b1973e1e8c2b53b7ad4b0860939f02206f08c4caaf1ed0102430c9b2f382f58e6e0337ef8c11ec6ca0894263b99086e1", + "id": "7c156161c977de562a5613d8b6b955144a56ebdd9efceee9e8981ea639e144fa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147015057164, + "fee": 0, + "recipientId": "AJeAvhsxGTJQKtd7RHBfmNtCy9fPUZYNVV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eba3c97cbd1002056427991b7c33c21336783809f814936183ff030378ed3c5002203c6dd00bfab286353f3565d3bc7be98975a9c8f412c409bf182892043e350719", + "id": "4511231384b91d494dd617db65cf90e99d7be45a761010761b39b2e07cf0a896", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147015057164, + "fee": 0, + "recipientId": "AeyAvcAptKRxirAwuyLei1YLUMZap6RSCe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e30b9369318e1cad7ec287448f11e9df997d10d4ccfa1cb14c5bc16f042969550220716c4d0772ffa77db00d076cea094ea196e1bad98aab377b20e87aea6e7b8b34", + "id": "a033da0ff01712d4b8e724846bc0f524a3931a0e268cbe798a006361a6b5a2e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147015057164, + "fee": 0, + "recipientId": "ASHqJXYyyKCVuhjs761cz5bLm6wy8nbSgn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203c354c4d829f755371122ce7249619b2df5a86e74830f1809e977fb80875e5610220713491f7571edf18f009dd7d0a1d4ddd94ba8c53fdc7a1e53596dcbeaff40315", + "id": "86c30a20741f5fc1738e4f0180b64b585f0adb966d414ac45316a879fd5e7fe2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147165816371, + "fee": 0, + "recipientId": "AVpQfwASwF3WSC5CM7HErLYvYBkpzkTmUu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220030a63d6cd68e0fcac0e2eca89c14eb6a8832d52efa56e6d1a5b772805d1ea480220091c1fc5904423e14a6137f02d153573765905e76d1c4db66e945ec7851763b7", + "id": "93ea7b1a7e4a623e75cb4245a2ff78894c9f63c9ae4438a2b614350b9f65f1cd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147695811297, + "fee": 0, + "recipientId": "AevPmhwSCevsJkXhDcKvaRNQwnG32ygbC5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d748dcbdd417e023f4e3976a7625d8c7f2ad45644e768bc5923210b70b8e535102203ecf2b3f03c5caabde8ab41047155bb14ef36073bcab9a76b1fde86baa8eb21b", + "id": "6cf7722a4793a9f5134e80853eb954b8772ec20f9e7732b109c7489b722f7c0e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147792818385, + "fee": 0, + "recipientId": "AYdQPyMPRyP2jpp813PpvKn18rew13EJTk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c946a710663b006a6065a032f29ae1eb13ecb2864ee79e7443e795a7d566714d02207dde19f663673b881bb01ddcffdc1bb88481b42c7b78058ef722238c3acea288", + "id": "5ced9e0a7ae04dbe285c3087898108f56f6ac791e3650a11c9b6d05c3279f0e8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 147794072165, + "fee": 0, + "recipientId": "Ab2sMHQxXcEFT347HyvsSdXqTwBWcYkiNo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008f73302141cdd6646a1b0c2be14aeb4e40bed6eeea5ae720ba24d482f45ce1fe02203213e8ed9fd98004e859de842345e8a3856b9a1920608fa050211ea76dfd7cf3", + "id": "ef80b356e77a8b3e18c21b11b6ac09b3f89b7b99f33aa12fa53b4d2ee3f380b5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 148338192678, + "fee": 0, + "recipientId": "AVS2aoVuzEnY74iWsgzfzmMNQfSRc5KTzq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c6b3c68949296e1794246ebbc64bd06f9f5d099c5a4498030442a6eb8a78d6f202205f0be315dfac6b7647a33934a04df6c40a2e40dd8a17303f0b20bb09c6bec6b9", + "id": "e38e2053c5110aed17e4a7654650a3061ca5441c3a5fd8d159e2140b20d26f33", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 148900000000, + "fee": 0, + "recipientId": "AeNApTdu2Kf7vphV4H2ZTbopitfEchc2eb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201255ca8ef4fb94fb2a0f9eb089f9f1d48971adcc9bd153835e7df739109fd6d7022022a497a0b8ebc793d25e3348fd884f32cea85189bdae2f66799a3818c15e6936", + "id": "5e76229c1016f43b961ab11b51d4be7598434be225f68ca4347df5106c68b0b5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 149442810367, + "fee": 0, + "recipientId": "Ae9cyMJ32RatQDd1iPd5pftvBjFpRyghhJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210099179595efa579206006e9b6689f62d222f67a4ce4565508d7dea57e3325c917022009425c7807f05f46406a05f02a8cd70dbb6cd20f7ee172749a54eb21f9fdfab5", + "id": "578b56b6076f8b418eb1df12980143f0ff841890cbdf447b7d1d5320785cd0fc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 150899497250, + "fee": 0, + "recipientId": "AZGVDCCTnVz5zye3hsckws1AZkgnUdEwLj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022056e8422f6cc77310114d7e7aab0eba13fbca2cc242a04594af17c8938d7a55c302205d8a258dcb617f878e4b25c84f08d69f77c5575e7c6840028059706635f5bd5e", + "id": "c543394aca3a02fea18e06f89d12e6f170269787be922539139bf72ba173caa8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 151036016000, + "fee": 0, + "recipientId": "APuhkVvG7u6itm8bUEfqDBpw8vTqTZMpdE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008db754d788749079dadc57c9bec350c02487e095df746ae3e196c566b62edfa50220089ecf52f504b54a6d5ec5da34b0eef3bc5f151d8a65598c07da8cf61dc2524f", + "id": "2a5b77ad3a7559a3b066e6845f41c73b368032bf9c125825f6ecfe7f84a48f78", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 151200000000, + "fee": 0, + "recipientId": "AVJ6FhpMSHC7sywQDJujkwM9yv75tyzjrj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202a8ffe7a80afccaff07d8d972d11e8af2396f9912171e3fd7ee869a30262739b02205b9785b72fd0ff1d1028c44d29d9423cf3a22a3e5c6514f86697742777868398", + "id": "e1e3ac19ac46f91d4be3977a0e95de5cb136219a984b03b1782243d0b5f2757d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 152400000000, + "fee": 0, + "recipientId": "AKMDyKrWkSbpU3q6oTxuoS7nr6G5US5Ppm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b12f5c4a7c7e0366f303803b62486f0d6685eb60a873fdb9f15d508c8796af2f02207d28afc1bda7c2af2c650a7cdafcdd6111f950cc10d403fa95132e0bf5669661", + "id": "188f9c090bca5ec2214a0c41ef741aa5b7f3a947cd2cc6ffd51375c837bcf9b7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 152400000000, + "fee": 0, + "recipientId": "AR5yuVPejTDoeHhfbAAYBMCoMPWfT64wS5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207530f550343588e92edebc0c239f9d9215362cdba07fc9fb0a9a0ab5a3185baf02203a151c01fd5ee65b3b90915d6bc0d49a756a687e6e409ae21dc64f18cdf61c8f", + "id": "b5b6f86c2a8dcba704275301e6f78c80fc63a7c3f1e6f80a11e4c1f35e738e2d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 153500000000, + "fee": 0, + "recipientId": "ALhmTEX4c8Cu6j3LmvMy9QRScP4qd5uS1X", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009582b66e2d7e23ee2cd95f4a35683e67370dd60f84bdd4bf13a70b0e950255ce022027d4f0e29103bbe2c7626378b5d92f9976ef5637d4e30201f41762f6b58abcfd", + "id": "d337a4d55a6f848c4b56a1d8b4267ec04379b7ca49d51c4ff12bb4c91dfe07eb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 153600000000, + "fee": 0, + "recipientId": "APL1n3DRcNdNvtGEAVXrZeF8aE3kSje39B", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022026c5e59f6f0389d02d8303807aa6b56a1677759397bf1131fa923da22cc0ff3102200c206cdad36b67bbfafcdc42bc5af3a43865c4dbcde49216cd6acffb1193a86c", + "id": "f855d660cd350500dd7c352d9e1e298f7faa93fe3b99d54fc5179caa3233e9f3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 154067041808, + "fee": 0, + "recipientId": "ANe5pi2srBMSp2Hsdi4dvEQkmRw6QRufWE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b6021389172baf3c7416f95ce39a517a755f44e213c7fc174dc47c2a9f67aa14022002f08e8c6e0109219b258fec7dfbe25e18c5d563a99cb751219955a68ada0e0a", + "id": "300260d69932d0fee55a854007a7c8e16da8ba9a531fef306df009c500e42901", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 157600000000, + "fee": 0, + "recipientId": "ALUZm656LEa4TqfwdtkjP3XFidv8hdFG9y", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220564681998c47b9747919eec5e8d497815a14002b497a43789007d5fcfd1fa37d02205b6097bfe9e3c44dd8a7bd9d610a55d72c674cca6f89e29b37847ce57eb06ec4", + "id": "2c1eb9434e82279b5ba0aafea3b7813e4f024f3386126e65cbbdcaac3e1de27e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 157600000000, + "fee": 0, + "recipientId": "AWtwaGELZxRTxu6znTh3JaxMJWyUf5yg2b", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a87d31acee7834af08c83ca70917065c1a5a413a74d7c4443e316f24a8526ada022077ddd8c1add111bba68f01695dc922f680b83fa3db1edf0e9750a0e041a3c5d0", + "id": "b724ce6e1fd1bc009fd05da5523c735a21300fbe1a4fc1da52cf2c6ab9d66ba6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 157600000000, + "fee": 0, + "recipientId": "AH8dy4DPGzSnyMmfXmJrtyWhUkZoqsscWC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022000f7c8a6f8a8b0bdda54992f15a566dd1c9c9d5d48f03b0205104ccef6397312022052ba2904b07535406df11778a86256ea7893b6e41c91a7889aa4e4f4e93ce7fa", + "id": "a7d0234ea7c0595d44d473619c7464781cfbd6b3be2edc583b624cb9210ae7f4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 157600000000, + "fee": 0, + "recipientId": "APovNoMU6T9i1yvfXczPauvFA79FFPP3Ns", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e8a6834b4380b17f340853bda75c96ee9c553ba2b570402c28261504ac6f11e8022072c7c130180e042c97a660bf1f6f2b17625dc24acc16e03c4b54bfc767ba4e02", + "id": "86f6394cec011bda87c30b080e27320137e936adb8e67a9f9fde6afc84582c7e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 157613342003, + "fee": 0, + "recipientId": "AZd59X8LMYSdKgz6zz64jKxhs6YHUcE2Dc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022021513ab40baf4bd11223c976ec339be0374e8a96ee60c90e499202e2e14916150220229b9355d285561da1b1cb33ad210df305e961ac485e3ce51db39faeac96475a", + "id": "a3b4066f32a764dd0cc8c357a401e91c30be8b7913851aaf8815c61f1fde18ed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 159675898944, + "fee": 0, + "recipientId": "Ae4moTG7YCQ4yvzW7NV86aZVA1SL5jZGUy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220799233668ec57eb2ca1b0c18b433e788ef25df88edd97bdfbc5919e1184efdb3022074a3d4fac5a531fee1ba3a6c6378d5fd080acd163e8e7f983969b1059376e9b1", + "id": "2d900441744993dfaa79989178ee5ed78cbf8dc5a67ed945b2558b0531c23653", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 160600000000, + "fee": 0, + "recipientId": "AeR1SNtu15o3UocwDDh8kBeov2RWeVYzve", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c3ca5aaa9b76d2d4f4916b9268fdedd5f8c75427bb980e7437e5e50e4028560d02207f145bba9763452fd5cf86013c35ff8a71aaeebd0966e2a6e218e7dcfbd43dda", + "id": "4f732a783e8fd503af9584c2116f4d2d9cdb0763c1629f202646dbaa79445be3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 161600000000, + "fee": 0, + "recipientId": "AP8RQXpVHCYEjNaQzHPZLYEARWeMzYAnUH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dea089096a8ef631cd271361fd564a626f541409e3e166082890f5c3d39a5db1022057eaa7c8ca2fc37cac5129c8994f26b231dada2607cf7f3e026bc5b22c9ab89d", + "id": "99aad499192c22c4bcbe71b8c133bef61eb16efb59b9281323e04c6c4b925860", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 161657213879, + "fee": 0, + "recipientId": "AdLtfyiktPxNa8Wf3uEgGiPHHRvBwb6BVS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022074316c70b4b377bf7ede11dfd7e246e947479c62c6343c8370db0f0f5ba9e19702202487a7a8e86ee4970e3bfa3c42edebfab845b42f98c454db3bd0310c085acb07", + "id": "70a2af7bb1dc6807071d4c2ecc193a16ea4adaa872ff14de76d40e5d07c3498c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 162022981403, + "fee": 0, + "recipientId": "AW6FnNuwQGBcJ9nujbPDx7CBjukP2tK8KN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c29213cead6a57f15622d66bb90dbfd8e9f46bbdbc186c752113d264e930486a022006f20912f179934ca1f2f94e0fd43ba5b415666d9949c11f0092de3553f648e7", + "id": "5c75d116edc4dcc35d0ba18d7ad56aa45dac0d9404fbf9f52e857fe8e8f34406", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 166527964751, + "fee": 0, + "recipientId": "ARkk3RAAeZSrA6Q2NiQYi1F5J27yW8Fz66", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220297e1206fd771d82406755d928bc86097eb8126f245e500f9c3f425d5b30fb1f02204b2258bcb30b1a11cde8cd76c81ee20482f870715cfb861c62c1512bc2737344", + "id": "4f3aa1ae966a9f4ea2bffb9e340bd522f3ee33479be4fea1f63d5a7b5eb47c0b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 168221932329, + "fee": 0, + "recipientId": "AN7y5f2nixsdifEkZTcgpnXWBzpiuCkcQQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e460a99b932fe2c9b28720352ea943683be7120993e9dc4bd82bb336167c597502205a46ceac87e8dd5179eb40194229be169446ccdf085089e657b200089bcec557", + "id": "6bd4850be884fccb495c347ca9a9ee5b46be31fc1a0404c03cec0608912be6e6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 168973133925, + "fee": 0, + "recipientId": "AQ6ziotXeWnocAnwzz7V2idW8uxz5V1pka", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bfeb6d49f03a89820a6dd1e82d238aca795866ca245be3f43e869dd1ae5bf9f20220580a0224b2b99b38564cee5b3470f88aa7c7106068e2bde76fd8ad1d8ec0ed98", + "id": "81e4e95e91247b73cd9e2dc3be2a84efb9fb428f04b184f63df43d3c1a9de40d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AKDpBAVgjd4cYXd7dP6khrK2Q5GyuyQfoi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202619a5fef5f87ca49dadda59139f17bc7f388f09340914c5e0b997c1f0492b9d022031a12aabd0dca56193409dbf39fa3e9ee332d3e47fa4656252e161ea90563c6c", + "id": "6c86bd9ad81ff5305fa35d8eec25872292bf417ed456a801bb2301cfd9d5e79d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "Ac6zKra111cp2QccMk6zDNYwyWrYsjY4zo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203677d3e5102ea36a2a7693d8a47c86b3f182aaf7c4c8cb4118efb17eed87b6f002206a17cdfc67917a0c18faedec37baec1be75245eaccb45dc7fdf8ce4cf49b1e5a", + "id": "df692fe1270c264c2cad4bee6383428d2038d6b82d29b805d367690097136575", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AZbfRSargxVS9qk4cBB2BfVCiqir8bsrKS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bba6694b9e169a8b77a512b9c09079d99586551db4f5848a059b18d7500ec4e60220438d0b0f1d75a0621a6a06375ba8a3f75dfcc542d7d13f08175f9878b5feb409", + "id": "c0ca1a1626b404ec63d0a50fff867e2e9f9a1edaa744840c39a58c979862e33e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "ANeUUTh74UcDqZhgxEwMe1SX2CP44MkfZX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022072f2156f00911ae5b22ed9bb3701c89ee4ac9ede3f3b4451911e1e284e21f3ec022001918a7e52c65cf762ace735e980547e60abc3d00df59cf16770faaedc05e1a6", + "id": "b119402b26404e9bbba26df0e59cfbf8d50d41d28d428202fb0b293e19c30830", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AdVL1oAWGLDP5vKax1kfA9izMrZGkaStAj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206d1a108b0dbbdb2c78252fc7ad9a91ebd9a525fc5d6a0c13f94d64a1e6f8ffe002203c04d2e0f607398933e30df73e93a40db9ddc41abef1980e954654a6bfdbce12", + "id": "ffce74f355622c5a278ee320d0d9eae789d9a8a81440815bd919179a5a0b2633", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AFpTaoXGqbwVG2HhaWJK5Vky8QMXCGNxAh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206dd92556e443b03c0d88cbb88f562223e8def85b3c4e5ac40cefd0dc958ae157022012b06f7fbd4551ada6d470d25bfcd38cf8cc711c9af94ac56e4415f4f099f528", + "id": "ea0ed0b1bcb706d1e0b05b818a91691221010f7fece97113227de6a2851dac02", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "ATzVyx4Et7ie5KLrjEUm4iULw9Lm77vgkt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f42364a66c031a8584365c68b926931d1ffcd6edfa8a4a859a9e07ff248a507902201731c3bddd83d1f64f8514f5914b0bba1f385c6ca410241a0c313c5b8b614b1b", + "id": "6aec2e00bf4ff94b4374f83df59e85d0b6a5d05fa5520b021d02c74e9b582e26", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AUeaN1Nvksrkynuxe2KFQcDnE6U8dHFPdD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206facc2f5e57cb6ffe15840f80384f3d386125e186234a6825accb2c8fae4dc940220456e79a2aa1c1302dd8d9437d815582b5cf6e2dcf9e7bb18f5036a1a1c77aa80", + "id": "965f90cc474b98cae2de4369455e85d1d10b71d5f6218bf8029eacbd8ac70324", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AbFmTF7BXLzyHYPsbzhH73fcUSzYrLcZeL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022025a0f243b8cedb3282616eac1cd44a0f15f0d908c75cbe5cc9b0be8b46c4e72d02207463d2e7f585be69268fcdb655d3d23c6f3b50c376b2683d7e2f86cc7a5e1aed", + "id": "c1d0407270d31a4e5a569d340e0e71f3e7771dee306d0c229c2c4badced51dee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169700000000, + "fee": 0, + "recipientId": "AXvyLRRSRYxX8HsQHwLgDJNwwxQ6MPyuhz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a149e9edfd55fbd717c71c7150b9546a47072537b1021c7fc4e23703b4322c93022033f23dbec845e796d00102b8570fabea477c75c88b574611bc46e3aa94afdd73", + "id": "d7fccd7b5b4da68f0deaad41f9f132f5891a82295259510d2a4c8f5c1f2fb22e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 169938227241, + "fee": 0, + "recipientId": "AKLvGM1T9FCVstMPjWTC9ytw5UjyZJ9xMT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d361a2adb15f3e5b20238eb382fedf6a502ce30044b065b9169f4bd223d30fb902205e6004454fb61b7562e86ff5dec410db6765e29128e23285a6dd2b34fd437791", + "id": "a59e761db39ffcf16d170da0745cb36b5fa9d13c103592cfc28e1083ceacd886", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 170449652084, + "fee": 0, + "recipientId": "ALwMf5fAPMJhW3iwjxMydBBNr8EgZcTkrr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200d97423751ff5c67b4a4bf29412b80ee84ec48c34a9412addeba4bc0a6278372022010398454bef83c100bfcbcd6920bef23951cc1534611f143c9dfc07a247d60e5", + "id": "d0dc0ac9dcbaea7ddeabf7fc94171d286fccc49fd07668e417b672e96b49f362", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 172000000000, + "fee": 0, + "recipientId": "AbpQwBzBGHQa2NdEpPbg8p1N5j1u9KUMkL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203599ccae223b04921b15fb6dbf2282c0847656785d0fc12874e739520462d8b602207d431393872a4a6c5e9a7014e99c5d5dc6ce17ed2d21745661b36c34096a96c8", + "id": "245622a915732fb38c48597bce7bb1b91a4ae08bfc629ab7108f13f17804a962", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 172479663865, + "fee": 0, + "recipientId": "Adu9KbxhD6etHEMnjkf6n7diLWdjEGzwiw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220702ff0e19a0dcc3fb0e62185a5cf1c094b6b67e3be700252e02ece19e3a53e3502205f45c8b0ab6d45cacc2572799244c5304b832f068e0cee10dbfefc14384e98d8", + "id": "3e2a032b3ea825987a4f374298f036806e3d032d7047ffb77f3c74c62d1bb8e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 172700000000, + "fee": 0, + "recipientId": "AT5ziEWMZjWzBjjKbgFVwa9XsXvgLF7E6x", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203072cc3eb52d630364deff654fedbc54d5a05feaac0e34e0b56acdd843c5d57502201e8f8c94205b700683610b373a3014b04fc0c064ecf7e48115febe1dd26e6c07", + "id": "760939c2cc34d8b911d94e7c5d33e577b4b521695ef1283793e47e9550cad9fb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 174283983946, + "fee": 0, + "recipientId": "AUnkYxRkpMJZ9jKzRQMN9p5VGDmYhk6bZQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bf56948136cd8aabd99228cfe5b668e3c31ee9ba51904413885a0c28de5785b00220529937211a9e16896a45ce426309e06db74d3cf0014316d6472088d843ccb6ab", + "id": "1440feb6aeb77c3ceb720c4cc1aff6c3ca840ad482b13ca6fc44943f26bd41d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 174283983946, + "fee": 0, + "recipientId": "AYP5kYDXLi1c6wrL6AbUX3S2JUEZPVLyug", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220564b80aa83110cfcf4c05976c11cb0bf022aea5539ccfc7285e6bb7024ea92a6022054cd14b6b69a9a7aced496951201a2b39b665282a053686b6095aa8f9af74be9", + "id": "2350844dac54c1d2b56608babd879cd5d1b986f4d0c3b32f587e0d270f9faac5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 174996809637, + "fee": 0, + "recipientId": "AZ8ijDkmLBLemmH1AVPQZz1ucfK7hmpLaG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e3e8f38a1b850c94c87b33bb095221210280b1f9da1d2ec58669063f7b9302c902206b3b20dc5e9fac39ab48f418d4119751c52692aa353361c20839b564ae2d86d0", + "id": "af897d4026d8b58fc97ad3e0a4f555c1ec2d38954a590611780229514b07aac6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 175000000000, + "fee": 0, + "recipientId": "AQyWsH3NpyMMepkGbMJmqBqZehhQoDnzgu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a9dadb4e5df8e5b324a66eca8e5a2abbe608886ceb3058c3b28d2c4eaaff255602205bd59390dd1b2b0019a5d928913d9379cad4adebd2b686d61b91733acc1629e9", + "id": "169028e76183f099c5c6937c4b0f33d085a4dc67b0cfdd5e70ef985a7640cc46", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 175000000000, + "fee": 0, + "recipientId": "ARaVs5XoWqrTdQrfXSSr2PTFWpz8ex6Tpd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3043021f2f2ee4045f6a3a4072212a905fb674ac22021fbe4f627ab06254c9d8996a6302206011608f1e33f52c6e310bb6f5990fb97b40e3621266d832b5ff3336998c75d8", + "id": "b29913407f911e50a89ee33658efe0bdd856c17654f022df7dff3ba66037456a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 175000000000, + "fee": 0, + "recipientId": "AaAYd5qhdUN5Suq6wnJyLFgvVMbz7tAXhe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dbe0cac3b1a67b51c05c4dcf60cdd40898c0d797c9e82ead41c3d253ff5936aa02206a1367f167e42d0272eb4a2aacde2f6392b845324131d83e4060743e981a2982", + "id": "8c1d279a9c3e6fa01cdd41be4de0389ca0628a81e9f54738b3521a38cad15199", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 176418068596, + "fee": 0, + "recipientId": "ALemeBL1Rt6JgL6Z8CJysKDckba33SxDJg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210087e1ac43a27da39206017ddb7b7d65c9104eb079587244283e27156769a6669a022002838c9763c2b4c53926175830c64dde70709773dd9ec5fc82880f8afcc57ac4", + "id": "d1e866be9ca942556ce9e7b7e86fc181cb4b2507a254e213837cc9549af568ea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 178300000000, + "fee": 0, + "recipientId": "AK7bbYXM4Gkyscxp9sVecVn12z3oniuEfm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204c1db106bfbc3cfb05d07cd96f73e94a858a3b48d98e6ecd569c70e6bd381b0d02202db38c2a3f4796f90719ac11132e0bccbf90f3c4df55a423a8102cdc2617dbf6", + "id": "dcfa805aa6fa5e3b48e2977f881e0b5e958fcd6d8f5e954d2ffcc19ec604b548", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 178300000000, + "fee": 0, + "recipientId": "AeBnLajb2S3ggu919PuDkcHKvQUyHogQ8e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e22d2b3fa99c81cc47c253d98cf068c6e6d4392cea6b98046a6f9bf96f08cc2902206b95a356720f6ad9a1fc35a0ad4a20dd52ff5554e917b83a47323564f6a59eea", + "id": "b7f38f5e6054fac0d79bfad045c9d6320b25c5daafc7981cf20e97a86d0c960c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 178500479084, + "fee": 0, + "recipientId": "AQdLeqJwgo6Tq6K9ydgN79wyaFt5gzKFde", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa25b2247f33f524a63be9001bc7bdcc5bec69b58672a2716c818bd6628e2dde02203b4fe9e4e3ee61142c081368f69b7aa585bff6a006d89be99f11c6812f8adefd", + "id": "f31cb0587377be839be6a8c831a618f305cf2aa81524fa8b7db848d1a890ff0a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 178900000000, + "fee": 0, + "recipientId": "AbZg4v2npAwLZfgjLAX3fj6ygRRXiT4uJ7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205069080158b4cdea2222990a79074e3b604f8fa3c9fe3c64a6ef43c28e17235502200ae19cfc468e9a6be38f7d32418a8fdbd9645926cdbc41f69983b814a2370268", + "id": "741fa2cda9837f82593d70443034a0fffcce8513f05a6808230b4dbc222a517d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 180659436535, + "fee": 0, + "recipientId": "Ac9dEA2bwQW99JBREyPDyhEotZ7YQRH6ra", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ca9bd24ddc2d0da487f016603fcbec17be8c090360e57b8e8cfc33609bf72d080220033a502c201ddc296b9ffe5a6c8a439a56ae3b3e7c21907467f22b5c84afec74", + "id": "d777a58efef6dd5addf4b41c7d606d5087d2c04da46cc3de40df6b253179322c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 180700000000, + "fee": 0, + "recipientId": "AN1yL23GyoHGo9sa9k34ivBDsbJ9xNTso4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202c19e9171610eea55add6b8ce88e6dd2df3cebcb2c7408750e2d439462053b9502202a0ac408178498ac59f3efef9fbe11595c6354e192d93384b40016ce4032d73b", + "id": "9baa8ef575418b7c21c114677123bdf2994083fdb026f71f3160e42749a3d896", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 180700000000, + "fee": 0, + "recipientId": "AQvopcUU3ynU28hy9qrgjNgAfNJUXxxh6d", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f192ccea6e18a7c7085772ac499c17747e47086e3e3f8091062a5024f6e1d77402207312fda88382fe4897d0f36660b431290e6cc28d09da713929cffaf9f6a49924", + "id": "bdb82c07d56940e5aa3ed4738521380d13c46dfcc2c41a08a552870b9b2f26c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 181302136380, + "fee": 0, + "recipientId": "APytrs52Z9zEQFbTTsVseTAc2ah2w3Rz6E", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206333d9530c4bb019d2fa8505536bba29d34255860b173bbce1f3c04dd5dfaae502202f71f8001cd13550442493273dfb28a76be302bb4958fa2c00c1da0752c5584b", + "id": "c8e21ed6c8a7b2de81d217a948f55842dd2d9f3ee5c5355e333357d7345a0276", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 181861548464, + "fee": 0, + "recipientId": "AK3N2csioKfWiKcdFiM5Jz8bh6pQurd8B6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205db6e528d94b20b8a25c31994a01ed5b2735bfa1bc5cf2e8a44d44b666ca11b8022056eccb3b38df08cd32a6d10abd316caf075582233a4e3f33cb73dbd143ee2905", + "id": "70ade5cda886880e03b60be04919c3623a511b655e98dca632b59c49c861a285", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 183741686918, + "fee": 0, + "recipientId": "Ad5ZbazVtWzjZJ7PPep5xcd6BrKXujAVRs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022046b4f82debedc188166749d7eac5367ea18d2c56ec136943dfea7081d2a52af402207f6b27161c1b32de2738e9d55225e411d74872db44109b4ebd740abf1f3751d8", + "id": "d7f3e67bcfcf0e9be5368f8c3a0d23f48271465c61a3d345054c5be9aa01249d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 187700000000, + "fee": 0, + "recipientId": "APaeLc9yFj2Kr4c6TzDvSEhtv9yuyYt15r", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201f2ec1b7426bca1a40df431b64b458e41c3da447c9110f04752b17f27e19922802200f450da9a7053e18de7d92f27b373bbeb5e8d83d9ab97e521e7b2b97d70a56bc", + "id": "2d55f798fc91fdbbf8e8cadb68372e3562b7c14b5c8cc9c15e9835c442f37133", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 187862979565, + "fee": 0, + "recipientId": "AeK3nfKry9sXXTKdf1TpGxukzwjEbFsYzB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049d80b6b0f5e5592a925a105fa49d3824cf6ade3e1f82c6e08458fc6d1b4e1ea0220084e937408e7f66e690d2170723464652d7fc5e49b5a50d46edd4daeec028806", + "id": "90e496cfb4d6231cf2e764ba08e6d236086ee2b0126d754e96e1f046990b4741", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 189637269418, + "fee": 0, + "recipientId": "AURDY45qCCokzgcLMHFgMQedxx3fAAvrmS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022070499c504fec7c112ea30ad8064083d50c86fc7bab9c35b906d3f5dc574a63c50220295bfade3e5b109dbd370a713f8166eede8aa42754b532dcfebb2e1208ee064f", + "id": "a344917af836c95bd810bbdb119bfa4505d53e314f7d8064b947a95765acc668", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 190000000000, + "fee": 0, + "recipientId": "AbQgQntyhfSarnTh8cFN7o5voAyDvbyZFa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bfd6cec5bf192f270babc3b0ba8435d592bdd369e3624c9c6c3b1a01b5f2b6820220191aa370fb41dc7e0f1c4ac848290419702e467f73dba7eb5e63f31028585fbe", + "id": "5174fb0eb29a8303387005b4518977b8386e811e228754863418ea5dae98953a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 191010602651, + "fee": 0, + "recipientId": "AQXAihgHkyTH2GaA5q7txsuGN8Wm1fRCsh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f63d5da7c7ccdabaeee4c6d4d7a6a280feb33e4c0309291d9d32e689be6d83b6022013a07a743b78fb4a3fdc0c405279a72a1514baebb9dd166b9364c46eb934c3f5", + "id": "0b931be5bc7475ba3080a14a864bbde1f78e8dc0e9097c7d6541ac32990f55fc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 192343079840, + "fee": 0, + "recipientId": "AJVL5Lh8fjMSrzDMTXxsBt8bjjEfMp6eFb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bf093f56441607b038305a26408c77428d3f41f0be77f1ad8a990b8837902eb702203c3badb9b7c845cd4c84261403de3ea42e644f11cc5536878828ff3536e91c0c", + "id": "e47fbbeb7f8b522d4a60ae7731df51df96a687adbea76a3d216c8671106e9c81", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 194167513244, + "fee": 0, + "recipientId": "AR8J56ZfnBCzB5Lvx5VyMbjKnfvN1ue8KT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa1e878c673805908d682901f13bf1f0ce133822ba183e9c00425bcc1d43e8bd022043c553f199195ec42da2c5ffead7abcf0aa9046dcb8a5009f0b04d64f3611c8a", + "id": "2da708e273f79eae2ccfc37fb5ce48fa46f664f90715e7109b4b03d4f336820a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 195295588810, + "fee": 0, + "recipientId": "ARRpaRQ3t4LQdiJSN2ZMsuShCn4RqV6uq3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202b4e543a3ac721ced2a4cde390f1b7e31d5efdc3d036d9e6a525c5a093ca7768022030c034a23ef72c1f335fb32fcc0e068ad14ddb0973302f8895f46060acac864c", + "id": "1cafd9c19b66bf9168a643075f84b1a78d80c909e2d6fcd13ebe51335ce45d83", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 196578368156, + "fee": 0, + "recipientId": "AXnet9aeWZ6SUKqHqHaZfCZNuTvQcdS9YR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fde0862fac2154078b283cf6d5d95e9913b5b19ba708cc17f6287832216e54e502201f71dce1c39e215b9d5a3c971ff5a6e8ebbfa6fb79a7135ceddd490216fed391", + "id": "4a3e46a8378334a550bd54172491a19a8832ff1c4e909b68f54a9acd22fa9e0f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 197344240463, + "fee": 0, + "recipientId": "AV9rVNEKtxumKAuw7ZRui27Xqm5oDQTcGu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fdadaef2d3b1c8eab520b7a5738db35eb35c224e2fdd0d215911454d74b09da302207d468b503cea3e7106c55f2b4e3081036c6ee71b0d22f3c7ef7c6ace38c2c1c5", + "id": "7a263c4f547c159bfeea9542f083060435453a72ef8251375930a6ce50788e35", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 199700000000, + "fee": 0, + "recipientId": "AGJDGWF7ZdcFg3py7fg5DXVLv7u835YEu4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200d2557315c3ed28f6e9006adff24c8c7653482ae2c5a46536dffcd2de17187c7022021bb54089176340c71544b27388cd2836274b5fa4578af87a8bcebf8509160de", + "id": "73ee85253599b0a5c6795e002a0369b02caafe3d471e3ae50bf06257d9242ea6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 200000000000, + "fee": 0, + "recipientId": "AVQp4XzGX7xezNvAs7zK9THH52sHQ5cVMF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f4497eb3cf8dab8dcc034e86323ed133378045e5be031e8a0aa7e146df51185102205b4e2fd28d73d1c6b0363163f3d43f455e909c5d1e91ab351f1c54a0c1180c56", + "id": "ab4b8af4e3bba9782ddd6e1cf423572c08018b97c1edb458ead90e71d84196ef", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 201600000000, + "fee": 0, + "recipientId": "Aa5mSdp95Q3UYCfq14hyqoFAb17nbvwtce", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a859f753c037cf28dd0adf78f00b25edfb1824a361dacaa6e638c1a026072f4002202f3fc4d70a9a783cb18cdf15cbcb44152c33c4b1bd3258a8d40b080fb677b93c", + "id": "f8f09ffcd58da0f41474741a12846a993315794679c3689f683e2caef0222616", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 202607842389, + "fee": 0, + "recipientId": "AZ1X4tRmPioTYsF7M9G6LptiwUC3FiTcSR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d687ac1127083d06605b4f903c9052cc4d0d4b32a6721e2bf5802d7363dba00e022072fa83d6653c638db9f2f011a61c7277e3feda10551d6455be11f7ea8282e625", + "id": "9fbf5160fb1d3e8c0b218e2332a1fd4ec0bac96c346fafcf8430cad2aa7dfbea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 202700000000, + "fee": 0, + "recipientId": "Ab9yCa4xAXYPMhDVQn4pq9sPaNPf87oPn6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203b4ad209e97f90c9042d145d904fb43b7415dee167bc23704e29d1b1e6f57a7602207cd6aa93c337139424cc160791262310216b131fe347e6261f29c30fc470991a", + "id": "f553a38a56e7591467c16af7ad8b3b33661f70cc6e7ef38b7193ca30d7433e80", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 203859765516, + "fee": 0, + "recipientId": "AdAVt1aKEhoTSpoThdzoBc551Pd8TvcnTK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ee9a0e9d21b09cf8456a7af712aff5576bbb7487afec2277c6ce857ea32044202202c7c723a32c2eb349c27cc9b53aba8c41eee9c668e606e8d1f39a4f4c51725cc", + "id": "fc7712497be31254a03197aa586d121307aee0db828ea22ae5f64efafd4a7d2d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 205000000000, + "fee": 0, + "recipientId": "ARpbHrGteAtmcuq4ogVhDHGYa5bNX43vuc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be97fc5e92f8dbe342499e2dfeb861e2a3034d90db0ed46263c492c02821842602203c072ccfcfc88cf36e9e2bb05f4bb6bad6055c6b5f409d46a686e2594d92b2f7", + "id": "3fa38602a65a4cbd8289112ad9d2dcc38682688422da1399192e9becb3713419", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 206040822146, + "fee": 0, + "recipientId": "ANiKFTez41cCVTDCeQMGDah3CZcQE3QtqM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b2d3c8ff809e06ce7f18e8578c098a05f17ef5725f5493455fbdafb0a18c64d50220278ebca469d1ec78d436a6876bf69caf7c43b61de58bf725768e20e5467b214f", + "id": "fb110b753cecae568ea684ea89219ecc435e4c0b36c4732f39429b96c4324d42", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 210000000000, + "fee": 0, + "recipientId": "AMEiCa1gAAMQkkNBLq6U8XU6eojD9NDLhK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100858d68292d61d3ee9765bd761c669b387362768926892d13e20de085dab00b34022063217ca008673f5d910d52e76172ae3d5cd89c5397722555fa02ef904046b3f0", + "id": "5e160385a766397e2e8073acf3c6e61ebc1e5531c051511e2305e1f9a99c9074", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 210742589495, + "fee": 0, + "recipientId": "ASa6Ptm7PRBFEabEA3xSAaTzkXAATsgWYH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201f7e5d2a2b1eb413415147c5b0531be4acc9657a258b0642674a9222c9e111aa02200a6b12557e8b15c93363cbd2f16c5da5b347808b81ff0787b509a5df402bdcb2", + "id": "7e416a3161f32a1c8fc7a50ddaee0005af24af5d71634ed412a850b272773300", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 213596124973, + "fee": 0, + "recipientId": "AW2WdVA4pT25G6Mg4Y8v4zVzwMqf4aSzVY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f483231b8fa37f29b442b23f5cde12bd0a3c5aa9685b0474c9d8b49e29bba09202202909c7cfd49d450ac8230e0830b501c30d4bde12daf911c0123c2986a6cd940b", + "id": "c40a53464e01b2d155f20f7bee18aeff16912e6d0bf62ce7af98242c7d17129c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 214366548708, + "fee": 0, + "recipientId": "AY4zDyuA2GRwYA19o9MfJWHkYX5y284Qef", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220433e6ed95528685139036657dda5bd0d00835bf887ff48b50f2ab8fee55c862f0220454511b151d642bf325c1b76e4f87c10e2edcd6888db2ae93b80dc002c64fc26", + "id": "d11284b0b9371d544a9164d028d01780bd65f8c855c147788bf9863670756898", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 218074902818, + "fee": 0, + "recipientId": "AcGLR6W5eVw7Z4nUNxAKAsPbff7W3g27LL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022059ae53f97ead4bcffcd1918d7441d2af25e3e82b8177543cf2ed491506ea4452022040e8491b7acda16b0cbe26376c7c363c31e007fe6d530d0a7b59a2e0fa8bd2c2", + "id": "492487dd0ac90a5e56db964bf01399a2e68a6ae90ae7df983d28bb7a016fd3ce", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 218233858158, + "fee": 0, + "recipientId": "Ady9TkKQPteVBFnEgsB1jReC31ChZPQTrM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022044026bad7aac5170ffb87b2fab2d9e042093d05707d14e8cecb33de925fd04b70220181f5fdf565a57905911e0de52b99de3fb686768d269bd825e660a7136e88baf", + "id": "fe4a120e7ab4c9029ce22ff565bb6a3b1c94b271d356fdfb5ebff146b521e983", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 220000000000, + "fee": 0, + "recipientId": "Ac4TAJqrvBq4ts5VZBhHdH1NG9t12T5iG3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207afeefd18cfef28ca2d575ce09186d5e73dc9b26164ce2f236b3191c0f96279202202716438a52ea4a50fc87bcf18b2053506f19cbc8063c6115d878c5f6392effbe", + "id": "97b34b6492aacfd46d6cc9abc808fe0a24753ddb97bbf6f6fecb926097faf36b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 220522585745, + "fee": 0, + "recipientId": "AVQxWb8vskpGUtBHGyWtZSdM9gGK4oWGAr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022029c9ca7067178a57095b53e99b9ef5bf510797b677eff8270dd269c44d52af190220773c8cd4647f9bd75fa8d67164990b3dd5c4e872564438d5125be7c08224858c", + "id": "8bf8a19bc12cd56778d9ceed54b0f85bf35d93074acfc79360c845b3afc49701", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 223213787338, + "fee": 0, + "recipientId": "AdYX3f5TQrDG1E59vtT67yfUkePMA4kdLs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ca26254fdba9aff2aac672c8f81d6501c7ed05f2310408211593616c79dd1d7f02205601d25d8967f49a12d32334a65c2a5135bffa6b00ae0b8409c20012db15c977", + "id": "01729bd3f0c4cf81b3c5ee92c11104e3ba3bdaf5ca1cff75468b1909b0702c0d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 223400000000, + "fee": 0, + "recipientId": "AW1Hn4dANHJAbEYZeEjY72kHziwS82vgnU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206554e7abc84d48a0446916b7dedfe2ca80b81ee68fac6a1306e20f21fff13277022069ce2552631634c4ea0d8715b01b78b2a6a5ae0ae27adedee6fa6e943845a33d", + "id": "489622fac2962052e7aebeee1e4a4b2474432b88164ee70863fc4597ccb0f27e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 224600000000, + "fee": 0, + "recipientId": "ASHHaaJMGmaBdgYzLBnCf3QRzmBfiCLwCZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f52a3a95b11ff46babb1a698a31a2c15543cd1e4edc0222b7f6593bf9e1d4a8402207aca0fc68e3096119766e390f2d20d2e48d308520f2700a4a1a8400fc9472407", + "id": "83ac0a39f01b70bd420881285a9cc7b4a99fe52f1329da58561a10bb7f933c4e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 225135109888, + "fee": 0, + "recipientId": "AShwN32PRrdnpqBNruVJBba54zvBxQT1fZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205855eb9048241c2c794ceb57f89f4cfe6c6e7db7b75819eb2d6b613ddf830f3102205e55a04f4d782a61e969ad9cfcb8926990001650282849122773fcc25efd49a8", + "id": "83093ca92b08910d1701846610d091403f0caa583c539c555c676b6a31fc62e0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 225186625152, + "fee": 0, + "recipientId": "AU8vV51b4PJZb9fWwHgVUrgz4Qq9ybsM6o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202ceb7829288662087ca7db79218ddb0d576cbcd112fc065bc5121d2726f7195c02202deb812e71c09a4e21095b881ba24d55a7cc135cc9dd739e66b22149d669269b", + "id": "2cac15432180f5f1a0459c96a42fa93dad5626ac6f3abba1b79116c9c785734e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 225944551562, + "fee": 0, + "recipientId": "AMw99hBqtW5JzU69NFnqdHmP7hezSHEJwd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202ecfd14378b25946c8f865df0a638300d26e6f82c4c557e47acc3ae5fdee98590220047e32bd8b1cefe6ac337acee1671d0c83b6c4966bab9de30615c36f5b78afdb", + "id": "c62a1fc478ea0e2bf3e784ed5a79e7073e62911ee9d078dbbbe888fbdb69f4ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 226088941627, + "fee": 0, + "recipientId": "AYijkoBCzzshgn3N89mw6tVHfFVDEoHhTj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008a560bec818d7a518488d631663ef9db1051402d2e9f24b7e45ed008de328adb0220783be0fe7394775314a1ac063e286a47d8f0d5a03bedef6fba927a08f16991ee", + "id": "b5ee7b65892cbd18ce165ec3039e9f103163b4775c19774223c7a039706c7be1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 226432949161, + "fee": 0, + "recipientId": "AQg79cKKYsHB27WksW9k5Zj4gfCgd6y334", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201b6566d5b7fef4e562a28df76b1cf240b1ab6d8e404fceeb6a73e8eeac2c410e02206fc6d1f95cc2f73268bc8c8a16c018f1e3d8841b5d1689a0ab9a994eca88c35c", + "id": "7f3c2b6326ae39f356f638e7ae2a62c6743f9a0ef5d96bf5579d4ab633945d3e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 226920728780, + "fee": 0, + "recipientId": "ASvcE2iGk6NjtGzsCdhtyEmYTZq8vzA4ro", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d491f759be71c566db0c613b002abe362d5773c394c4c334b2be2c594339cf1202207af821eab08a548cf1ac494066c9f5c44f1e012a2f802388cda6dd1d3e929136", + "id": "d5f945780826ed328ed99545019e5a71b6758320802accc0e27063a882bc410a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 227205088343, + "fee": 0, + "recipientId": "AHAD2pRZH6sqxHbhGYjAWWBbrUryGgLXNb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fda316ddaeb730b7d40cef2c6b67b1ab4309013e1fc2dfad9c7338cdbddd99d8022068dbecf8adbe1f3772ec3ada57f712d621c6dbc05586a7d07657f515ceaf8301", + "id": "c90e06437de6b21fefbc7ad2740019ed8a21d3d2f8cf4dec41c9ca50fecf9878", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 227326935581, + "fee": 0, + "recipientId": "AMzGgQHh5MHzFzpMV2RnP2ZEaDxh648gYr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022100df2e8ea17c584cd5c2b98d8f482073b0283a0e08dfff592d802b136397f482ae021f3b7a55fa8f7e6cf81dc6366730081f9350c929a64c00d57b278e8e927a6639", + "id": "4ff0f93c6049358eabbc1fc17d2085b198fb2f853898c31e4e86897921bf0739", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 227395512019, + "fee": 0, + "recipientId": "AadNd7r4Sxm8nRqRXauh8e93JieVxHr6SE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cb986908cda1d4cde6fc3d3d1d677cd884b44cae1f8baf1ff29c60ce9ec230f302206c17455c3a4a0cbac75aaf7827e3a2b6e04904114b580e6b2e34e1830824afe0", + "id": "8198e7da0ccb9bdddc19850d2b12b7f142e5bae867e699865c6e07f380249de3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 228227511241, + "fee": 0, + "recipientId": "ASyPsMFeWujywouFojtZHAmk8MhB3ZYMyr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008f58884d3e390ffce7e7dd713c9f3c0522a03f995ab5e78310ad81e490dcb80c02206ef0c5569957702d9260aa8b1ddfe7460737fbae69f7685cad4615e497b027e0", + "id": "76cb4cb4da799f5652caa816f58da0851a83281f7412938263b9d93535026bff", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 229694826743, + "fee": 0, + "recipientId": "AQejgdtXnkygMaN4iYGsceURNvrnGLN96D", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022043a8989908833bb50b5a74aa77ba1172f4126ba24fcfbb7502a8b3adbf7f9bf602205227d5352082b42dc8ff3a6971d5c04cae58f7c84f44b9804a42d7d424e076fd", + "id": "5f4e06198e65476fd625fe07b5876e9e9fc27cec66fe4e48faef7c89bd79fd57", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 231352203905, + "fee": 0, + "recipientId": "ALr1QRnTAnsQXCKVoc5nHjA78uVSDNGArL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202b798a36b794fa8ec3a75ad0edea4d6376cd39df6c0d263025c4b09b606acad80220371266d010a2cfd2a22e5e46322647528cca9eec61f6e17aedbb64ee19854b3a", + "id": "cefa0126ac7ae644b539f9004f2cd71b5e4520af416d93e4541c5ca8a84f75ce", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 232283790318, + "fee": 0, + "recipientId": "AeZPqVdexqu7VGES4UMMGdnySdKTRapfPi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022015972edfca96fef8811354524dad92eb10b6f891d80fd0daed0b692e60f65b2b022052ca9bcd9a6963826ca5ed308c0268c94a0136d236245c522859091d405d9599", + "id": "e36e0129a20398b9ec6d8190e2ef349e4e3ae61e0e1a09085f153523f57bc3a8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 234568404307, + "fee": 0, + "recipientId": "ATAva6NSSHp4tfH7N9toWikSqd6YYKBBLk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c2dd0fc577d306894f4464c70fe39c83e85c80f31003102a5ed878f6095ec21702202c64b5a075465f7c3014c9255ce0cbdf593b2158d6cdbd8e91f1b44ce9e3803d", + "id": "7d7fe67913bdd7317ac7b930a9cb90e7649f05125ebdf1e8691e4ed32a482af9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 235000000000, + "fee": 0, + "recipientId": "ANMT9kqisqY2KKLx3VMokjLCynqMxQGUaS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a8e13b798bd778badc34d9280e1d2ab9414c813f938384c0a86550e826cd8b8402205950252bc1efb41ff5b354dbff2ca5a8ebd5d4d815d9b5b3c39f2c787b881a48", + "id": "14b33a1754dd1dd5f243301dd176910255ab19ddaab5f6655f18790b9d2d8331", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 235931714746, + "fee": 0, + "recipientId": "ANpWLmDRAqDejcZ7GCdcKChmRGoMZCNJS6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b99bd138ec399c08edd91e0219e9bd049ab7a80e32c813501d80442f436d2d19022056341b7bf689ddaf371c88ade20a7952815bdd784dc2ae64d30b2ad311e79e39", + "id": "6953ea52c0b42bd950b6a53eb1e0b4e6af5c13d0836a30288726a0cbbeb34f56", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 235942815916, + "fee": 0, + "recipientId": "AcqrcF2KJgjenxBxXoK5vtWo5ELqwfkbhX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210094dd527f0c312b8ec349fa10aad1c7dfea7cd7dcc9c4c9196fc5a571c05d85cd0220595c9587b8bdefd34389e98cd78ef8b4bd9f1912700f0d3d713afbff6f5280f0", + "id": "6572a91980977673ed2d3c74edea33493e87d2855e060e3ee17a015e35331159", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 237605753191, + "fee": 0, + "recipientId": "APNyAxuJpws9bQ9agQN9J1hKE2kMoBpv1c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ec9cc4a71bdfe43a491208b81c164c1a7dba990eaa9232235a6f26eac2bac7b002202ace026d4bceaab43df36c3a7dc38d859e00337e0a278550fc11d3f7ec2455d9", + "id": "45c45d469ad237b9eb967bf033998866e4671c955498a39bcc4ff652d7366e69", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 237874783089, + "fee": 0, + "recipientId": "AUn658PgseDk5iLkcAHwEMAYy7SLz7aByk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022010c2a04ea0859438a7c8baec0c633a993afb4f2fa74b09fd3cdc36bd2a74cc36022074a2b5210fa27262bb40d9a5d6eaafcdd7791bb62bc678c3dec55d8a0e196b2d", + "id": "5809e7da60df4b7f9c8f359c859c43fae0d319b9ba0ba7e8dc2228d96cc8f651", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 238708836916, + "fee": 0, + "recipientId": "AS62XXxC49oGLwGiaFyMXCxCamuATfHSHo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206e893e194882b8be5aa96582595ca69b55ea0ea9a7799789d0e3a79d003ea77c022034edefdf0793575f1f2139b925e7cd1e87c831ef0e2a11f923440ccbb62aec8e", + "id": "745488ac65b21596e11eb41d380bd595a4270fb691819b5c0437e3b9b0f5f52d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 239265443070, + "fee": 0, + "recipientId": "AUdHvFwv2fF2DBpYqbnh8fakj2Wc1HFDW9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203fadb40ba73bed4b275b2dcf2d205b496404c65e896468fdb66946792fad929902201ce6b87ca0ca7013dc22177e6915c06fddabeaba1f818098b63cc0918c33c9cc", + "id": "7d94f1bdecf28119468bad192f7ed5ef12f67800246f071a312bd23e8d8f2d9e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 240950563055, + "fee": 0, + "recipientId": "ALE7xUp2tVw4XqoA9gK1d2UADbZ5XbNPv4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f2d3dbadcc28cb3aaa43b29caa35493ed229e7feb032985420d6cdae580e8355022005ad1413bc7dde581e08d60a2faa4faabcb8cb1d4e822cbe23f47922e737d7de", + "id": "8b057bffb3bfa183445d801a220a07a2c66e7e4e1aadc440265615fec4336442", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 245331115580, + "fee": 0, + "recipientId": "ALRhEXxjx8RxjBMbFJMZfRPVKYgTeABue1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c10b2b4f807ffc5f82391479dfbe6d1e455e16f6adb0c150bc92200c8fcbd9a30220033b3a351010143a22272ca5b48d73180b209b9d9f2367fff8ccd5dd899e642c", + "id": "09dd0da5b96f36e27e7d67779d3cdf266ccc2efdbe5c9ab88c20b5082942d4b7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 245368130406, + "fee": 0, + "recipientId": "AR8zWTBs1pw55kwS3Wq58uvhhwPjtTYNyr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220076a552745173a93750265a34cb80633d8d613dd72565420ccabe9d56f27c44002200509f4e62d51dd15d11fada0edf71e02af8397fe5e5bad521bf28bf9acf80655", + "id": "39c6bf781edd4da283148bb6f34d1152cee2fc98cec7464932a82bf380df151c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 250593911397, + "fee": 0, + "recipientId": "AGnxrUgmVmqqRNyo8nVVnFuSwiABeiMjXp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a01fa1cd2c20a5012c88ed1a0756a1f643a62c78fcfc4f69a7c983e76932ebd202205ecc72be31c8732feee7ed2f79ada18b52b9d773f26fda89825d5364ffb0653a", + "id": "abe6f922e8ab5509fb29fbe11356a182a5b7feb4e91d0ffabe3969708b5d221c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 253760909613, + "fee": 0, + "recipientId": "AchFpiHbXRyCxc1ZY6z638QtmJnDi7hozK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008e32159083784acaf47a4a5bc2fae2f25b42f27ccfd4d1f0bb2e6263f902ae8b0220068d23c7a19fcf30456fe8117678b6f16d14765256e034764f7be575c53579a3", + "id": "99b8b895b70466c7984edfdf0f05dc35ad4b52b3a620bed624cb11fd68a588c0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 254454616560, + "fee": 0, + "recipientId": "AXt9rWvbGupykENfpxW88mnD9zdxTDvWXQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022071ecea689811557ce917a64653771c2660ff29f9a5a903405a74f5f7e6884a0c022034acca7a4460d555b186d371be5c90de69ee6cd2e562b874fa37323dc9b7479a", + "id": "189b47b1a4ef03306276ad9e7afa6de0a26711fbdfb2bcb6527f679ca1afc192", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 254606167851, + "fee": 0, + "recipientId": "AaPyrCLTj12R3oc3iW2mWAwFBy6iu6VbwB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b4927d09c6bbbc5e300e8e12107822350358939d92916affee3f5bd8147bf845022052635047282fc85c9fb4f115bd03f9fdfe42636668f1da00073827ee72835b4a", + "id": "bb4f8ad248ea6a382f7b492587bd4ac14bd1f06554261b9e2d187aac784930a6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 254687439215, + "fee": 0, + "recipientId": "ASddgAZdbm4jQAWaT1q6Y2zQWRqEkJFEX9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e7ba05a49b3b569d7f55ba5dc0309db7d775c5f1b5ad068f426cecf7e6973bfd022073aa72abef5c92119acbd30f5434b066d2e9c3f68d0a8d44f61c6ac60d76d9a3", + "id": "3aed748d0979d80a807df6e530f480a05485d147925f971b26e7c7826e854fe0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 257637193659, + "fee": 0, + "recipientId": "ASsSVAcmoUukQd71zZdmXu6nXzmG3gsKVk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100823c1f1dd6b34885aaa0e0ad4fb4ce4362343dd5a328bbf0d670261a6b90d4a802205a54410b7ec114c84c47ca4de5390e78411fb580f966a790266ef570425ee935", + "id": "00dc859d0b95f33615098b9026cecae7a86f1aa61dc2402725bf41ed589fb563", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 257700000000, + "fee": 0, + "recipientId": "AdeWwxiobp9H6WiabttYQp5H3kkNrHXQ4h", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022052cfe09b5968829e0742892f8bf957072e13528e293879aaecd0c0c5c37273b5022021eb5fb8c940be0b569c6c90921ce4a5e6e20df8662d50d94f05b03429317e66", + "id": "b55d6a965744191be648d34aed3eafea6346102c9c078ac7e6ad16b3aa9528da", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 258336704622, + "fee": 0, + "recipientId": "AZeA4DJAW5qBbSdGavdC1CikwiW5usc19G", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3043021f791bd82b7e0414daa7a8a9567d935795438cf7e23c4ce95ec0c634ddf8bea102205eda1bf6bbb5f7f374460fefca7e8001e4a37d1d2615e776c347a3094ecb88a7", + "id": "5ab82d277db94a82df64931ca7c39d4708fe488152263dd47abc95f889fea4c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 263400000000, + "fee": 0, + "recipientId": "AQ1qa5TMsSF4RWU6fBZCQCFxcEpzmtkGEM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204fea12280fe69879e6dcd4eb86c2590293a9647627db7709ac6848690e3d9c3602201fd4810e4209edae71a9dcbd3b58daa95ee072bff610b035a8c642770d70de4b", + "id": "0985c36709c6452d9520cc9a2742c2388949c62c811f51add27bb59e0092f144", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 263517383725, + "fee": 0, + "recipientId": "AVLiBtEcZGEyUzME7mkjwn9whygssd6iio", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210083285814a6b94481dfac21296003cc4f343d4d7c8bc7a7a0e5345d4aa48eac1902207915c6bedd8a3a10812da78de0368acae101cbe093685d18fdcddc98b76053d7", + "id": "af50dd79f9d422fcbd301e204ba6a1e27f7b52bad2ef1183ff20e434049c2ea3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 264706758253, + "fee": 0, + "recipientId": "ANTb8pDUmSQHBQK47P3JSU5pTWYDnAuGGW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204175f6fc6e5b7e7b453dc121a8af4df6dfc4c55028d11c3ddf71b9d81f4e7e0402207723e4e6a7ca838f645fbab78af16c493d384a06e70b32909fc4cf915817d5e3", + "id": "0ed931759cb9de29db6d266868b504864389c2d1cf3a81f5d4a5efa54ae82a7a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 266600000000, + "fee": 0, + "recipientId": "AYpgKvx4aBst2hD5gaRYv21FtiELyaehMa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210095cfda172969672c538a02e9befc1343f837d4fb1f3bb9570312f03e2dbfafd602202297f4fd1f28d1c63614a75d47c4cbf3f60ec5f77eadf12769d4de7a39290277", + "id": "2ad61362f35c9510c43e14c3968ae3ab8a1f0b1ebe69dbba2b0bdb094805d55a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 266600000000, + "fee": 0, + "recipientId": "AFsy81B8AvNKkxpneNm4WaPXsXezYLRVwV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220632892e9de2b0f3e915e2af3a869e834b065a33d239888e4d9cc86068e9a8d9202201cf798edd4f1785b50425d3cf0e85b2cf41117479e4d0d354a00908ac809d711", + "id": "2e7d5d559b6bac5a49af20ad2e745daff3651fcd782d3a681e1935c2fec8793a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 267156888910, + "fee": 0, + "recipientId": "AWxBnbH6FrttJsempdhhoNqzzdynnue2Kr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204ba5295df2c4e3d4b1c098c7ecdedc98ec316f115399ecca37ac08606010a4bc022057819841af1ce0f3c979c9b3ef0f6de3493e21b10040dab878cf753849c25b0a", + "id": "0caf79acb11be4d8712ca28485afcee25efc113aea6eee515d490a9235608e28", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 268215057163, + "fee": 0, + "recipientId": "AKU81Sra3nx1y9FcCJYxuuHQjZ81rjHavg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022020e417bdf510dbae5780ddc943a9b2fbdc1a79d2225a3e491a223f3e6a8fda2f022021b6e95132010bfd534af5d9404295e9ab497f6f00798ee15d7a6b5ef49257b1", + "id": "fc18ebde76c5d15ef00d628b4e987f77c4ec85d4fe24dfb56d7c3a20fd8abe27", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 269700000000, + "fee": 0, + "recipientId": "AXHDNRmJ36baawXLcS8ipDHgrunCeRN471", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220580727096e40273d96f9f2c52109eb4810c2cc10797bc59228f1abb8fb42af22022062043d9bef15d4e1239a42735bc3cccf08a15c6b1e51058d3c0ea590418e269e", + "id": "fe60d828232ea75e7df9a6008cb7b1b3ad2e5d8e72f638350f6f40420b36bec5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 271217304301, + "fee": 0, + "recipientId": "AegyGSE9AKWNiYeiBPA7p7f5MDBbsQobNw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220587cb825e6b40631e4b8262b05b170e219c6a42c723ba1fed4f6cc6b472474120220784a7ff6e72b544cd8807dfdabb9f00f3782322bef851bba6995ff4b2b826710", + "id": "fb0dfdcec943df0624049563fc6c04fdbdfe413bbeef620e7a27ac06af71f0bf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 273312359613, + "fee": 0, + "recipientId": "AdG4rV1nutJ2VmGfKDvH1usesnFgxdokd1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220367368621c5adcf56ee4cda123934cc5fc9ac54332b0bcaeaa0b413a0d65a71402201971cf564a643d39c915a8328da5d3cfa36901b373ad69b8a72a21345946948a", + "id": "5a71a47bab2f7cab9e01e4694cfbfb67b77c411d4488555101f187dc88ca3372", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 275823348504, + "fee": 0, + "recipientId": "AQHWx36dxxzBjq7t4HD7sCMtBMBSjCBceS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022020167d72b49f9c4b420066171449eedbd8313fcd5384f06881d19c1648c1b4d902205d0d2bbf805276426751a1a45daf52516d058a1776f43963c583cece69fe20c6", + "id": "a5b96fda073ea900e23c4c85cee5b79f0ebead350d4353d1697874abeb377ec4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 275919189541, + "fee": 0, + "recipientId": "AdmQcPaXYhe7cJcD9C1Ghk6BKbfS9VqFQq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d577572afc4ed0362d6ec5c4a5c9b31abba6d2267c51a69c7d5f7349f8760b902203e73555f377d34d71c77786defe682711866f02f4be29787552bc25e1a277082", + "id": "99e450025bb676e306e237e05dde8a6f2d49913794ed927d9699c78a5e363409", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 279247094098, + "fee": 0, + "recipientId": "AT5ANRumUi7WBhCSVs35yfkbUg186x96fe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100967b5bfbba242e8efe50f90f6880afe77a1e065e471a7885060af61baed6736d02202dfc5b008abb3466e78cc24315b51af9058ed24e2f77eb6e57c80b3a85ce250e", + "id": "ca2358076d0c35255bc017cc7a85023f41551867e9ef28287ff6d5da2d50833e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 280000000000, + "fee": 0, + "recipientId": "AQN9znPRTre9TCbGYmriMAc8Z2m8H8xrL1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022032247fec35acb98d299336331d44cd915e19b15857c36ed9f5bbe6dca7c2d9ea0220350197b5c1a25d9e0dad1d00d223596aff07e8a0da480661a19e27342a26dee3", + "id": "fa5d6719a86996e3661842604a1448e78e5b9d6a91e6c137a4ca6af765b9ca19", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 280803102800, + "fee": 0, + "recipientId": "ANfCguzStf32kWL6HueDisbWajTw8acRWs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022014dc24aa9c13fa450a824e24a25ffb06005807c86b04aad84c4fd502e257647f02206479aaebb822b9c1e0ebc8d28acb759c7e5b09a3eaaadd48409dcca0c7045c0c", + "id": "08fc563e818b09004b5ce2e4c7c5eed2890146f62deebcafec6d5a2e4ad7f0bd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 280988036090, + "fee": 0, + "recipientId": "AQypxoNmusR1UsP1u69SFbjb3R9YH7mszw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d63d05d551bd6c9610dad7ecec2332de5675540eaa9c7989a00b4622d238ded3022071a021dbd0a3e69cf9cdc1ac0d949f803cb5137e08ddf476fe606a774f6f1127", + "id": "52cb8cbb5a802ca5d4f20438a0cf60ce155421404f9b8184952571a2382745c4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 281336687456, + "fee": 0, + "recipientId": "AZx5ahPCDgGR7tdGeZUTv6oV4vfmKvPeJs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201f040daf2812c5bca3ca29ea83bf0d24212707eec898547a76d6f00a114d1c330220179b38aa34cde2de80c519e0ac5beb23be3e14389d5e12b530fcc8769779f9ea", + "id": "b3e6422692e6b305050f7dcc3d23c3ea9efead66f14327c7ed41f02e1471039f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 289471840817, + "fee": 0, + "recipientId": "ALApc6GgN5sAA5MdRfws9MS3PxcP14Vk3i", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220284ba45d70676f0e34c9f8401d06c0d5807a6e84d021ebb52266730491ac5c0d022014d14816d1e86ca5568ea8c78e54def350ab3817f4fefe4404362042cdd12ef6", + "id": "899123718c97ccfc2af2a5bc4b50d97012d46c2d1403c60b51d54ae30e3e7da0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 290500000000, + "fee": 0, + "recipientId": "AVd1HMGBkguKeBduZoN7JD25mVv91G3diR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220623a9ce997e94680b7fda4412921890ddb91de2dc833c76331026df41d4bc93f0220159e4f175d888472f3eb4318344b7fbde4140cac5b43dade415445a5c848c24e", + "id": "b2fd759ed4885ab57848dcb86ccc2026f47bae0cac4fe119183550435dc21d90", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 293042430719, + "fee": 0, + "recipientId": "ATdJXNdCCBdPaD9pTzZE1uHubNd4EixaoA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220333ccf40d7686d7ede92e901701f2e4fb65206b40bf1f71abc85815e6eecce1e02204c30a9f447bb751fa78c08ac0eea0dd9bf22950cef4a8ae85ae101970a963362", + "id": "69d303c8dbbec71c451201ea88e64455da3260bcf9eb866a784b13128ae3e6d6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 294030114327, + "fee": 0, + "recipientId": "AaobgufVyjG5QYYUCHfdD62yqfKnheoiK7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207badea2403c148d9573e4b8551e91ff258250cc339647cfc7c09492c64d57602022058a84d0ca29a3e2a9d469bb5e31460c3fc55a968a296337dda894b1f324a3406", + "id": "e1fc93b662aa17857ec8282b16af5accb50862272ac7e804c9a599072e13a7b4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 297800000000, + "fee": 0, + "recipientId": "AavHyFGe2JQE9ZwkWVHZeZF4otbj8q8aKp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210092332a9a11c56d75f25b407f22b3b910d469d888b30ddeb12ab843b37f5eccb2022008048a684ee0765e703fff4452d81e85eee5060c7730ce7959e66ca4c442414d", + "id": "e908984cbe697b713493d9e3f6a8e928932c5352d5f3eb262d7840cc04e8741e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 301300000000, + "fee": 0, + "recipientId": "AMwtequWhiTWPMH7R4PUf6wzEUVoQf41sE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220284be66d3840c7c5a748d244c17b9255e2977142e43263139e36d89e1b204d9502206d9ebc18bd3f19ce852d2b4e8371baed5853cfe7a57d550fb89332f3cebed29d", + "id": "caa7b788cd2a9c62b6f83c5e549fdfecaafd76dc531f1f29689cf3490bcbdbc2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 301947752203, + "fee": 0, + "recipientId": "ATFJPWAy75j6jXHzYPCG1JbWYMm6XvAdiK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b5fb6d53b4dd98f55f120f556a6bb569291942b574a0977ffb82009a8422d8cd02205203575ca53fc5636a40e6f931e429ff63e2bb55931177732a04a0ce0431d431", + "id": "6f0fd1a8e7236b0e2ea0353de6aace7a1a9e52b2268a2e21a7d70ba50df685b8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 302851017757, + "fee": 0, + "recipientId": "AGrNFNUrFmtbDEtGmdmcXY4PmdcGt52xtD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207085b431cd0a8ef985dc12b3b01b7e45ad8b3c0aade162cc21054fda777114b202205df8eb5b1e36a571083b0c89af506f917d5bb80859852a0435fa7af5ecef1994", + "id": "640cabc266bc124bb7575b0d0d54826bb4298f6dcf86728552a7c85cfb9d5c42", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 304323259134, + "fee": 0, + "recipientId": "AcmxYd8QHTF89awqRBCtJFsC1aPvwfnLqx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c4a2d7ce45f32daeefaadb4b12d0f3022f207f68019a82d76fa41fae5f50461d022015bed2ebd307241f334322ac81eacac8b517a6b4942ee9dfa866ccc85cd3c4be", + "id": "88585b0976182c15633b99af07e08efd91f2e2ade96f6bcac9a87289b7b2f7bb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 304700000000, + "fee": 0, + "recipientId": "AcjeSyWdbJbP7Y4xXZpPuGLuNtTL1EVmKx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022040412540fd1740a5bf25c9edee8e55b8697f4862b476d93c57b7397b5265c14e0220394c3e19b2a9ffff36ff404e4ac60a77b695cd3641bdb119a0fe2d9a74ca5b19", + "id": "09b69e99fa28ffce86e2333899a9053e7cb915d4a6eef962c9b19fce83c42229", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 308731620043, + "fee": 0, + "recipientId": "ATXdja19fpLfnjPbpuusYqtSs2xGK1yYy7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f16422eb334f756c50c1af8e4df9897a0341dd5b222fb7e24838978006d1b0b802206eb59fb05242d6208812e4601aeb2a0a64408ac03aa42ccfd6e4d2176cab3821", + "id": "71036c6537b211efc0da3ded963361c03c47b5002a0729cb8bd94c9171514900", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 309164632390, + "fee": 0, + "recipientId": "ARftmK1C8xzd6QJk4xNvvnLUHNNHr5oWD2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d9ae4512d715c87e98370acc74818cb15f4a3575eadbe764e15a6aa4beff9ec10220042de90d3d51992d850581e2f717c40d7e00d85a07d5418856b8b4d28a6da8ed", + "id": "aeff6d588a242455a1226b253a08412327b1936cb47d911cffc5b0e1d4e01876", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 313315057164, + "fee": 0, + "recipientId": "AVziGRo9W9A4ngqrkof2wpHyrDDeBDiKiN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022077e882d387450d847cc5ccf18042ef7c0c9a15b9f01ed523ab2dd90f3debec66022000b0525cdb09ce49c7ffb1588002e56e20f95798e2ed979e7f34cfb7c95aea6c", + "id": "c9fc04b7d64a5f6cc60fade0d91d344ecd925187f0e2b5591cb1e0748c293508", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 315100000000, + "fee": 0, + "recipientId": "ANPQroio7mrAH6hSrETf9nhoBvhz9UfrnY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022004011cfce82a2b867330e93ac537230402fae45ebd6c28029c20a9a179fd588e02203c1d4518c47a0f86f33d93ddd32f6f97d297c74ce4a3a22464695aa14e144311", + "id": "b50c52e231192da85a6249b53545fcc91f17e03db263ba9ba9830f6e2198a36b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 323400000000, + "fee": 0, + "recipientId": "AbnhZAGRAWZQNfj3NSwWacXCcXsoDg1UDZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022043e245c68837fc34dbf6a6f1806a5ded6a173f97a935aed9fe532ec5df5a045402202f5a8bb2ae72c7995e2fcaf1acbb9615b2d0f3567b2bd9db01af6e02c33c1a96", + "id": "5475983134ca085d94e93c8ad7847d8ec2d124c0606e7f65dc7360582927e28b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 333412838852, + "fee": 0, + "recipientId": "AHFC64BTXc4kKdmn2jw8Tb8487uxsbK65j", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f1d1ec1e2e7af629ab53f68441a83f4107ff97e7d21ae0db068cff630a2ad887022030152a4a06f8a0d390d7957995be0a5134f1fbf58693f4ccfb4815abefd9ba45", + "id": "178b974b999420ffc3f16c4b96a2886660174655e77f83e1277d1cc139c42445", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 334419127093, + "fee": 0, + "recipientId": "AbEtCb6JAawzHj3iDF1CnEBWpjdShoRes7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100934d8a30fa7fad31b7e59d5befa251d41a9b476f381394569b6c670388b88ea702201789741ebd7614767df9f43b73a8ebc1ff546e9747ffd22cb1c2f7060891622f", + "id": "5889c1d56044d500f6abd85ba7e02e2fd69dae5740a88a6321acbf5620f0ed6b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 338228944448, + "fee": 0, + "recipientId": "ANm2X2bmWfVavS68Wf6zmisgvyKzmsYWEk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dad3e59166de998a91e9439ee044e318dd82040d8c20ab971d6ba12fe5057384022048f7c14d431001b88b2ce5abecba8fec190d38a1dad0886b1213c2bfe649b96f", + "id": "3255e17ccac83b9df7d9d34bd09ae2a6b6664423c632046b5ace6267e591fa0f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 338641901001, + "fee": 0, + "recipientId": "AR9nWb2Y4rZzrsCTpHCDBsjeTB2cSqxgLr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100842b25c4dcaa3f85e3cffcb5412650e3d16f65600c6f76b3ffda4d3064649ce802204b6e0ff006c7045ec16bc1f2ddec0c778a192bf5f65203f1c9330dbd25e0317e", + "id": "11d040dd27672f77a6dd8088f2a40c0d095d714ff505582422eb9c280f65ca7f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 339243683819, + "fee": 0, + "recipientId": "APkQyY3hE8TTfDdYFxVX9BXfAhKvgLMFFC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d5444e7ab774e40da05ae30917882abef70cd94d28114658515c122bd33b32c022022e8c524d76b82f9241dcb8d49759dcb8e11efdbb2bc8f41ad1340b0ace8ef3e", + "id": "05c45393976a5e5daa2ad7386d12587d3f8096e6e99875b8aad24b6b87fec4c2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 340662911557, + "fee": 0, + "recipientId": "ATynVRNrhhRBWx9xLuwzqRMatJEebrNag5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ab90a2e21ac0cb5269f1e3f662e29d54fba5e57b867fbe8c716a2212c9c2fa06022032daff32ee5c0167d8f1716462077882e411473116cbd3a23289832ddc414a6d", + "id": "863339c27a73d2b35fb206638b23c1048f35854c4d8ef4e0d2c2c9f664a38be3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 340892990675, + "fee": 0, + "recipientId": "AdDGwzuSnwbTgS5zCn9G2tjgbRSVwtwXgq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205c4da9c7896032a24fb33f087f28cbd4ee68a1af5f242ad4cc246ce189c57fc70220754cda9f04dddd1db8a420374a314b3398d2b20c8a13ca69490c9c48af950917", + "id": "6b0fd1ad749e8923efea2a678bd5fa503cad05c1290b85d87336a46641148a1d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 341600000000, + "fee": 0, + "recipientId": "AZXZtF8s1rtY2x7qZ4fup9iXsGdwLgCy5o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022029cb43b5e7381c8692a5e474af506b4443735f56198d2c03d8bac38edbfc46de0220736670e57d73f4d8ee09c27ddf435b1ddbe206fde50b3cf2362115b11ea8da9f", + "id": "79bd2a6949472941f09c7872534dd18cc5cc7d55d8ce9276f0859cc872f1f56f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 342836625893, + "fee": 0, + "recipientId": "AbSRLLJkexPZCWdhznU1YnBs6UmsvAsg8R", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220615218597c85309c9eb5db6cd1d0657998553494a8ceb6140102d9d6955bcd4f02205264ce22077d58684a39bec2dd60d2b90780ebcef424fa29720244d19907dc6b", + "id": "ebce164ccef6fb06922b26611a1d4c755ac5393812f1b6e6725f5a09eacf9607", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 348484491500, + "fee": 0, + "recipientId": "AVv22GuhDy6QAq5EbWmvqPpyNK327dDKpX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210091c2b8ace2dc7f2d4bbda72cb5d52cae4b6413f823dfb4722d7a6b78d93d07df022046b2f87bdc34fb337175fbaa95d17ecd967a2b3bf2e158759b50edc1be0ab51c", + "id": "74c1b29f70e726c862c8db61964d259aabdc5fc1e5ec4a14caae3338c474b092", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 349954995610, + "fee": 0, + "recipientId": "AZKSX4x6Wrun1sKFcm5tUMQTyEDKHSGvEt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204db55e406f565efc50329b609813b5361fa8829413c65dfbdb81eb62d45d4dc702206eef0b82a59c4ae156d53b5174d667b2239934b2f4ce6e3a433ffcf133dfac8d", + "id": "b4f3fff5b12a05df38f253328d0212e80e1b86f9f783d1845676a395fb46b72a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 350000000000, + "fee": 0, + "recipientId": "AXUuekRSD7Z8aAqRz5oEra8tjAhn5Jvvrr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100875b2ed22d8bc7cef1683d8aa8b5f5bf4696859e2770afc120fc5fb9fcaea1c702202771caf3c8ca619fa15f339dfe2c1106d066804689421836b19d9a5d1fdb5cd9", + "id": "15a1e1313a9a1df3686417a2eed1cae978664b8413e79fea6b38925a286b4411", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 350000000000, + "fee": 0, + "recipientId": "AWnx5rbjMA3BNCXQ2JDXw9jjtdJtqG9gvU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220723a306d9e64fb999de916be02dd0a12c45a33da542fb93eb5be5a73a39c7df0022063a52580d99354654e80fcd43440c0df1dd932c939eb672308bfa4ea705ff901", + "id": "6a717fdc8c6b1052bc7f70a2a18ffa4bf14f78b8d252825c90424d920f13a24f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 350918258935, + "fee": 0, + "recipientId": "AGUqJi6qb2E4F51wdxxdtomHGdTgDMYhkm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a2ed646787d44f8f362242ce96426f32fa938967c409f3d1f748b713aab0a2c9022019adfe63d28f5b634ef60f843021e42514baa766c465a45840d73bedc00001f5", + "id": "a4b4f4cb2902f4d9a0489fe35641c04bf8911b064d9cd29b80cb0a1274ed64d5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 351177075272, + "fee": 0, + "recipientId": "AMQLH7k26GetQAX3q4shwWaDooL9cDcW9F", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e6ec5c6fcc7b88f4325f2e449d139e6889e8eea625463b9cb120b8921c26a902022049986b8ca2d95d0f3b461601b3411cc7e694271a2496ce84088899242a9212d4", + "id": "6f0898653b2de3e94b56625e6d6248bfe50f2e120b0a6425a311f6a47e7a3b26", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 352101061907, + "fee": 0, + "recipientId": "AKGkMr8fTn7d5Wcu3AAyjbVM338JWubiHm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207b22f8fc7e97f5669baeefa2e72d9d30dce95e4ece077da817c391460050de28022045ecc963840f840ebf144a32ca3f47827a3f8665ccca7653a718d273a29c6a52", + "id": "35b48f395bb80488b5b9542206cac08dc7bb6e1c4ac2f07fceeaa028a615b2df", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 353468821454, + "fee": 0, + "recipientId": "ALkHY1xQK74faXu6C3Pejk1xSuAFqCFdhz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022077fe72a93d8d27611229b8f74ec4dcdd4fce7c4c3379d44237814ed7e5b81bf00220679f8baa1992972284470f22d085907bcaf41b5cbdeaf9ae886be71187e38c0a", + "id": "ac919bd687999aa984250568b6c43e8cedd7d1a639aed32a816f8e1588cd901a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 356573668788, + "fee": 0, + "recipientId": "AaEfJQu8y267hp5fQSyJtzR4iJSor7q5xH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f101ca42eae873277eae594351e634addb1ecd245c28fab5a3b6ad97add0800f02204a262f763608d06ae684f5147b1ccf82bc9538bd82937de72cd1d8593c9e9804", + "id": "51f08208e82c1ea12099b608bc6cb3fdec272ae383c10cc18cca09d3a6906ad9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 357292915405, + "fee": 0, + "recipientId": "Ab7CQLYGsWUEwHRY86hEPRXaABtMRfj4vp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022048b11cb528691c8397aa0c8b01d4824ab9ab37b2ffbfd5c93ea7632be5414abf02205d868c12d097942225f488ae41bb9a0b83fb7e12fe163eabbec26a50a5751d17", + "id": "cbb6e663289d0af21a30738d8456ad4bf347fc60052eca55937598ddcd1f8127", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 357640325222, + "fee": 0, + "recipientId": "AQzZvHDiVUfrPTs3PhNi6xXaCmWwPJ9rW3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c6744c0afeb8b450b915b4131c6a5318073f20cc7828f31f05b5fe7a51d40413022075e0848a80f03df23205e31438c4f7fdb16aa36060e87838f5ea4098ae5e92c5", + "id": "aae37ca7a805749e82bc2ccb0f812c0c80c5731d4b0258a6a44b394985f4e213", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 359722188329, + "fee": 0, + "recipientId": "AH45YgpupuJgJaKfNqpxrbusmyxxzAsSiZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205685e59ae6d20fe9242cf689a3a6d7741215b7d473405716b61eaa15c4ae156f02201251a4a0cb27f4f5501c5b5feb891c730a795620d7092d7906b4d853e830ffaf", + "id": "bcf73615f4b48a7ea0a5570da0b75b6edbe053808301a5d3c0465da8a1c8a992", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 361053804649, + "fee": 0, + "recipientId": "ALGxVP1rkTWWUpEZyhTvzT8YJprKADx6jA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100850f4683c8641e83b64d7076b43922e70569769b224813ab0513c88e51470eb302200ba35068526155c1413020bf4c18f2b4b8cc32f5e16877a9e10aa32dae1907cd", + "id": "afd3ee65410f9d76132c8cecbc796bfd54a530327ba33af33dc0e9a07760c1fd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 363315727061, + "fee": 0, + "recipientId": "AMQ8ezFU9gvYfyxxk7VETnXmK6kpt1Looc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201689314194023cd9e64eef30f0051279da6253ab7b487de47d5ce3b81487f2830220148ffa133c7bca9cf6d1d743105be8b3912af8426d7e0309b969c2e2d6334674", + "id": "a9bcd03694c114a041220271fd5f01274c7ae365a6b0f85d89b364a104a20e29", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 364303311651, + "fee": 0, + "recipientId": "Aeg4n5zYvShKUmhuXe7CmzvEJqFBTv3jVs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100899497a5fba8b821e22acd35d5027771d09baf69899acf626c47d63ee4ac74fe0220481eab02c6e999522954a7da743125b5162b56620a93d6132195675464f64c8d", + "id": "e7ac9b3742d019488f8d93c3418e19fa8bf556bc232ff0af88cc300e631329ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 364467189703, + "fee": 0, + "recipientId": "AUSKfmvgiPu3LSAfY6Kfyo4zR1VgL2twcL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022055af734911a3bfe58f44351b3ae7c15653ea3738911cb6d9872c62471eb3716d022032d62b6716655c8a6e9109fada3a82a3b4a20809ab6c0166ee79327529e92ac0", + "id": "635946b171c00b3f9c0983e4a43582187bd2a3016349a6760732e434bdfcd697", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 365586352150, + "fee": 0, + "recipientId": "ANV7tExbU5ZgSDJGYyDtDmfg3Z8SVZ6rvf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220493d2343b10c305a73614abd14c038561f4ae878c6d9957a171c0616f149187f022042548385875006c2cb45faaf5322f455b178d256075a007913ac11be84fd68b7", + "id": "ebba2ab5afa6f21355ca4f77d4b79e55d08b88c0b8b35afb69749ac61f0b031d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 366287950968, + "fee": 0, + "recipientId": "AcM4tvSTkSHsvjLM2MtBEkMX53ErPpFEbo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f9c913da5d2500b202003576ef1c2f478ee3218ac7d90ec443b94b64e96012f0220537e1b8af0d427a41400ba6008a463d00006c5f722bbc6b65e1d83d524863ec4", + "id": "a6fa7609921c11ea4658cea3ee0626f1a87d0ae21324780ce17721ed0d21f539", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 369653766955, + "fee": 0, + "recipientId": "AMfKrnGPBwckMBZff3rR4wFGfeztHUXEXk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202e4e5c658ba2857c9b0d56b5babd7bcb848bece7b38820f4407827132032d59d0220713d1203f84e049caebf326b1eb3424b91643f407ee34494d1614ecc7b06c7dd", + "id": "ac8c772db29278a0bd50fa5e4b0a016fa8bb80b35b20bbb538ead6f53218cfff", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 373149442409, + "fee": 0, + "recipientId": "AVHXn563x7mzNDVN32MCf8Jngc7UKgGoY2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022009b8fc1be1c3fde84bf3099e79c2356bea23ae042a0f76bb1d07b34fd4e90b410220704e5c4ced7376c4c7135004c0f07ae545ea1b5a0e72355caf0aae0150c865af", + "id": "36ab40ef3b10937488dd8593e7726d4c965a5baa8b2839f06620cd6392c146aa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 373746668966, + "fee": 0, + "recipientId": "AUc2gugBPs87YHajno9jVS2niPspZLydtb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d83d24ca3f90fbf5791169619436bc87c015f58140ea0c72a4c68df3996ea50c02201a061ded755126c92713e464bdcfd652cd4f7530aa1c5a56f1c56e366b388236", + "id": "f46f2c84bfb139e6bd22eaafd4b8c18a0547635dc798b91a3bc40567ddedb41a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 373774406219, + "fee": 0, + "recipientId": "AasYFcLgSztcGK66vSat1rgHSpFSxbgMML", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dd07f53bb0e76df7d4dae83990242fb5db90a9c53c1d93855620b03eacf5d95902207957e7389be539c14ff9a17b9b0cd4418ebb14e80a74e3e56b555062e209e333", + "id": "19b48281b3104b5c29a0858d05fb08e8c324437fbeba15fdf68756842b3434e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 374988724940, + "fee": 0, + "recipientId": "AbNopZKDikntAR9jguVZBChPeVmrp363ey", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220238b5a7da45bc180902e8b2c49d8552306a721f6a7f7f435eea329e0fb6952af022028e04e87bb64fb457a3cde60771481e71aec7fc8c2522cb05918e6677242ef67", + "id": "3e5fa869674d84582bf5a2cd3a2d18a52af64b80c188ddc9b15d6dc4b129c67a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 395360944311, + "fee": 0, + "recipientId": "Abk1pu25HhKdUVPwwkGqp1zqhYMTHEU2bR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008e3454257b6c3f1b6bcaf812b5f3551ecaf4cf7118fca5367d4cdd4155208650022038add348b1a48125de8a37d8724e497e140b69234cff0a3923e70fc305c8d58a", + "id": "e0aec5537bfbb10983ad3268b4059a8b08fecd76a16db4a871d76e3f9d82c6a8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 396243735944, + "fee": 0, + "recipientId": "AFrHJuVb2HT4c4tWqYEvWgiY6CuymhkNFs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ef46b5c739f79696c0b0a9a8f735cec91a0dc546accc233ff32f7e5cbf38b40902200dfdc24247a4f60f3e791fba193d9e9d16d83131184028e248a50ea4cb2a7272", + "id": "f2205ba50118bfce10259a754db70528268ee9112d16da6e2bd7dac30d06418c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 396752660012, + "fee": 0, + "recipientId": "AdWC72U5LSwghdenW8DKXWHYuULfUDrCP3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008a55e63ad2b9b3d0baa2b6a2f80606f38d02abaac071387077ccba86f315a80c022013c555cafa7277020701df0004f2b695e34253ed7cbcd6f2a9e0e66593390577", + "id": "61c5f683b3a21404863121a1833c43e94f7f97acfc4100198f24e8dce6e5fd7a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 398860671270, + "fee": 0, + "recipientId": "AHV54m6LqZG8A1YdPKzvJdBfdoV48VzNmt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220281ea4f34930789c6890fcae28d56ae12d1877038a112355f45a8586a7f7108a02204b0ceb0a989c686b91d84d42cbb5c8de0657c2988d9acfda7911f6fb37a4d0d4", + "id": "8aa2f2aec071acb8418bea239baeabcf9e22a4ec3059777df5f2f379f0dde30d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 399900000000, + "fee": 0, + "recipientId": "AMDpfwcmvCFW1jZYJz5Vk16pytx8zwqf4M", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202512aac371fad98ace991a2c0b3c736cb12b18da8206f5308dab02b71043c1df022031d2c0fee93eadf08de6f04abbd6795f15467d0d9264bd22c67bcaab93a40307", + "id": "9147058ff25475d0975386208f9d0d4cc8c60bbdf77737a09dde4bc168638db9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 406030861383, + "fee": 0, + "recipientId": "Ad8LXbyPdTSYMHvcCZiQzaUtsd8VkLRdUk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201df9dcb7dfcd79218826fc4777d0216f5020377bf2c1ab94e3818f17a03a6ec3022052324948abd06924e123c5e5042f9b388bcfd0f127b962f4450b166207f6b4d8", + "id": "7dd69f0c7424458dc39d3500fda4e8b1e5faebb079d6917f908c88192e03602c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 410054755558, + "fee": 0, + "recipientId": "AejLSizWdJowzJq3o2DZstQmyZNdqGGzJK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b53a8ca2840af4fafdfd607ba7a2673d1a3a70c4c3001cdfd03998e1d85049bb02206d11131b4410a8214be0bb9035664caa24e5ac6788ad7201442e151ca4e75b2e", + "id": "84fd6c750dbc8697bf563b042b5590d7d543e0f14b07cc3af1de3a89ceeca224", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 411430774232, + "fee": 0, + "recipientId": "AQwJBgy1TXokikH7XvqpyWtgAexhvnEX4e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022005de47c592104b12833c0de59912e4f98001ab5e263af9d085828237f378a73f022074efe6b3260b7fc941691a426dd73e567eae101b680e360652e9c1539dc26c1e", + "id": "4594cf80dd9c58f780619f1c5b07ded414855e984bfe92f9c8b5e253703eeac4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 414435446144, + "fee": 0, + "recipientId": "ALcuF8EWQZo6wHdhEab3SPG6NNnUhxmtrS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be8dacfc1c3bf51ff37c4db4df096642aee830e9c54b03da3a2cb1093150099a02206694dc3b9cfb53ef9c9af478bb5663979bcf740cedd5c36dc892ae50ac2b5c38", + "id": "3683470aee128d9e3198d1367815a49543180de9e5b6a354c6cace53386b998f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 414495122050, + "fee": 0, + "recipientId": "ASCzbyDxfQipaZCjC6VR5hbGG9YYgsaAwd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b9aef4b9f36b7c0fefd1a5fef061047e327f54935080c118cbf913f607e42d1c02206edd5cbb8feae8309a3ea4e53ce4a86f9d91a084d453cfb51dcbff13522ce002", + "id": "198ee58a22c79eb720667aa9d17e249be4e2eb3114c7206ab0a73b7c9127ccfb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 421404948418, + "fee": 0, + "recipientId": "AcZ8qLedqB3pqDv59QMLD5zLCMLEPsZiyA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203294398344f0e3863dcb9709bf6bf915ff4c678a681edf48f03ed605ef990aeb0220247299f0e0a0abf1595d9801220e2be6ae9baffd4611aa62683697dbfb051f92", + "id": "83fbac69f11ce4a4d5993f8b7c60a4d1da9738f85e0d2e6905d49f41f62f22ab", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 422404168409, + "fee": 0, + "recipientId": "ALEAsaG5tcombmn4ewk6CtCZcT8jxS5LfA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022044a875f285cb908df1025a9246529b88c88320c3d2c5b212721a67afba69304e022035b80eec3f0b523df1d8b235f9c9ad05c569e33f6ed4b7d72127ac88a8e04a51", + "id": "f01d3a9212807fcad775b20db523002d72db87387a2c0c0e6d6e43a96f76e10c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 425000000000, + "fee": 0, + "recipientId": "AaFVVK4bHWAULm3enE5jFuW8w1tGDVAtPP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e4c2fc79977dd2c8202b0b9b710c1d345266b58a583911b8d7dab80cf6a9efa8022043b02b4273d62a6bfb357060314f977c8688fa2c9fd9655f1225d76e5f210598", + "id": "a9d807156e0201c2e6b62cbcd86df7e744c113443547394481215613463aa517", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 428842921747, + "fee": 0, + "recipientId": "ARTND7EwNVQyRRcHmxRMXD4vDxMsuvWHe2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206d2758d81dfb99fe616979b5241ddc7054c2ba0661db8534b27f5a15a6e42f52022075af3ca1a436a580397f182ffe8dc07734eb4b2a67bda747135546f8fd3462f1", + "id": "30cc3a106ccd48d7a88c1f6d948d5701670c7541ea85d7472bc0a8e66899a9a6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 429330120346, + "fee": 0, + "recipientId": "AUnowRvHGnZnpPYWQhjnP5GRh8waH9oaoW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220396919ca6596b28199114e6bed458f8b3fcd53d1bc01b9c55a3e8c393f2694480220072f26bdc63d2ca9bd7c3c4a4ad1e246676055338077894de505da12f2b81a8d", + "id": "b673418ffcaa8a343b85cc02856e8d1aa2167c9e80228b738ac3d9487a56adf1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 429900000000, + "fee": 0, + "recipientId": "AVtEhnUNTLMG1ABGW8MxoprpQpcnjVMMs5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a4775a3edb88c12d3117057cb71c556f460bada4d578d14d52f6f16f52dbe1e502206f0793cf3cba1bbfb17195e79343b6652414cd85a1ad47a6e76819f58a41e794", + "id": "91d608f9137abb9d4f30446786eca2f74cd9173b0cf9e47f3a917b400bea2d78", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 430758143690, + "fee": 0, + "recipientId": "AcsmQm5EH94A8bmxVhBX5azqRZ5HxSFurH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ffcdd344a83d815b35399377593e907593848c590e4f6b690f90c8d87970f7d20220098ae996ee30c244bfbe04fea6f0940488dace06119d2af396b7d08dd925a328", + "id": "9693698248b30da4d32f3bb74a94185545a22511a32789b6e7c6a62c22300d15", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 431169218840, + "fee": 0, + "recipientId": "AaNpyjWJC1SzuYNtPd5eJrfdNGD6syTzLe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a19b4cae750b166dc50c3e22bd12dfc5f490b58cd13c139412b61dc7acc7149502200418a034b9a2b6fa26b12276e5bba9f79a15ddf3307b51ac1d3b35316087a080", + "id": "074cbdd2b8b306c0883d1e6d0a468376c98f00274da7467af05095edb852a044", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 433300000000, + "fee": 0, + "recipientId": "AS31C6KTNhEc6P3pHgpZYg6L6xduuqvSXW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200369f8d253aed3996199457e932c7f1b95d12cfbac50abf255e04a331eb6b30302200c9643963f4b856e9ddb9572797944164f4a2991e2073ddd0a9c0853da0b969a", + "id": "bed6b1d63572f48b674a38739a4f33608fffd9267989b21144f3fa1de1f6d39f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 434358663505, + "fee": 0, + "recipientId": "AMKqjbsJKvsiRyzBwc3htV5V2sqrwkXpzK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022026566e29b8b649d199aa887ab842f5b815df66865cc04f5e1d3a26378c3a39450220284f908cca59b861fcb512612c0e66a0ddd855d4472d0fe6a96d113963dd0ed5", + "id": "73c577979ee440ffcda796ac338ecc04d7a462b69c17336ddcbf05dafeff9e9d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 441082175858, + "fee": 0, + "recipientId": "AJCDjQNpuuEN2byRdGY9xf2qRBCeASsCv9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203e7de0ac5c2dca5bb3b367f2006e33648f0b99fe5321af1cf06923f5638376e702207b223a41226368a7fbf51a26ede9ce708c86dadf563446aadc82eee78a142f29", + "id": "295bc81dc9d3e434b8b4cd76bf32878432742128e94b10f133b6f8596e4ae65e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 442296547242, + "fee": 0, + "recipientId": "AeeJpSnh6sHMH9QFnaS8iNQUxHX5kxZwa1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205a6753a13b5dba6748e93d9ff8c9e0b243916edebc82d50f26dfa4023c5a1c97022015cff0083bd455c998ba99ad3836fe8f569dea1b16e46f3cb0a1789a9015abc1", + "id": "db2bd8f694e5d5d7193469bc9af661d54ccdc1bf5f8dd1672648edabcec5585a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 444900000000, + "fee": 0, + "recipientId": "AYBuKPH9GghfWpVeoXmHheJ3hFEHHsPLL6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022076e35e14f992269d3388839ceb60bf32ec1b02a0ef6e63ddab97fce76cebe95702203cc902cf0c383317ee84a5b6fe7ad1a5d0e507d3dfb9a5e1cb6b72bed9c2e013", + "id": "05b8f0519ee71468c7b25ee5d7e80360de95b6e2076886f3c1407f0b1b2c9f35", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 454296574143, + "fee": 0, + "recipientId": "ANdCnCPbZcMmb5DcuxKiC4Cun8XfNpk9LH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e4072324402b907461e4bcfcf4b064dad72e9099acd171f27d208a9a018c2c4d022011b20091c75c4dc05d32e8c40b202c910a32a6fd18da1b531ddb4f5dc677a0f5", + "id": "9c35f16fe5a213937cb9d260efb89e114bc59310f3843280f6f858626257cbe0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 454410176686, + "fee": 0, + "recipientId": "ASDxEzhr4QLiKvE8gx8NyBNpNJBiY93bBN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be0f72e16ac306667849e550bfa53a55f77446ba1b3e85190a28c789d80e2dda022021706b1b73b64cd7d98e85c79e9787cae57efdaddf31c60e9d13c6530432d09a", + "id": "68038c71701292ed7e30371c8f58c74de6926a9eecd9833f205e85817b4880c7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 458329154329, + "fee": 0, + "recipientId": "Af4KkTdudJhshRqyQknHmieMTzgN1baKGk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200d98f84a03352a7404824fbf2565597e3407f554602a05db8c9e94842121de5a02201500a98f7224e8e716c2719d8981995c6a9cb0c38b456842b8b219c6034819b4", + "id": "13437129b6e18a12d3acdb01c9e113c21bdd85e2058aa56a460af7f3c8423501", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 461698156433, + "fee": 0, + "recipientId": "AJJ7HZHTZGmkLjGLBiWJ2ni47EVVmsPAcW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eb4bc75f85eb983bd0b2a1d73ebbaf29c0163ba020b3fb85781ce3c1a97d6a49022071bb413f07e6c7b13f6f8c82dc9d023da2ef6f262883aadbaae6def1dbe5cc1d", + "id": "2e52fe4e3801a2a567ab73693da5448d5f0bfde55a2f0d6f4052075820ecae65", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 462363719366, + "fee": 0, + "recipientId": "AVsMnuEh8yCdQjAtc81NZzRsxJJDVjf9Ka", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ffd9156e78730c609974cc6a31a597d827cde890c7e5de46bc54cb10d7b1d51d02200303b539a531b513eb059a15ed6a5b08f9299e634348e3228ba323cee42d16bb", + "id": "5ce506c37e536cf8d7da0c3d2208bf27e461c5b261b9eae156808812fa14a6ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 462789386912, + "fee": 0, + "recipientId": "AVmGqru9A4Zpn3kd7goY1kJ3h37nAaxkN1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022039e8e2eff6ee9b7df8d20d070143f244f3a29b4c673932576e2d1d514d31d7af02202a79c76be0c59c0d27af4a7c5e45da0ac00944eaee1eb9aaffb3768eb3986095", + "id": "a3875b9ae99d76e48778e0b37392498715806214a2cfca46307c828e0d160ba5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 464492402483, + "fee": 0, + "recipientId": "Acb9QU2CSwSXNcbZARBwyqBSSsQRubqjHq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202e6eaaf793a04b8fd064315af7d6cc5b911f86faf21346fa2a6e013bdb2191d3022038a043a4914bc834a47c6c903cddf4dba4eef84885201b9c4a637cfdd9552f55", + "id": "7f81ee0bb3fe048921ccb934a4476c15a339019cd8bd3de40685062281d9373b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 466008654301, + "fee": 0, + "recipientId": "AQ2bpZffieDKR6TFatkS3vZwDLyvDpA6Jk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220516e15a8cd6382b7e0b93ee04ce1ceff0621dbb17ec2f79158a1dfce7d3829e902201383e9cbe11f387f87cd8e1701a0d974c61ea92f75090620065125f1b283ceaa", + "id": "838d54e1f7681e910380bc08a02aefa3bd47d347c10c9f17030cc1bbdbc9067a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 468845108203, + "fee": 0, + "recipientId": "AbBz7xmNnHkkdww8fBK7x4EcEi26V5sqr6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009364c19fed0bfc1c4566e19fe01e83bace4233e7f7bbb25ab08971ef5c2e00500220118bc321de72db6de795781e1ccfe1c587b89362be1a8f9ff43d9ec1e2411633", + "id": "de57689682daa61b89193fc451040ae47b6335ef07f363e86bb37ff0b28f75d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 477386564720, + "fee": 0, + "recipientId": "AT3dyR6SqoMafVWKsMvL9h3iBvsU2Q7S63", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b8e109b247b4d83739b36d32ad9e54f11979bbb54ec0629da72f65274e468d7302203b1370fce9d9e950120e102ebce751b2381d2319353e5c0313c6c84e5f24fff6", + "id": "8ec9dac6224398952f9c801261fa4a901ec3e823bc47301201c8e1d377b77bd1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 477386564720, + "fee": 0, + "recipientId": "Aeb22zZXfmhNvEjUH6H3ZT64fJCEqR62iJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009fea8a4720368c5228f554d16026c0286bc036d49f5de51abfa2689d27e54346022061f2f0b0165159567388103419fce40a4b296079369bff483df251fcb9427764", + "id": "a47039058f275732366634483cc82c01a7ceddcfb6fe448f4e60eab614ae0e64", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 477600000000, + "fee": 0, + "recipientId": "AWhmJrkMyrJsdGjVaz2LoqAboibJFdpeKg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202f3417cab05063c88942c5011311b5d19f7c05bbffc67e6ae090388803a8639502200a05fb48af403296455264ca2a3a3f6d4b1cc89706877be3335fc5c490c88692", + "id": "b6c19da13c34a12fb2a06d6a081b172074f07641024f3021b2105864e31faa49", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 486438883282, + "fee": 0, + "recipientId": "ANVReFpTPz83WVcVZocHwuNwJLuGW8Hg5n", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc6894303b0552ad18f66494029308976efe651de9f707a0187059e33106431302203f70460a4113ba5b089efc2d85e885832a349a9860bd05563c55f5d5923bbca5", + "id": "2644a2b4e0c87b2b0fb23f8da21f0c085ea8aae031436e846d45d77699ad1c28", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 486986935045, + "fee": 0, + "recipientId": "APoqq5cehPy8xdqgY5nPMFtupDK8HcCWFD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e1ac0a3e424f949cdafbabed541a5fe658eacf781e059b22460f1ccceeac7554022050119d98a70c087621994890f55d3d8a79e889b02cc186f8be1ef04ca4ede1fc", + "id": "0bb8b3edd973b8b51a57550b49252f708528319de548f0b89a77aaf8f567a685", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 499452432600, + "fee": 0, + "recipientId": "AaVHPBJGnHGAyDsXqcXMkgnTXrP2VA8zXd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202040b29b5ae655905a4be13aa5160fcc682c0c3df0768ef4c15142e0b6d79f45022070a5b44807bfe60f84455ad05957fdee06cf677a99279b25c33eab56b33bba80", + "id": "a8a4acb7cd42fb523baa102fa6dfe72371a2686c99d18e8f26d83cd65ce69e62", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 506413677424, + "fee": 0, + "recipientId": "AViKTbBER3QdyjPYvu6aEikJG6jyMZeBMH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220575b7844c2c7dfe981396bdba8d577ed88738a16e4307c522077255c5db8225502206ff9da12d3b9a67eebfb1234918c19e92b1d94f75983c5ffc8b5598d0992a40d", + "id": "a424e155f169f58f922458c89352fc80a71a214a9b7f5d7edf53b833b7e9bb3c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 507369287698, + "fee": 0, + "recipientId": "AN4SVH6wMd1J2UinEszD1m6rNd2Qf67s6p", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202fc2e97188ea29acbbb5cb669a91834399df4e95258f843e5f508b2ae92bdbaa02201503b9313688be4f868f27960bd4ec9d3e26b49d09455e7261f811a8f4bb9f64", + "id": "362dce6072bbf7ddabf9cefee0b27fb43eae636f5d4a0d2f777319764ae643b9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 509141981427, + "fee": 0, + "recipientId": "Abjr6LJqKumLBkGwWdCmqQU6ySWNTvYs83", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210088070a6fcbbe069be1a6a7b9de2ac4baf72f01078d69e8b3203bc85bc87e68e802200993087e55d13928a55f3a543cb91e780fe082d1a571da71ad6e4edd4ae63af3", + "id": "980d85c7519a5bf12e66c830e13372ce93d7918dea83f61bf88aaf57e5d44e6f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 515274387317, + "fee": 0, + "recipientId": "AKsTx2HPyBzhbNiEuUTjaBefkXsbWBmAzj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206799aa5ca25023037fc2046c9976c29eee3253c6110d6d68da3b38a011f85ae20220498f5755684e9f2525f2080ebbec2c1306b18dbcbb1ef40d9006f17b3e12c30b", + "id": "0b85faa2155edb7516010b7b78fab8170527f6f51a144eda464f9bf148a2997f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 516873898330, + "fee": 0, + "recipientId": "AWP1xqsXMMgBDfRpvJZum4fywFLtRvT5Uz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205a98023efaa4e52f9936bfc80e4f8d4230a2c907a7723a7766a7ec0eed9992fc02202266a5cb028f371ab13c03ac11ec40a711c0473834a595da18591c2293622f15", + "id": "bbf1e033954f8086c4ba041a94635ae3ed8e0a34fdb0ecb699aa1a62edf9dac7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 522000000000, + "fee": 0, + "recipientId": "AZydcTLh6qTuJzSkt2i7j7neaaMxN57bqL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049014a7b3c815b9989d7c4da1275d9381d5ea1bf94fa0197e2f1cce3093f9001022018b6744495c6da6cec8820e4d28edac409e2f4a9383ea42f918a792c89fda18d", + "id": "253a6707bd401ebd1b7d1465bf5857db4aba9e8ad438b8d97d121a7bf3a6e7c5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 522851951837, + "fee": 0, + "recipientId": "AZ1BHFwiQLgXfnbiW8poyzX44z9fg6R7Pu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e6ea691fcf6ceb3735eeca8e83cd6be8664b9c42bd31aa0317fda910b154e65002206556f64d1821aec7395eadb7d2aae1a5a95e7a063b62ca6dcb6176d3ae8190df", + "id": "a01d46e24b46904aa9a2163d3950c53af19460ac839238d03c7304ac83b33820", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 526711619581, + "fee": 0, + "recipientId": "AUsWkvkAVgvaFhj9VTX4U96FyGFh5KbdZU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f4e1b32ed7f1c7831444c0b2452e8d329dc9a0a439da676a5a56d0ef2b8c266902201bf1c1a13f4ecd6289b91e866e73b727ce735baa4128f0825acbd976e147ea32", + "id": "c22131de3dff92e746993d7a01636fa1ca2dd20e01d4db0708271aae6a63bb49", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 528376108643, + "fee": 0, + "recipientId": "AUuTNzGAj9F1aNpcKDJfuDzHYj2ndYLihD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ffbe2910b4d88d818dbc798913af9ddc106e423242640cc712e0d55c1a4a1d602206262f13fc3fd933e9f92f1455ebe280d33727501dfca60f8dc1bf85bfeb51ce0", + "id": "b487b250cc3f98368b60ec6c5fabd510e1a8483cc03bcd74d4931ef792080712", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 531321344927, + "fee": 0, + "recipientId": "ARtGz4V9e3wzW3iRtpYo4DbREDAvCwAg2k", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203776cd4346a82b61c46c2ae9fe7e7b5e412b84727183646992b4764249881c59022025e092b1d5180fb4cf23717ba0de4444e52067414da0bf474752763cdbefb8c2", + "id": "fe48867e9156b1ada766c579bce19e3d01f92164d9c265902b7a6c20159d520e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 536606958259, + "fee": 0, + "recipientId": "AWFff1CkyqJWtugHuYYE8WWzBP8w9qwuhL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220024c4fee1e8ac337e575ed098a4ecfe070ada692085295894459b7b7a3b86c37022034f2e22c2438ebf949955ee0e865b528a781da86b942321eb3725178a2f8173c", + "id": "f1fcd6c5c6b3c83d0bd5accfcb929061d8804a863015cb094100ed09c90b0076", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 538220305472, + "fee": 0, + "recipientId": "AMF2nM6FEMBBfv7qWc9tW1TWv93dxWbXjn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220315cf7c033bfeda43ab267511e88fbe392380aede5ab0087e7295bc4b20b0abd02202524f858f7d42cc1c5fa6866221d7463abc1b94d0185067ff0b3dd57a182984e", + "id": "8e26b02745f1f5ced11fa24895b0bd773a2db5370126c1e92235f1b123570548", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 539888138573, + "fee": 0, + "recipientId": "AUoii6QU9zwWhRJzLZMF71E6dffv7c9f1P", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022045f6e18076dffb721463d1e34eaf47a3bdd0d1d0e87d19d9e0a2ece49c9ad71f02206a0b723c4937c7861579b098763ddb8c533a7308dced9baf55d943b17e8b6237", + "id": "b56baa4d48dda93fd85fcfed035b4ee474f64469e28eede0b2369b4ffe17a542", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 540008433875, + "fee": 0, + "recipientId": "AKpuB5YvWrEzgxGqs9RSGr9oT9WQLCyxaF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022066b1533fde6b296e7cc40d4764676bed7e9e088489e2482d9d7151f845f1c7c3022041502f6705fed4575514296bb157a9c5ad601758992acb22be436badfbb66f21", + "id": "85882091ee2b032360bfc72a9502434cda2fb938f5362225d68c4f77c3a47600", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 540816219226, + "fee": 0, + "recipientId": "AGGeFX427aMiLYXk3n6u1c4hup1X2STgdV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220276e0d0e57371d2e5d6d9dcdcc8f9f1267bc27f640e95eb88b8024716e019743022034bd26c909f2653ca8af6298aca77609e8eb367e16f4b4507ad280beeae51699", + "id": "d966f146fa457ac9906b6b928bff9807416e8506483774c8948c0398c0481435", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 552260891955, + "fee": 0, + "recipientId": "AQrYk1AbtQ2brZ3dvsbHh8rdErYGJb68pi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a1ad97eeb99f64d9eae5da29ec69a174058788941b7d7d43348e0e84a8d04cc902202520b1fc1ffc869ab83da821886b904d1e94193dd33210b5fa6cf38a1c9ea9bd", + "id": "ccf83a1e1d918e27052860875f0abf5e55dc14f6933c432eab13264919a60d6a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 553060575959, + "fee": 0, + "recipientId": "ARMwrHG3miRYdVZsGYmeXKQULB6GwMyyad", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200110ca9f26cf5a36a36e2b035b932b98df256209b95465a57eaaf8c2b19c05b6022062f55565484251018cd6e30fe19ae6387d5bff36836dc7b09fcfb5c350efa4c4", + "id": "f8aecfe8e66cf62d917c2c231855f9d4a7a479e918464658a98d4cf5dc03edc7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 559276526635, + "fee": 0, + "recipientId": "AUbZ5CjroZETpDU9rmQtJdVU1ZSBHhxWNk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200c8f7e1cf87873f2035f917f692cb58ffb2e8a5ecd68a13f25e8ad08b3309eb002201b7d7b59ee7e63221343a44e4a04642118e38acd08ca658ec01bf620040f7ec9", + "id": "d94bfd69197aa074ae9ede3c8abf9fe3a67b49bf498d3c620c364197be028b16", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 564299912400, + "fee": 0, + "recipientId": "AcMseyJzDq5Rsh2RYqJsGXYncjQinXDtvX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a5edcdabd03e64a2e29f5bbf6427db67691f39b54ac517fe46fcf26b3515db9a022074d62771c4396e6252d56581e6413ed4b24891272bc7c8fb2a3ae01d2b7d6161", + "id": "a2f8f856798ed3ea34daac7d04f99409ceb4b58e897f8a4a8f05e1706926c64c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 566286551662, + "fee": 0, + "recipientId": "AQFkoubX26ch8tFBy7dm9yG98LLqV7cMCo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206e638a8e03710eb44c0d7e8ac09b1b38366156bc8c46e1a0ee2f88424b86c5b40220247f979fe46dce7faff11bff26d3fd1bcd510747dc857b9cce2c354fc8d31043", + "id": "392e37377917bbe974e3c8ce837d0ecf52f41f1bbb7ec2e0056d0b09fd44620a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 567899118315, + "fee": 0, + "recipientId": "AYMj9jGadgkv3uWMpRr9f8P9poV9HPqr8c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100937f17e1cd7f010b860f1f87e21802ee7c72e826564e9eea6b942b862e56f0ef02201673df293e24e84c2528bc70bca4ebfa5f73102beb49bbf999ec169527e2a3e7", + "id": "efb581f15c255041206ec351d3ee4f0b20792e6f99b29da27f9f588d1a5fb34a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 567899118315, + "fee": 0, + "recipientId": "AGpjXT2iG15ACjpwGGnZuZ1rcEB8WuwLGn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b12dd8dfdfad12e59a4bd1b13e589a882674f1d9ddfd7f91286e10def66a8e9e022054bd93d1ea02976eb99f7d2b12c3df7b4d10db3cd77bc02810332c24a2e26227", + "id": "4aa30ef712b9518114abbcfbcbc962414af90b4618d22288fcc6c9b8cedf4453", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 568012720859, + "fee": 0, + "recipientId": "AGM85ysEstriQdnff6hLY96CNegLmfsTNx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009b5a8d8225cc5c573f92f1f007c19ee1a702093e08dde891385ed3c19f00258902205e1088df5448c3a8aed0cc80a51b16db6759b80cdcfc318ad0cf70bb1eb19ca3", + "id": "32da511a73a5b2077d70a3299332790197c37351f0f6a10178cf1f777126e6f2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 568126323403, + "fee": 0, + "recipientId": "AWkSJ6mwXFC75PCU2Jq35ddXHyDAWETy8d", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bcb4517f522f1c416d6ff3a5112dd22b15de6ac76fa7288c3e53859265b71208022046f9365a91793e947578c86fc7ff612c6f34d468dd6ea61f35b7b4467ca7f87e", + "id": "6de463003b0a10c7372a8ba2490533ae3a485d916b93986d2049980bf3771e8d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 572950085963, + "fee": 0, + "recipientId": "AXQXmHt4c1EcpxFAqkC9Cy7J1ui7FJaTUe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210081b78d77da50f2b94713bb862662883a4e0dbe744ff6d41e14c5448dec5f99ef02203d4ec23476ed4adebfde4e7043cb0fdf6f7a07800d0bc350c4509baa698eb45a", + "id": "2521232eaf12c8a956b5b0177a86b430e4557aa782be3719e3220a6ae1ef77fc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 584622281689, + "fee": 0, + "recipientId": "AXVz1XQf4pdPdXgyyU4VCFjsRRV3i9VrbW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220296a2e02be62e0a620e5b5b166757a449fe5c45829a0d41efad153710f94d065022075f637f41da115284df9e0a3782e73db04e044935a60a90715555949add9a470", + "id": "1f4180750e1f7c3f5c48fcb94104ab167e677eac601f7414f675c984e8dced44", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 585926998753, + "fee": 0, + "recipientId": "AeWy7BY7i384uzceaAbtxT7k7N1RkQeVK1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220296d7c9a6a71dc7d9803a3c95c67f69fc0aef05e2309dc3038611d28a712f8d5022050fdf931fddfbdfff9a36c4a554a7198c3ac011d462580f103b419b6f9a996db", + "id": "f8ac8625237e4d9d263547e716bbc276f536c7dc5612a9b102195f76744e0d78", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 587913213597, + "fee": 0, + "recipientId": "AKEyZbWJ6qKChGPXrq41cHZk4Jz8xHTqh8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100993c7c4f1c17412bc3f44f64181821c60a5532272d1f0436aaabda139274900f022010e72f7d9343ead3cc02fba0e111674f31c2cf573994415adeab4d664acbdcbb", + "id": "8691098d97c62a3d45c08b8193b7cdd5416518973eb699d713bd826f5b978fea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 589063195094, + "fee": 0, + "recipientId": "AeZ5u74dBxufywKh6eKBafLKBnWBX7F39G", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203f7a428270130c3cb5cc77cf307f8f87e3e99da03bd9c673805de883aff6b4f7022062a643143c9be2a68a3c49ba308a96d89920130f4e28bc688a687b05e5554ceb", + "id": "7ef826360e319242f436e15599caff7ab705747e617cbb8a2e0865b3821afe7c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 592519033621, + "fee": 0, + "recipientId": "AJQkUYcMuHxLho6Ag8yLiZjTcjhMXZ9aHB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c2f173878f73f99e2a044fcca1fe6d6e5f116c30a8153f0ede31f3c1963b19500220081480f6aba6fe8568d84a6ecb26c70a8b6e418c60aad5386148455d9d361b0c", + "id": "af49856b1c3f6a712330fced86bbb90b577150af1568b8fc64b024eee487daa4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 597999101241, + "fee": 0, + "recipientId": "AGoFy5eoJDS7wWbNpAwyDQbwGcWHF4iAe6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e0a578e2efa657e551cb1ec1fc7330b08ec8aefcbf4458f720d7ece9b04b655a0220065c046cf75908b41f1d540e33450ecc4506f838818d97240d3c256562bf1daa", + "id": "50a23397ce1b9f9af4bdae64c7658e7a98125d17e803340d310535c11aef57b1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 601400000000, + "fee": 0, + "recipientId": "AYqXPht63UU1HKGMYSTgmgPoZj33zKWAGV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201d214d26ae57304653dbf4deff3a2f4c2b5c0faa6451821a4aaf0851a66f77070220563a930f4833d36732cc2845a318e4d59cf77c2c7808eab9fa80440b6649c3c7", + "id": "545a75b3f45b8d3ceb447f8a573ad527853ea7cbfcce54c2ded8e8add84b1574", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 603323064588, + "fee": 0, + "recipientId": "APue7kzQNznGLbD361AwVKyfeqk66Gp8pb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202105a9341411c738f541af56b48370e5c670710f26375dc77c968e3a3d0306ab02204218af14c3f11038101be19628a0d34d7be466f0dbfc41518440498e23742b57", + "id": "ca525d14c00825ad6f0b1f41b8832ab154464f55adbefcdfcaa9158f195633cd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 604220262324, + "fee": 0, + "recipientId": "AamyoMmKy6vucKTsu13jYFnP4GeBy1RMum", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022032a24879fdefdebcb46b671a4dadf43e6f84af8b4d58646044a13d2dffc19720022052301e1eedcbb6116cc327c27ecf021e0e69345fc8b64d8864de1799356cb052", + "id": "5fc2c74fe023282f5b142ede55e47f0b218daed782c307d3f516b1707afd89ea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 636515419627, + "fee": 0, + "recipientId": "ALPyErixj2dXaoQf2aFKR5qRWrpQNSpVzW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210088327da183772be9161d481fc69fdfeee11f629481d8b564146f45709347775e02207bdac01a99707e5fd59cfdaaaf9aff15b0171bc5c4fd2a8a97695c5adae6e49f", + "id": "783b96a592c50b102e9e73a1d69279352e842cdd8dc7b967b2728f6f15031ac1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 639468721143, + "fee": 0, + "recipientId": "AWTTo6v3d5AwpVu7Z4zMTdzGt7nStpphgS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022046efb627b31d7f8834c4cb53547c86b346cc0a0c52b00d8bed26910909cb759e02206c6e2c5b6d5fdbe74e6a05006fcc9f2c1c0b2e3a488dc8f9baae9c1e4f9f2538", + "id": "b5be81aec8875f631918ea7e3471074c1ead97de2eb760b6367d43360f3d7917", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 649700000000, + "fee": 0, + "recipientId": "ARRLBH2goVLrow9EgBstBpS13mjmKzYwNA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210091648b7427049df79b49484ca0a5881a3b870354a1e74c38e56b83849d49fa43022010008dc0dac24a564eb2d82d51353602ff7d7cc0b01be101aba4e6bd8a3d5bb1", + "id": "f7d43309c24a58b257df1477766aafee1d1f4a41d4ee45a38fb874fa62517e29", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 650982673120, + "fee": 0, + "recipientId": "AeUoiRMhcgcw1BozjW2yesuRNsCaq4WzAt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e7623c82f69ca00ef2b0f988cdf38f53ef71067be47637c7db4da7f010a33ac60220429410215eaf06057c4f8c7d1da490340b0147a3d72be9ab9b1f4e136451cf98", + "id": "f3c675a4dab04c4a3f7437bf4264e97402d36f43bcfe84f5c2a874c089509cf4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 655486679871, + "fee": 0, + "recipientId": "AP7kuQYdDVNaxFpBy1RRZCtFJxEfjyafk8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220157e94b4569a8eb2cf78c543010cfabddffd8fddf12b800c5ae82a6a90605e95022041d4a41672b6f85bb8c2171a033906eea7043a35f377f2c1e07ae09c07906f53", + "id": "8e4a229041acf3eb033f4215adc26faf8bcdfdd1a8f351b1f97bfb96972c48ae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 667854242368, + "fee": 0, + "recipientId": "AXyLBiVC4v1vyXUwVCZywuGPgzGE6fwjfr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220117105ef4a93b506305144a883d16778f4d15fb5cc7cdc4624770e1e0976c1e802201ef7dfaf16ccfa96f2e408f78f7026377a65a59d1289608c47fd6c96f9df8730", + "id": "dd2a692bf311ad077ab9e0eb14d321fd4be930012b9ee7439cee939330063ce5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 668477464923, + "fee": 0, + "recipientId": "ARanutQKuQepLjbaHuTGh3yvFVzDbuoWj6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207bf98de6ba2b128320e753ee96a3740084cdb6aaa13b3452ea9c6dd3afbdb6ec02203a9c6e40cf99cc6694a504c913a3eda844cdcd0e89b594903d27774a78b96ce5", + "id": "a428541fc5455a8e86b42088317a82ec539ec88ee98e2b7016a8aed336046fe0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 669856703512, + "fee": 0, + "recipientId": "ASBZf3gbf2VLdkaFp8rkVRGjnCm89HRfEP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dfa8b07f91d5ac3309d0302898299f4695480d247e75cea9c316aead4eda1332022039a19ddb10f1b4b4471a6ae3af7cc70cc9a2fc5ba9f88da5cb14e25c1cef12a1", + "id": "3a0b61cf0227f3a14b973e4d991b3c892ca9c4b40f48c6b48dc0e12a70939126", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 683962013271, + "fee": 0, + "recipientId": "AM6uMWQkzps2vJqMMMMyEuZLNa8a9HetfJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100aed03caf6374a94b18218b1d6557cc6b24336e14a14932f1c3de017d61d969e3022040ae6f228c81d7ef90a5cd59d57844e7e712c981cfde43b4f7bf5fc590550995", + "id": "1ed84533e28f9b77cf94331c50903a74d10f648d4bb290eff722fafd5ad726b7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 685060228654, + "fee": 0, + "recipientId": "AMwuubQrccxV551wFW5oTmKWZhvbojTizD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204618ffb59c02a2106b27c71d14148d1932968b0c31490664911c343e96e5316c0220313e2e7f2387c05e242f162fbd94bbfaa3c991aadd853ebbe19c994095cc2464", + "id": "07c8587383bcb1fa6cc35ac3517ddbb3ccbaee82084ed71682c81a53db7b7995", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 688309516953, + "fee": 0, + "recipientId": "AS3HG6pG4cgtJBHiai6DrZfMdpUyTFPkB1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022019ddc6fe655b187a051a3d7a0575c8f3fe2b6ddaf74c69c8c6dc84a05e67d20b0220568dc2da3b1d3dbd1a712a4dc8e492500b475a08200bc70f62f73559a10d80ed", + "id": "0e1e914e5931d52145327aecf15e302d44dd38bf5c87756af8cb22a0cc1b8177", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 690823753611, + "fee": 0, + "recipientId": "AL5qEAPm6HBApbNTqVE3awEgzr65w8JZA1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e7ab0f12b99f73cdec3b34ee4b52d3987aaef73fab54b7aada17d807428cd0730220383d3191195bd2819afd54dc40bdc165c9271a201ecf57444c6ccea480800d1e", + "id": "cb4091df5629df4208f1132ad20d8beedac84714e5c73e52e8dd025a2d571278", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 690970768669, + "fee": 0, + "recipientId": "AMRtZ94wfCYmF9eAgK8345Hy9unPa1GKWE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022058163e796f8fac61be71bd7493ca8f729067086b0a850bf233a0f719ff7a4d8f0220126c32a7a178013b1c955dafa8ffc3ff7c785a41f9ed089fe0bdd5283ee2bbe4", + "id": "711bdc5c2a1da2fe4c488397f938dfd9569779581ad321f8f1471b2bc363e195", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 703958360199, + "fee": 0, + "recipientId": "ASCegDxvnqHPGYrUUS3YoX3fMC5B6WqYGx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fbcb790ad306e8b67dad036b7b02185dd3f40e92cde91c942ebf65976ecd98b6022033e4e5eed3dc65ed5264efff8a3f581107557fe0033fc6ef440e3a1622e4500d", + "id": "15c7116a20f1740fd4fa8c6e93c23b5149be45117d14c9c38c36684517914c32", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 706898356567, + "fee": 0, + "recipientId": "AYSpB9rSgnR9Cp9siWsdPct9dKKxbhBnnL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022000cf165386c43ac8d18e3304b5eb7d7a58bf1eccdffdefd3f5af84fceb94846c02202cfa34b89f205b55f7a8c90ba72bdb8698ad1abc18a87dd650fd41c348592f53", + "id": "be6736d3f90b290d9761ab4976c11397d23d0300cbb09a3d24f818c850c9fe68", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 716079847081, + "fee": 0, + "recipientId": "AGBi3EnW12LZUDcyybsqxbeqLzdxzxTsEV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220524fee405d41e04fedacc0e38c031c26ffbbb70cb273e53106382add945918430220549f961152cdbb902d342a57dff174cc6cd7d6b689d5aa49eac318311784b4a1", + "id": "a5a5a47040cb7f878d89cbd7b9e2d4da7f17b686f9f031b9f4ab8485373a467b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 726541178342, + "fee": 0, + "recipientId": "ANkCc7vjQS8g1jo5pKEvYKvrVbPTVmZxx8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220082613e1f512c2c74044df74ccfa0018f5066c27c1027911669e6a393094f22702204cc71514d9c0c72430d6b24dfa6d8e0ddc3e0d9e9877e5a45af7b66e06c77141", + "id": "bf09ad805c4fbe75d9e1dc6d17b319edf13885f5455094ce30e44c144a496ed2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 727897828829, + "fee": 0, + "recipientId": "AeX8N5GFDN6ZBqf3u3kXNxVHBYsz2jLg8v", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200198881de2264fc3c1921b214379e16bad210f1e3d62b289ec0714e4c47f51b8022013d911c9f57820450630bcb87670ea779e7ffed6bfd700f7be55fdbebfdef0c8", + "id": "83613475e1867eb2a82ecbb92db7506a2e50c0336514c42e48dd3868d5aeb6cc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 732204904378, + "fee": 0, + "recipientId": "AbzyK6ESfRUtaVWgiw79Qjx23QpEpBQdDY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022065140a29fa740ed9049d50e848a48398fc0976992611867e87e7fe0afc72e13402202bfab985f83e5ea588c2a15210e1f6751b263eae649c2eccaddffc07746a0a69", + "id": "03dc5b6869f88c387a1e29bc05e963c99e88a3861a8e6b215ea0c87852df252b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 733825657832, + "fee": 0, + "recipientId": "AX2fhE4ybwdvcWwnJKpcwsU2sHZAQqF9h6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022014e54a8d9f603cd2ffb1b589b11e26c1f74d0aa0c3c457b67fdf614ba46d1b1802200ecc6519cfb098463a51a3a4c8791d8cdfd7d0ba1a7841402c1c61c37df556a0", + "id": "57a89b3732488bdcab397c8c52e9cf56cfc18d316659388d49bd59b9b104be11", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 734634240646, + "fee": 0, + "recipientId": "AJdkWn2xq9mfJ1XbsvMmGWgzg1qXZcpaVr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022020e7d6b80e23a91b7ac3cf255416b0cd1ee4b8c99e7905f3f074e0389d41658002206e2056d8cd76a1017caf5657c0b2028f85e9769b8dffc3b55667f34e00b10187", + "id": "a6c0f4e9a9df5bd241fbddad25bb116624637bfc4aa4938c2167894d3b422d31", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 734928270761, + "fee": 0, + "recipientId": "AKBTUeWQRdFCE3Cs2tKVXdSSxFnm5m4dZL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022056a156e3715c7e280dc5ada73fec5140c0d7d58a36a9d5c2d191e141558cf22b02206f1a3b08eb3f4ac221438eb716232c205e5574753ddfe10c215dfab5961af2b4", + "id": "3fee18fd82e1b26fff178afc44338454f947dd5cbfcfb9b26e95786d7a852a36", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 734928270761, + "fee": 0, + "recipientId": "AYcACtdVz7kyrUu1RyfyYmnSxd4TKYCiAH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009173fbf35ca5887ba7e479349557a88564c45c0f9a2c84555ae04839fc96124d02206c2654536f9ff7f3192f2aa9ef23e612f4488263d77c342b81d0ff9638b59f93", + "id": "50743bc18dbf1b0b06b0b85da7831be155da905b66cb4c55061b2c87329b6857", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 734928270761, + "fee": 0, + "recipientId": "AJPBdGiVCiYb2Zo4ZWs4wQwGpiJxuPT6uC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022001bc153ee897c9a8088bd7d83db9b0beba950a777c79a3e19168a280c9a58929022028538f8aa4e9932b2a779622b99bdfedc92e16cc05aca8e835ed647a41419872", + "id": "aa2748fd0d2f735f8937046030459798b03ff7ace43ab7a6858a1d63cf38965d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 735075285817, + "fee": 0, + "recipientId": "AXe51e9NEuXQPFhY6faMfWJNAru1ccTJ3a", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022008f25a62cf8b062f8264aabdbdfd46a3b7d8ac5086031e0465afb0f9e30403870220207ae7d8f21ef03284ef94bcfa9d0d370dbc4e5b795b183decdd446f89b26fea", + "id": "0dbe3c763cdab7222f45d7062cf94bdd72a5ed8895fa55b268e2bc7626198c22", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 735075285817, + "fee": 0, + "recipientId": "AHPpn51yRx8UnegXydxpXiivh9xnuyshH9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203f1447733e52b6d8529e108e9a59259656da14f448adbeccd11cdf3770afed200220236c460323b60286ee1667c51bf1d8a61e7c7ccc88c90195241db585e886ee3f", + "id": "8e61c93cec0bc73a53710fd0d35fa0d4dff90a240513d20964782de96f256a86", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 735075285818, + "fee": 0, + "recipientId": "AKST4cpT1s7nnoP9fVYvyWSEvnKLz7vT7U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205559fddbf5dc0f93eef3713edaad47980b79f81dea309ec15dccfd4e04fb0d76022018c9a630b655ca36b9865b304ac7a256fce6282779092790079d6dc78fc4cbe3", + "id": "6ca20d3c6119bc42cf161f48f4d82c73f005d8e9e5f602df32fa5cc0bcefa9ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 735075285818, + "fee": 0, + "recipientId": "AGN9gWA67X1BxSdVNjDwuQrpJKr6waamAs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bbb6b489d4f1f55424f74280ab00615213d7c008718567eed1b849a66c9c0ce0022008129948f42b4be5837584c37bcc415d343c336d7a6988f1bc7ef69a698c188b", + "id": "031a5500249e80688c627180ecc8d3cb9cfa38c5ed42ea10d2624ecae5342fc9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 735075285818, + "fee": 0, + "recipientId": "ATHa3HMBEGDM4BQ3VcH1rAY6FdviPyDg32", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100948cf4bd55210d34820da658b900d4d4dfc4f2b538b9f8281189abf139fec2070220749e0a1a746c346b324aa393796276ea95f449dd11058d1857bba366fd228977", + "id": "ac676439da598343cd82aeaad1f306e6d1fa15d8db51bfaa967de14572af508f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 735075285820, + "fee": 0, + "recipientId": "AdezHrqbRQdooEMbLf5HEQjStMcWtKaUrs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a0265aa6bcc52dd3ce524eb0902b08a00139f01e8078412acb0d48021bd54100022061fd1f5fd3c0abfdbf11fe56932ed39ae9b25d468f9e9d2fe00a96b96b60e3ce", + "id": "e9d8c9795d3f32cbbd22b6811801dea2d4b99a3b5dd66013d8e1bf75a12fa573", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 743982782620, + "fee": 0, + "recipientId": "AMefnVvdW3xQzRnfy3RqzjpEjUayGp62cz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022046a419c537be06463ceef560a05950552f61b5429f7e2b4a2aa09ea2ad1d3d0f022038881350fc45a81a7c1c0ad407f258b100b87acb496b8ec6e6e62958468c55dc", + "id": "edd887ebf23a14ee72232666b61754f7835b4cfa4034f7294fc9bd9a30ac1f10", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 758597694964, + "fee": 0, + "recipientId": "Ae9YVNX8GAVUiVH4wA3PqeSMGTDTDeUfeC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022009cc850c616124e30c7a173d679234436dacba9fe466a501b2f645f01a59944d0220042cc6acd2cc25538201006a7a1df425d3ffce08a4408b1f5fdeb73d27d7cfa6", + "id": "ae6f8253bd2b564690008ff3ada0199ae1389457eba14431897332e6588e111a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 762146976638, + "fee": 0, + "recipientId": "AdScYdTwx5YAJSZNXhFfCXGqNpUZfgUAun", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207aecd31e853d981b83f6b4381a7e2d6152afcb06e08f7b001ad88200aee9d33f022018b9af36be0290056ded2c835c80902c824826ec0955e53cea50f4f153fc62e2", + "id": "62c9ac8c0b9bc3590849da10c55d14ed79af7a11aed9c88d4820c043655d8d61", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 764781255703, + "fee": 0, + "recipientId": "AazxWnociyHVd8hBNgCxPqAepYe52psryF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207fa864ffe01d95a14e23efbb66ea9a22ec47b0cecdb564c8842a6c0f0cceadf4022054ff2c650fbd053bf2b022422e49977f6bc4fe147834dca0198d5c87f39169b7", + "id": "3684f2c7ebac624519c3e3fa1751718e07c66bb695649cf049238506d3866e15", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 767271583336, + "fee": 0, + "recipientId": "AKBgENszA84HHwQg1eKG7t5KnU17WLUkDN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022015ab4dce082428342f964b9372d37588cfb5be5f916c8c936eca4d7a4457ad440220632c804557c94797efb8d041f94561b3126d0ce2105f1f51f038b49ebe387a8e", + "id": "a7d10521629488ad012d27edb9083ad6dead0b3a401089fe34401af2da0a847d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 772911580976, + "fee": 0, + "recipientId": "Aai89ucFnPch8ZHFvt5NJ6qBZKACJVYTeP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204335c3aa664c18916e6ca785fd3c89f76b790e77511df34ffdaf5094322dc0a002204506ffc0e8c85e5a68417b78c3c231ce5c48a8a1dc417f1962bf65d6a1b5da7d", + "id": "773b21c1fca3bd52438fb17f97403e5f26aeb20693c591ee9d4e9e3ffa748be1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 772911580976, + "fee": 0, + "recipientId": "AcBCaUbM8cS4sp6s1uXELKv3CESzCD5XX8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e01f0a0d0194e905f33d5189eb1f46da9ec86cd2520af288d3a88bdb3096d54202200f1285e4b0a6b4d3036e1fb85637725532a39e4cdffd51c324af87d4d30d0a3e", + "id": "2465e6d3f6b17da590b6f53136e4af7f893b9178bdfd0f7b5cee9f7c8ef816f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 780058983676, + "fee": 0, + "recipientId": "AcuUUQ4hUm4wfob8z2dy2fgzVjMBYBL7CE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022015bf2469739a014eb0002e02a27cb9873058c6ce26bcc30b491dd1c2a783a285022027942002fec0c30c3f73859f03d0b22d6e6b9aa8f2b4f996fe440ba00920e643", + "id": "1efca8a1ea9b00029fc970c5680d14f2aaff245fa0757ad0f1853d2c2b2e7053", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 783225207027, + "fee": 0, + "recipientId": "Abj2DUoYZAET4WYB73sTyr6udaGA24QSaF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207d994f0790b2279a417323e51fabe58f9406f3306b21846d986a3f7146bb2dc40220246635f1f8a5d028f72d6117a7a743410fc596e89722121f94f8643dde175495", + "id": "cafe13900519e38c9e814edf229a8a07f9f521ecedfeb8f2812d9c8a94aa7f4f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 792264143054, + "fee": 0, + "recipientId": "AH6gRrXAUiWeyi79vjBvysTKJHCny88jpo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bfabfef46710cdcc59a2223e554e3e7cc416890cb9d2aa3ea408724bdd11d08a02205aed7c99e38a0b4ae8991c7daf53da01ba8652434d589aab0122d7c043dff201", + "id": "26c101b6192346f37ee639f28da5b53ff81ecb7ac8802389ea6ae9cc9571fc2d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 795355926543, + "fee": 0, + "recipientId": "AL9EsuL167UcKpjH2Cco6zqiFena8LoURK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220601bcb86301d0406afb27b864ff246c6bbfb8e6668be9bdd87dde634cd44b1f4022070ecc8458fed79816df9f30790a903d99ce41bd765cfc92421cf6070c7e6961a", + "id": "b745abc6ac90e5123dd8235aa5b19cf96e2419a8b7710ba03828a33dc796fb11", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 800000000000, + "fee": 0, + "recipientId": "ALgKXQivLiRjcmchi1vaRBvZDPpYcMNv3K", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009ad6dfefc8a08f6337c0fb9283a07fca5b24dda8145dbb06644284a7f91a0aba0220730b2b1411070cc0f9cb67b09d8d4c897a2dd7fdfd49523ab3b9d4f759c9803c", + "id": "fde379bb0f8057769553fbf9c7e49b62c37f33999696b0a426d6df0f1198d7e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 800807608813, + "fee": 0, + "recipientId": "ASNbbDUUV537CWVyJhW4nEBaoqphsfdGoF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205c5f5fbe87eb844f435035cee3b081a0594c30581165ab0c5ee5eede125c99b702202db7822920671f90a6bc5ef5056b6c91599f7f48b1441ce679e8aa6b0ca53e19", + "id": "0d34c831223debbc92140029644e84dab7664c4af7cb70acd1271b023df5777d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 807327846398, + "fee": 0, + "recipientId": "AaJkUNWfyrzaF8VeoujbCUB96bncRBxJwb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204b973689c3730f93177aae4292752724a4ae4f313a98f70b8602a63d2fcd7898022061fc16b87f2e06b4f2adb56ea4e15de9ee8858b079b7783c749575c6fc8e647c", + "id": "784dabe70b09163d177db73a00dcdc56ec59620d25ea31e271fe15a479c31f35", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 817711112949, + "fee": 0, + "recipientId": "AY25aryyTvvZUjDtbG53zbpigrenCBXbEd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e23b475b7f57825c23c18dd4adc239468a8d185f43f5fc35b815f8d9bc04d03d02205e27da5cec444f1fa6941ac1b28c6536b5c4e56c9908099aeedea3af4c8943bc", + "id": "50fd6a37dba73576473f5d47c3a08ae07e39b1c5451d50fdcf5e9d3c1469a521", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 817938318037, + "fee": 0, + "recipientId": "AGTFXFtkqYmcG6wZowdinGwb9K56rWbSjH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c8908d3537c932ca3d4de3b28d09da5561604463bd531024a992be58de1802ae02202b07302aeb41d0a3fe01a7a519a0b19e14d726918dac560e79d575e5c68412f9", + "id": "9352eb84bca23a8932cba246fca96482c36b3c55cebf9545ab3260968c223b04", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 828092442164, + "fee": 0, + "recipientId": "AHNQc1eUKgSJWr7BiopwP3fCLm72NRGFnu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022078d3cd96dfe78a40223df2d36967e46708c8ed21dd1e098e5994c9832b2bef1602200ac6d2c0ba0763740046aae684d542a3332ceaa98cd2d62d0217a75c57411e54", + "id": "190407bed322d0dc2626669ba6de2627c6ed18f86d158bdc4e3d24e4e49bbcdf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 833532097131, + "fee": 0, + "recipientId": "AHSytS1gD6vMjHtcpkQ86V3tTLVbVy4UV7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a35919ee25620b1cbf5f00d83d28329563805282f1de3c6a14c3f3718d5fb671022003b1c6755cc0d0f7451fc3c16e41cf2b5a771c4596063a2a3458544bf927dfd4", + "id": "c36f7006aa4ada2eda35951c1d8e88194598959783880832ebfe42357baa5661", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 838975285818, + "fee": 0, + "recipientId": "Accz8foPxAgb53QZq55r2J3MHyhFJhWjyN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d38fbd8b4e808a943dc3ed39967191c50ab52d5049bcb95f8c7896232ad9a25102204ce41e68a375a7a77d12ac92c4eb5a32a87dd2646d481b4dd6a4fd9b28e0a8f4", + "id": "677b8dd3b694d5ca4f4a2d890d99038f0d3529372ca65f21ad0669eb2a703d4b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 843238131545, + "fee": 0, + "recipientId": "AVWJBw2eQiWU9dhYYiPZk6YDKgT8zWuJpr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100908c7959593e47247fcc045d6c5d14798668266960532130f43c8f03ee9e4e3802205da61bf5c85eaafea399d9a0f6ba0002baa6f011fc6c10d0367197768d5fc7f1", + "id": "442e380e1881f46f13924593a158c58ab704e76686258a9e1b33b645c9f1913e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 861005612452, + "fee": 0, + "recipientId": "APLNgrLNdVJUcAg8n79DRo5d8krXTS9jBP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210082d6450fb65d0734f8c6d7d7ddab2438055b37ba420a3283b73b7dd236cc855602203663d2b76b2c2e681e78f016bf74a300587259281d73be10a8ae1841db720bbd", + "id": "53462cdbd50f84cc911007ef46be4622b31fecc18415d27f7937b5c36a9c1c38", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 867237104113, + "fee": 0, + "recipientId": "ANnnZMq2TBZhCoQkrXtsuhKYVvX7ii5RKG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220054a3b0e8eb109670e0fb452e1e58b8b82048aaa531c4238a011ade0f3c7662d0220711ad6eeeaf8888a8774cd572606e7ba0613d737d3255367a96542b95181d5b6", + "id": "e6c1a70720897231182fbac0ff131552e938891ca01be2fcd96afdf3ce2079b1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 868945860370, + "fee": 0, + "recipientId": "AdWni6sVGmYL1iWEeVAt5erYacKrNE6CHY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022042ef637e7595b772dc0f2c6edd1d1cdf05decd33f67e595c94abc1357baab2c702204e8bd0a77224dd1bf329ead41f7d03ab79b61f3bbb4a607d9b1ec06978fe59c3", + "id": "93ea865c088bdf14d07332d702448dc31dceeed8f265334e3df8b859f06a048b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 871419919728, + "fee": 0, + "recipientId": "ALBGoFokLTX1VeswH5LRBtk3uEUitoYy5U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022036757f788fe0baf560512809ee624ea8efa84ec50dc22f8c8d14bcf6daaae4c7022011760ffdfd5f5c283ae6561b017036027b62b70a1281bcfb7cf9fee718400800", + "id": "75dd4fc72a01bbd3b6faaf4adf4fdea8c88bb2a6708d15f124005079cb9cfc2f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 875407840383, + "fee": 0, + "recipientId": "AStpm2Vziqqgtm4Tc3xw8poVh9XUBGpode", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206fd3e896284765a4a81713111ed9bfac25e977e14b70b2db0febea67d8e14893022055d0b6b689b94312aa2f360f5930c43341499b6551f74cee2d2469527f5699e8", + "id": "85f1956ed442417404c08f274e8b70f65b139a35ce77b85bea78f9b913e4e4f6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 877995100899, + "fee": 0, + "recipientId": "AYGjRAJcBQokKnpxnEWvxsLYohpa3R22n9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220563f3bacdff499019a348d4c9feb8c965c3aa80385328d1ae7fe2d1abd7b133a022078402ff3128df4b1bd885b3abf5d79c3318d6255ef03f6aa2c4148b323569a7f", + "id": "92ab5e8f2f16a9e0b3e8a127101142184a3f2da044a47bc9ff72da0afdd5c0aa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 879811609594, + "fee": 0, + "recipientId": "AN3odZjMnK4NY6oHHG5hKPySNbH6DvfcyA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022076f3867eb0610cfb8c5ce5be956ac5b2c37561fdd58525db87c5c89e26af7c0002203635be4d862d33fdca979630ae2f08f43a8375f35632af2ba59b148045bf1f24", + "id": "ad3ab65ab8c782d704ebcb4f1ad69d54a225ca2e669aaaac22724b946b244508", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 881943327924, + "fee": 0, + "recipientId": "AdEnEMDqFd9FGjuTAdcLgVeSYB4Nroj5qR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022022e4869dc0dba943de4d58bbad7c4252e5a7e29a4cc415940ec3ec947b4f427d02204dd4c32404556aa475eb6eee346ca5b408e6541d7078cb03af4c8acf72ce87b2", + "id": "7deba86a243c8bcf2c222986ba4fed01192817061e1721c1d42e55b04678c85d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 882010152950, + "fee": 0, + "recipientId": "AUNsfAkq6Pcqeew41Qb7fMMTtsjKMfLuBU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203abb628561fafe13dcce54fb723168690a9a9e766bd4ec3a250a5b496abdfb33022017a450e3a2f31be397e2696b79535dc956c2faa59ed503ac81d57fd8c3078c23", + "id": "55dd0e380d2c6b884a719461ceb712f3c6ee0518841ba10f81f1f3c3cd044907", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 882090342981, + "fee": 0, + "recipientId": "AYxtKAvHGgKmjAyszFLeeUdrBrcx2T4HMj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202dbbb29509d4944ae73a2957f94bc821480f99552f77b7b2576b63703832fe9902207c65af1e634a4f3f2140146658865b577d8fd0ab88563d943ac6ab9f5c2082d8", + "id": "ae02e0d07ada325ac33ee462c57bbfe4b175943bad966e7930af9b51a6a83888", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 882090342981, + "fee": 0, + "recipientId": "AQ9dsnRjod2zb1jYmQouSeqMECCjczHTe4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204ca7df8131d2dbae7148bb7d600fd2ae9504838ae6e08a36ce5038898dface6302203016472420df3880b64e341b28ae44d9ba50a218a1be088692613fa69ab95245", + "id": "ccce88fed70924e2c19f5ce8ccde69f7c74377cbb3f698fb678dd6455cb74ae6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 895174683068, + "fee": 0, + "recipientId": "AX2ZAQGv5aZfuB7uEMrKVBJixqGjaD7QTk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204000a219545ece7dfcabaf8d2bec29a6ac6d360046298e81f1fa847dc260bec9022004193aacae42218212effa3649334b8e5e865ca8018f4cc10a704e0bf464aac0", + "id": "fe23b603d883fcb3e0f2dcbd203a338e6402e95abd9c2d32ff36352b707ce12c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 895341745634, + "fee": 0, + "recipientId": "AKgTzN7RNMLvv4S8QzRHqvtP5Kx4KFeufC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210086703eb4f783d915efde3bc0521c44c2c1fa7f09ef0fa0cce1b83c0567e55ea102203402af173407f63130b753f8e503acf6ab9272d4cf540ca2da5f76c5da2704a8", + "id": "e61c6ec80612d5006059f9551b9f34e928aeb6424694f773c56500bf2037e776", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 896791848697, + "fee": 0, + "recipientId": "ARyJArFNkcQUDDpQdLGL47BF4ZVXJ3y3fm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022053fde5e97462ffe6d69adc37b4923118269fd9d858ecbca3b7ec8f9fd88e1cde022047da7a3a640d20856f1f267e2f06178d1ffefee2177e8a15b2957f24f83cfa0b", + "id": "45eed794987ba92e4d579a7aa21bf890bda28e3028a3139be7a4bae6ef232ac3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 900000000000, + "fee": 0, + "recipientId": "AKJn3FRweuCdDcNkjSGH36vXb5M8ZBjbcT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009e08602bf0177bd5ff9b95c0f173333563073c193eec6574a25b8a4c7453a15b0220778701ddbc1a30c3b0366dd7d7ad654b0066e4db8f8a884a668425c95dc57028", + "id": "5d3beece4057da03840834c3035f4b580872492de87afacbe44d540ed75453be", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 903190511125, + "fee": 0, + "recipientId": "ANN62YesPBDVEsW12iTwfwRaxhgTQGC6B8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cd15bb91ab56975fb578938b0ba8ec406c684015cc4a84386d93a1fd5660ab18022017b0b3c53b8d834a2a830676ccfcf346d21610f55e96c25eec771585d6993c87", + "id": "f7b3f239d399a69d954994d12753be50a09d71241516f3bc49dcc6fcdefb7d44", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 912865546892, + "fee": 0, + "recipientId": "ARuGpfJJNMi3BFFbsgS5BeHU2c5ASxcE3x", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d232047da315e33fe38b625c3bebad7400b03cbbd0b046aa0c38e972d5843f20220261e30aa301f0f4956d1f95cf3e4ffc50f0a6d6dcb2f0175e6e589704eb88023", + "id": "e2c3a809125902671f2e0242d8cbafaed3fcbe90c659a782e9de638bca44ef44", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 913029661747, + "fee": 0, + "recipientId": "ASycZbKLnRNbYdYgek8YJQzvyNFwjcQGAt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b27e1b308e009e6cdb6ebbe5b5ad0b81140bfb107e13ee7b044cf93d3152fd8802207b01d59ad8c5f909c37114ac86e763ad66c5398601d99fb1338e5944cd8810fb", + "id": "0a2f7813b32d7506cd8f633d7938b81c89d8aea083e830c65790ebaf4c6e7795", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 916154978619, + "fee": 0, + "recipientId": "APepu8ruATApkWtKxJhiU5CPYcFMfH5tfu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dba1440e8f6a7a3d4796995d180099e8abef17fff1f9d1c5d8c69cb3132cd1c10220351bb3f699b4dd023ad2ab75172a2c2f4d0854fd554ae437d11b3bd573f8aef8", + "id": "92f849292f6fc6ccb43b4f0feb73d9a875024a8aa9b32e4b4023936a71e2ce95", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 920325036170, + "fee": 0, + "recipientId": "AaDbKSFY6xA2UJgeBSE8r6n8wX65Py1feQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ded1448f1d4b184b39b787c38e550308b2e458d19ffd23b403c8d8c836cbdce1022002d021095c60fbe111946539cbefa09e5202a7261d44df441cd1996c01e3e2d8", + "id": "879e49379ae26a5f8120f6a4535400fb01357eee8b4aae9322d51e149fb9a227", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 921784408416, + "fee": 0, + "recipientId": "AJGxTV9yTgqZWNiXifF9JM8U2vdPssLyEz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201d722c68e0a469d9bfc0a67bf0de5c63c4a969b2942c30cbab883fccff7e733e02202d9cf49d2fee94fb552e356cc09291a1a0cefd9fa5ca98af311a951433c6b3f0", + "id": "8e0a8082e102ee10405d32ba82988839770f5f8a4736b556ca64ea6b674d90a0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 922643224501, + "fee": 0, + "recipientId": "AbsrMsgKck1shqR8NwDm1ntDApWxGSZW3e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009cd6e7355be59b5624201572983708b730e673c00b779c20273d94d4c34e9e4802202685f9d0798d3e167b787234885618a79d7f9967adc21786c7b127edaab39687", + "id": "59d8c7f34718a9845f8c670513b377e70b9f988c86007aad583f6318680be534", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 928155414454, + "fee": 0, + "recipientId": "Aaj14mdqcmAtZH7LnQqdWT3qf9BFmyR7hG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d10065682321cd83ecf696a90d06022a557d0e6f64864be26aca6d3e7958348d02201eb4018574d93c83eb706107bf2108a5f48529782fc2b03a4c55482f49cc4ba7", + "id": "65e3b5466ee3e56a4646686a8bed5a279368eea489d49ef0e093d3c6848a8ddf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 932790490195, + "fee": 0, + "recipientId": "AdQDEejPjoMpLg8cMDPJV7tyG2zLQmuDjg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e668f456c6c608cae80c8c429f20cd6b38690f069c232a8d8ecb57def6986579022015f6758e04f379ff651fa51c06ae5b9f64b48203dead52a732f08d88fd5c7ac0", + "id": "f14cf4caeb5874d8bd03184939da80ee11ff146580438247ecb81af6dc116176", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 939660273449, + "fee": 0, + "recipientId": "AdGeDcDSCeYBfS2DA5Dd9VZ5SwvgmAgCz4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100de0fa78884cbade7879b174697493b0d54e6810a8a5b1e9675b04d5a98668fe30220520492f5cf1c91d22bfe2d63edec163d25a09ab5c74338f20f3dc1ccb3a80e0e", + "id": "e11a97c65477cb5de932daf09eb0f377e14cb233813ddb680ffc030661dcd44b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 940453697923, + "fee": 0, + "recipientId": "AbfshU5wgXcrhfWNDjrkrvN52y3EScpg5V", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206cc11c0bf89c528f9efc84688be77e19339a847328790b7cdd407bfbbebfcdf8022057b0752aab9c9592f2387078df66d2ac5e40e49928df72a53b769b942cc40c1b", + "id": "1442d74ed4130eb43a84c53baa8ae6db3fc088d2ac900423f8b69953aa61a84b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 944039590123, + "fee": 0, + "recipientId": "AKj53MkBzDfkDf1SLXyMf5DnkC7Ra9cqug", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201edebd2222f9e616ed293c9c55f35c7cdfd6f0b4e739a588d912dc9761fe4dba02205b7b844d19d075b195a5cd94799d40c3292ca05f0b3bfdb4736739b7869e9d73", + "id": "8f981304ad1db95e27e2fca8bee4e51a421d0c8a406921d2e8aea3318e45fe9f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 944903185065, + "fee": 0, + "recipientId": "AUTtCwMqXuWDskRBVkdvDjCdMPz21Qqn1B", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220455ff666f653f5e990e60a35c440270b625b844ac3154e4d1a959e490fa11a1502203980c454fd84512adb7659279c031f72b23438cb3d5055238b34966837337856", + "id": "1741b8faaca3c787f7abc73fb5a926f1669b4c7ad363b2ff8db9c04f084b3f07", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 954261371043, + "fee": 0, + "recipientId": "ASdMtEgrbnpJzNg8ZbcA9wec7QcvgXBio7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202a01b72970b65264a10f17e18db74bff086e4908e01ea391baea9f67528abb4a02200a63fe887e4aff1fcbe7235e72d58a6ca6cef97e3f0d7c89f7792afdba1cd41c", + "id": "cef4106da76091c4ea826c2f1cd235a182f20634765bf8f6183a3c767fe36e55", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 956921007076, + "fee": 0, + "recipientId": "AU3A2mNv21o4SkKrPjjumxAMSx9dNSJkbM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009fb21352f67025aa234098c53b941d99b9afd9acd26e9a66fa08209561bacba0022067f9ec9f3b46d62bb5c30915f80b0da84b9efa71e6b9f357efc7089827803c40", + "id": "6f0e865e6a5d3b5564ebcc4f2e2c0aa6618418fa561284db9780fef7bf553f5b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 964800000000, + "fee": 0, + "recipientId": "AXUg9JM1fE7XbUPCsDeC3UxXCMv4tx8bgv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206fc92484602f29f7b50a5ffc503c5d1a231dc0e54802d9aeb373ff327602325002204efccab14943708ca58cbf02824c6d1590dc1090bc0470bbfa7760f14fa629d8", + "id": "a059a31470b9a196a3894a864cd22eb0c92e159e776c7406f20cb92db7a54dc0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 967490707524, + "fee": 0, + "recipientId": "ASvJ5NScpJtLy2WQV6AyBtzh5FzKxP5N4H", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100873f68bcdf9688710966b018209079b9a4ebea3c7b49e281b220391537b5b6a7022033d3d9d2463907307693dcfdc80edf4b864cb92cab31bf89ccc3c260ba72b91b", + "id": "c996d17aeb1aebda108edbd8af91fae13aad80b959a5f6cd1b2f8152cf4e829b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 969700000000, + "fee": 0, + "recipientId": "AQinQgE4vAbgTXaUKCj1QabiQbpsAT8fzD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022041619c75759c53ae76620fabf4c133b4bc2b8cf890f3c574a4ac706436a6e84e02204386f909f08ca380e03cc52330fa553bae5bc73ff9cfc5dbef030be47a4b934a", + "id": "ae59ee72e6a0f70f2a9ec115af54c17664b2515e5af10f3c66ae0ec5687fe005", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 984780889208, + "fee": 0, + "recipientId": "ATunEsg5YC1zoyYSuRap3zJyWmpzQkz9tT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f8ceeea56b0a689d42103fafd6a58cd236a14183201ede721f666309aa66769302204c345a794bd8c7aa04e3e734bbdac62cb1606cb1375ac66d4a405c9bf515c5fc", + "id": "5711b79607001f91ae6452e19d06b65a23310ffa4ad08acc8a30b24ee67bc3ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 993823555585, + "fee": 0, + "recipientId": "AWUKR4R5oo99dRkwRkvTvTJ6ffYBNv7zPF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eec0a2a2274589c7b1daa03e60382327c2a429daf422815f969a16a2cbe3bcab02201c2cc1ea66118960117cc6c00d5cbabb878e48783e37086bb7fe0e3ad3acb30a", + "id": "a71624227e6daceb049d5ae4cb83a6a6e9a04659f29e53a1fe30f4746f3dc194", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 998232238139, + "fee": 0, + "recipientId": "Ada2uLW2F4xsEVqmYhvk2y1gnjbQbeEGny", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008bcccaa6dc17a49848a90005e91d25cc5a2f3c50af365fb9e84bbd18f8cf19530220582fbdc99507556d418996f4aec523965f88c88efc5248d779526deff09b1460", + "id": "bd15a7f964c5ccfeeff073d396ccb099886e5bd562af5b66717f93dbb20e9bf5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1000000000000, + "fee": 0, + "recipientId": "AV5UwZD1wxVNcDjjXi4K136dBPjT8BUSMp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022034080e7de43853b75340e97ad6a2823b8d08290c7050fb0b4e7be311d95067c0022035815502cfbadf9851555f9bf30b82544aacfa1c5eccac34377d86520b83d873", + "id": "494e41b53f142601472c50a49c966c2af768701a283d28dac2b09492b902f9f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1001660153985, + "fee": 0, + "recipientId": "AKmat4frPHmBjrfwuMCk4uBRTPp2zAznfu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203cd5409d500d5b5fbf817476f4ea709e7e09cdddcb93ebd2a1dd7cec3f46dd0f022051cb3f390cf2d0d9162d5409aded948a48fa466b25186498de1e35a0d360ea24", + "id": "6a948937670927c06e5c1358667bb2f7611dd7387a405bc429b25f43e4e56827", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1004564574086, + "fee": 0, + "recipientId": "AHnrwSwuuXKTxxL41xgqAwAjHBc7nsYCDU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d73919bc931145f7dc02745832964a7cf2091a3540eacc25569f6fad1c954fd002202ca78633ac126f1a38818b7712572f0b17887e187dddfcfceaa4c92521b4c80e", + "id": "18f0983e59f657e62c56a4b3257d3a14cbbc405b4066f20d687852162c449a43", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1017708933348, + "fee": 0, + "recipientId": "AJnv4qX1hnuecj66hRM9mgLXfYUdEqWr8p", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022012492f457272b7d233a1324f49e7b1e04fcd80d555d91a05b1392b6559b62dcd0220530e2dab22b207a45dfeed314678a22822eb0118715ef7a87fb7bcdaf97cbb93", + "id": "5caa6ca9681dd44dc6bc566caf1289fa243e6448d9d9c5d19200800341f52dea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1022309295002, + "fee": 0, + "recipientId": "AaZ88ShGS4izM2RAjdRb1v2XBDjeWNRiCS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022022def44bc8637552e51371d00a3a39b5cd77cbbbf0166ad4f3431933ac94181f022055884070dc5ca41b51cfe9c8f363b03209c76b97c6dbd02476fe94397c1e4bee", + "id": "c09ea42b2ef6849ce9724fcb6e560e83c211df8c31bba0eb5853c1dbdfc2c9ff", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1024417009424, + "fee": 0, + "recipientId": "AXdzLoCsCHkLrhLGW7SbqQF7ycQMdHj5CW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008b4844b7dd8265790126f46b21ac699ac941d6b9e657ac3efc52ed7c874535e102204c3ac47d179cd180370993b8e7d2e322b584e3848b2d7a0ecb86f8f74169a08f", + "id": "3ad1bc54683cb14a70774636d880c11c2aa3a236ee3af42c524dfec3dbc7bd20", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1026165099001, + "fee": 0, + "recipientId": "Ab5PAShY5CX52trT6YmEB8Xu25t5tM5HjJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f74a6839a13eb8a3df1b72407585ecddf0893ed4fa3ee32bc30a66f7c49c609a022051dcb39162f3aae1ba1269a4496eb62db24ed3c176e488419bf19250fef3680f", + "id": "fa7af6fc72bd13847f83a7e5b0a9c689315429725a6b6f87101f9f5210a67330", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1027972402698, + "fee": 0, + "recipientId": "AXk5qyz8GftcgVTEzUDhpfWZL52ZJMcN4G", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206a1ff97dae426570695a4585f2e9e5734c742bb8be2981a757890cf99a2d9fe402200f7e67d7241d9fbef45f4c0f51f7c86827649cb337ce8a902f242d6367b48ba7", + "id": "ace052f0c133b5875489f3a7397b48ade06a5ea9465c0860a290b3d34a5f927c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1028958385088, + "fee": 0, + "recipientId": "AK5NYn7AJdvYWy3DyESmvQb2SDf7PxRa5J", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009fb2a2c3952a7df061783e1f6acc7d7ebb1db208f242c9db13e82e0ae515eb8e022078dcba5d7c2487202d4a950b66cb3ba4e4cc8718bac248605405ada6131e617e", + "id": "e1230c88023ca1225588bd7a9dd7ddf53bfc6f93c0610c3dfdccf630f943ac09", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1029105400145, + "fee": 0, + "recipientId": "ARAkS27JzjnmAGTBPMQmKJgSwCgD6SNUDi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e2fc6163e74ec9cb17eeda9bf171e5b4789200831d5ab73aa00356cb911f884102204a9b214977eedc6106cb9b6c416ee8a14d000e854be082764d65109f88ec2206", + "id": "6c8816c6f0b88e43ad7b4b790bcb539d7be48a61a830b9f4d32ecec9c595552d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1029105400145, + "fee": 0, + "recipientId": "Ac78t9XdQCAVc3aQVwrvB8HTUqSoDqiHAq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bb701ab9fe8c4f9dc7b1cefa260c8934b2bc4c2aad31fd607ded1c768526aeb902203b1b477789a7e8a69c02fca7bb5a8d348785a975e097b4e6cea3f2515d68ed82", + "id": "5005846db55b1f7f32d13b95ec2ed595dc8927902d1650fe366356fb6cdaa5fa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1029518225860, + "fee": 0, + "recipientId": "AWPoYy3qsD94b5k2kbNuvUzZVh9SnQjBsa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200c1463b83dac90ff2ce43bf2743c30616939b4a9a1c3dbee8ce27ab1015ef3bf02203c474948d5240127b76ea1f19ecbfff55033dcb3257faf741c0d73c0c4fbdf83", + "id": "fa84fe86327ff3ff43663f888df8c167f5567aca4f61c02157a7c4403af4f29a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1030548774634, + "fee": 0, + "recipientId": "AdPywJjNoHDnCzYua5irL8ShvUx9KSHnc5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202199eca47200eb76742b28d1b0767359f588e9c2e6b19405a40ee32816cae9a402204d9b9980c2cf193f6cc38e6ca8796b0e192b055bec5b0c52eeb31b95da466bdd", + "id": "c65d4ec488a9d907cdbadcb865f4732ee6a0ea2edb8baeafa1e7e76c37237c2a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1031249725970, + "fee": 0, + "recipientId": "ANLRw9k91KW3snAC3NCPhhmzgg9cMgdg6j", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022052dd06e5866b27fe253f1b64dae8d3e1924bdd4666062d7e9384bf77cad2b86102201cea424f053cd156ac65ffbeb703d245b14bb2ba0575aa90d9613a7e8fe4a7c5", + "id": "5c7bcfce2963fff9eb502e79dcef563274a4c409a93334c3937c3019cbf55185", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1039396454147, + "fee": 0, + "recipientId": "AaAUZ1yWFqde5BYXbbUEWFB9FGGpQCcScK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008bf00e9e9954748def806ae413bc38629058e7c2daf5f56c34991b81a61fb7e0022015c587a5adb9e7cc11e4da7f2b6e5f1bc1e8f4f36c99a3fc15fef28390dd2c46", + "id": "7dbbdba5101c67507ff51e5375318d0aa8a9e3f25de019dd16af2fb62077d6d7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1040719589660, + "fee": 0, + "recipientId": "ALHQ1oorGCEvSPsFNWdYPwRyk3rVvMj2B6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204a48b4446d976bce24fe26d4e666947e59567c29d55254c4a4d986ed2e0af1cb02203147efd709c8bb850861b19c71997a5780e606889ccabddb70ea4f5beebcffeb", + "id": "b06a6b4aa2f8d71c39b5ab05f1535e521cea4d1a6cd304d56fcb02518e5d37ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1041975741930, + "fee": 0, + "recipientId": "AZVgPvHxJNtfVaG2YkJn7zPdFdUtk3jL53", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206c33c5318130b31313a7431a249900519d511ee8f2a43986ee37ca6d534aeca802204b833d1bd726a50e0a76edd62600031e215239c7e823c556e53aa225cf5d0691", + "id": "f9bf139b97f3dee7e627fab2bd00ad2ee7b2ca61ce259c8a9da4c0e33f8e6680", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1049727197915, + "fee": 0, + "recipientId": "AQrP3AzVXbRbps1UzCfjDFoCCnc1f19a5o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220333ec0cb3e0cdf420ae2a89997b32607a748333fde1bd62becec1e8231b5a98d02201a424700aba7467ea4141d1d847156b83da94c26acb464cef725f4f8ab36f651", + "id": "69baefe519ef9cf9c530931b69908efef4ba290f89a014f2fe7c93f7d4f4d3da", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1078361899572, + "fee": 0, + "recipientId": "AXhwWcy8HhMYLbiWhUKnZPw28o6HfGJjaN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008ebdc6ea8c2c50965088daffa9b2ec09f1a4548729284ba4aa2e9adf38cb82510220524ce025795ef5cfb044667870375c205818ba4da875e0c3cc4ad6283673d884", + "id": "fbcced1418f85f3aeb54a66bceb7bfee00708ba6eeaa3d5b7aa85cc841a14536", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1102465913669, + "fee": 0, + "recipientId": "ANMuAyS1SD2CfJvuZSi6RQb261gvFSJt1X", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205e2895cfc0f9708d2af9c6008451ffa63461201ccebd7037c81196198f3c25a00220151c81744a95f78670811a8b66995a0f2229be460d5dfd712a7e744f5d56b706", + "id": "9c19d49856030d283b8772771d575a16c5356413501e2acd042835873fc02cbf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1102759943784, + "fee": 0, + "recipientId": "AVVhvtdbiqHm6zuFnUYbCQCsgfKs7GxJxS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a5a5702c73261b34e2ebb67471524f509588fa51756b267c861b3cc8737c9b6e02204322f7c837883abeaa450b02e6c51e81c90c5a0d41196f046dbdbe0827e1de63", + "id": "734294bf0236a11b079ee873079c7c4c9f1b85a53750c0cd97fedcb5cdad0074", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1108427216593, + "fee": 0, + "recipientId": "AYnxyQgq1j5i7h7AEd1UcYVWeZhip2BPsy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e3c2842f21cc80db48cc3a7fda70e84cf3fed43e68ec7486d2b6316bf94e85f2022040fb1c566bb45142bccea742c68cab0d9093a823c43d623ac6038062393cf171", + "id": "81ba05c7bfc196047ec8955ddb0ee07ab51176ed9884beac7e59d49e117e7bae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1109838235948, + "fee": 0, + "recipientId": "AJvzuyNaWbYyC8UqDu7hPVBLtB4yEm8ovV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d0159ed2472012ee33ddf9230004b427c7f696bf22eccb67f3ef6320e657a33022000b1dab519c3bf58067af22ddb3fbd1df9144324ed404f0a3f67d7fb2f64f7af", + "id": "08c1f4dac6b650a1d2e5ca059fabd37ba23a2a8718ae034fa1759e32f0ecfcdf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1110036076121, + "fee": 0, + "recipientId": "AGvTatnwQJR22A5dDEWXvWpfLCCFNu8tof", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205979870c79c60800f61d96ff866cc18b494945f2c805678b9d55650548562e6e02205a070cc7341571569be79255018a01cbe86a1330c1e2b02ad4aec2ab498910aa", + "id": "9541a811866acc0b84756a38bbf327a40cc96ae00eef7e20a540be4adce3ca69", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1110492080313, + "fee": 0, + "recipientId": "AGfChYyMy3ezANVDo7JBECGJxEYGeefPwV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220298aa44e674cfaed265a12495d1ecd96299c34f8ee8beacb597ade8a5b22429c02203b640535bff8f890be8292c5ace7085658b1e71ed361a467945f1d6b97f5a223", + "id": "b1887110f49e04d53f26b0182321f2cc049877bc774784dd69a856ebae16d260", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1113211377847, + "fee": 0, + "recipientId": "AWhG1JweDmRpkT4jbojz4d8QshRZnf1AZr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022060139759d9b0b550c2b108cf60902acd2da2cfbb0747e2782124c478fab51d1202202d51d8bd860b50a5822448e21f35eb97c39deab8cceabff01e30d7865a486fb3", + "id": "dd436734a516d0647539b60be1932d984dec023de8ec0b3d963bb48e2544da62", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1117612928727, + "fee": 0, + "recipientId": "AaTpLPFKjkyMqe1HAzsQvnvVDAPj7pyv6K", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206a305e6563c1eaca84abbe8141f6d16b053e58796933355afd0c004e4b4615f9022022de5f7a2dcb182a421cacb118d5606b3180e54397e535ead5fafb3808640ec5", + "id": "d946b6d8a8a3a305eaed80a1c5c856dba6b732d0a6f2976d2e1830168d242783", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1127012726593, + "fee": 0, + "recipientId": "AdwPm6G4tnxBBgtuB2UCX6kUt8KYv4vm4L", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201527ac126261312ad02383d5481500ac4bd030a800d2d1e8b0eb2e26d5986df90220666b7179f7e9eb56b0a91b43165247d2743083d37f61c773780314fc18d811fd", + "id": "228725e2039844ee5584f099930f5ab6c1c0185f78e69fd5d418c74abe97e512", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1133071775570, + "fee": 0, + "recipientId": "AQsoWhRqcDBKREvizPBDehMYfUMwpfoyts", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f4c04ab56643d9175bd4c8179c2c3efc5743bb2b1149c38a182583c72b2ba1a02206fc46634fdd5548d2ae01950b9e9696ce6b880dcfc59abedd4c62c3757d0783b", + "id": "71e16485b6b1168dbedc07642c4dcd4295c7d6073b6b12d8c86afddbc392675a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1135911839174, + "fee": 0, + "recipientId": "AUw9RAfTKkMqFky8G7Q3s97cA1NMwsxsK1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d29979af23b1a9156c2a0223a3e5a76ecd62b666452dbc0931590730b0ef15bc02200eff2f5a2cb906c959ee855bf3261db9240d7ddefb5cc52bc1d70d295856ef99", + "id": "dfd87714aef9d4f11ee8a5383fd9cb8a467e3523a3206eaf99ec66d943bb41d6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1135911839174, + "fee": 0, + "recipientId": "AHkybDKEp9psTVmKPvHzqj47z56BY5UcEc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205d422f18bd80adc6a12a4c8bf2f933fa5a49328137e696e1738f1e5ad0f743e602202522cbfbe59ca0e6d3080cf64011d7eada1505b5984553bcf353b871c2c6513d", + "id": "7500e3531f8b71b5f4b7aa57e9cfd47de4424a8b7eb5dca89e2f2b9e6f5dc34b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1136025441718, + "fee": 0, + "recipientId": "AKwY5JoLghksohAZG7oEG2JmgZuubyPGVp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bf6dc08b189c178a9201df3e5c32c3e42b534a09c7f676980d47d6d53335848c0220531edd5c8315583016cefb60ed96217a578c14dfcdc4d6ed703c052cc2c2adf6", + "id": "09bb287a691f3121e58b6f0d862b98e2fc1e6a703594897dada3f2775f8ad9fd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1136025441718, + "fee": 0, + "recipientId": "AdbutZprjH2HAM17xxtqixENbzXPW9Wys5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100df1312b79544c50df8309c5c47227587ffd9a46eacee45787cc56e37be4c0c8502206e7679edcba8ed6fe9bb634fdfc96115005803a8c12cc6292445585761069d4c", + "id": "d77a533c624889a76d7135695ab9c70217f78ffea9319af6800249ab846800f1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1136025441718, + "fee": 0, + "recipientId": "AJknkVe3o8zMyMVvhLoeoHsgMowWV2gZj6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220090cff705808e2057c9375917aa0e29056143e9e30a4a631cf32be7e9d8f7281022001259193a883851281a9978aa198ede0dc910db7215f1da0aa40c6eb749fdbc4", + "id": "f0813945e05653ff3b2f3e0f7be0a392e5183f5805efe5fba11d23386cfa310a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1142768005049, + "fee": 0, + "recipientId": "AKjKD5TuG62qWVFPpeca7JGeg5ZSs87F2t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008c34f0cc07ecff34844dc8b3c59859eb986e4713d2177923de88beded8a00e4302202dda4d34149b8269f5d62b28e18b3c0eff841acb41c7d19a657f673d8f2ae735", + "id": "f456730f13cabf7d2b355c690cef499f35613b3d9943769770a371d68ceda967", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1143514373646, + "fee": 0, + "recipientId": "AVzbLnDifGM3MgLLXGJokYRRaJtCTSDtJf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e5ac125df4c653e04fcfa0ac241245873a2ab254eeaecf65b2e55a9974627da5022026cb84adf8e3f7b3b3acfe181df6cc1e4a1d64f06e9aeed4ee3dd2fc809da404", + "id": "7084d48327cb27c550dea563c70084c1b113aac2cfc8eaee8da2d2dfd231e7c4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1166230353463, + "fee": 0, + "recipientId": "AdFkX9y3WiLxaeEsdHgTBr3fTUHSzEynGF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc010d5be3aa4448bf37dd53b39cde7efd9112922ff340bd4271e6989ef0a6eb02200a155c4869414d9feb7359b3b7abe7a44dfeb329f70dba2f06679ed5f61943bc", + "id": "331e701070f52d72d30610165dfc9aa6c0d7077f7fefbf4daac202ed64563323", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1167544630860, + "fee": 0, + "recipientId": "ASDGBYEPJkRm16xDXW2uBtzegkmtDX7Fs3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202e7deb619dddea19875e03f43270d03e73f37e48efaff365eda36e2962f676e60220697fe1276d816b0e5b2d6a55f06a0aedc12f76d6f228b07da41213d79d85f2a2", + "id": "4a2f713400914d0d3a66f565c2d1c9cc39a3f19c12543e2ec418110335a0b596", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1172864247614, + "fee": 0, + "recipientId": "AWbSK2Pwu5USnhcQxaraoybLz7XSRRM4JX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100812581fa24ca31fb48b100b3bd5415e16727dd8be600f0f604c03408010055330220032db4b8659cadee9a1b949add6b4ac8fafaf0fca6774898848f3c417c36a180", + "id": "7dd6f8130f9d3f76512de04ba1c3473ca264e77b0613a2c8a86f3cd791fa659c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1176120457308, + "fee": 0, + "recipientId": "AaRaJMpnL5vKt7ADUsEAbVw3nqnLY6CvMg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220379c7a758df66f5b8e7879119a902aa415fad87c14d36d7288b8565c90310773022074efd40722d365288b37f2e883b4a9c3aaa8dd211762b496649c63e477bf9e48", + "id": "df44e3d6aea679f21c528cb1e0831f1c2c1a92abf61ba046e05bfe4542b5cad2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1182001059596, + "fee": 0, + "recipientId": "ANZWuhTZmksp11sRpxy1JdT4mymjkdz2ys", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022004f1a606b87c3931b43aa9ad6c39c1b7a4425d7b9463d3fb1165ef64ef3515250220614138bfa85aa722ed98ce55a3a8445c39fdfea6add75b0d780e47f93a45d933", + "id": "fa762596e4127da262c6663483dacf38836d6efa88c818c9bef23d3c8c265cec", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1185739919799, + "fee": 0, + "recipientId": "AV7zqi8PG6BfB3Nud74eC2N3BQA3zi9zCa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220246839d23affac82057511a02b479902a17d163a2df94b950e8cb7757436cbe402201fd4e87363021f4a3d341f8635d61d38010aba3ea51df5e1a10c9af1613db6c3", + "id": "d3aeecd07fcde727f3e8e8ba0852c7ff5b28adf9da80024cfea7c51f8d2c961d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1187780964457, + "fee": 0, + "recipientId": "ARyuySFRx29xHYMdL7TnKZv3xvBgrWRdep", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201235cf2bb5c1cf1c254fbe24d2337d87d100da46a7f39c211770ebcc937765b802205bbad34509bb0b7f86f8a2807e08be016273cec72bef1ab9bf5457939767832f", + "id": "b7eadc91dfd13958e6a36963095361b289c1c37c68f0aa3fca1ac4f648440ba5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1218554570073, + "fee": 0, + "recipientId": "AGqGgDyDQdj4stacZznPKNDRbdeQ7z4nfz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e43b353214830892288bbb6ac6da9459ccc205692fc81860f5551b4a6fe8f325022040f1567d23489b76539c272293702890658f1acb22ddaa3e43ec2c50ed293d14", + "id": "971db6562dad82beef13c4ec839c0d60866e141b03ec2d4a9d63c67e2fc46992", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1226105576744, + "fee": 0, + "recipientId": "AbumjiTo1eDMprMELVf5RnCwY4jFYxu5js", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205873b0046921b459e29f8227a7d59181a7abee5d75860c5f941c79a9618351dc0220795169efc3aa3651755c0563b623945bb5aa8908196cc2aa0d1fca2e87f4c6fc", + "id": "f22ec22f00452a8c00256018006eb2fd665b84932dff56090d95faffeb895a7f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1241085387065, + "fee": 0, + "recipientId": "AGNRsF3qaggSf4N9i9gpmMuGkHBKnLCYqS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6924dec0957658a213b7e04eb24a996018344513ea1a35163b4977bb1db709402205b8d978e83d7c5051c208c33617bf8ab611d63ab376fda7ab8a3ab6917ea0ac2", + "id": "9137dc5ad77a4c7b7946f24e7e150c2622c15b6d1765f3efa552a386dbec4552", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1248321295924, + "fee": 0, + "recipientId": "Ac9WFSBF9MPEjCD1f3G6vWCHtZNT53C538", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c9ff6cd56b1723f0bc7d5e7623bb66ca39e4561b79ed3f6b2e3fc541bd08787e02203b69a4d8b2837be4fc097bdbf174cc41c1b968626d125f497b106c30c6b8f5b7", + "id": "24d7adecccd21860742650572bf854e50a79d8edd7f23af74b7a41311b3d9917", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1251098136462, + "fee": 0, + "recipientId": "AZ8GhCcmNL9HGhLejRShEkbPL4fsDcB6Cw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210087e8c65ba87f0af0fe5966e9d134ba62000972b7a4c5caba161f634141b2e07002205ec3411d77ffff2f5652d2074f622078dee8b8149b0f14b8dc779164ff03a3df", + "id": "912bb48bb857b0da7007fa875d86644c803c3acc59af672692cb0abe4fd684b3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1254056287561, + "fee": 0, + "recipientId": "ATRX4J1yKRWZwSsNyW2rEHwbgNxaj3SaDG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009eaceaad92bedbcf6a3137ecef0dd7272072662bf6158c82ee8242dfd6a9265d022032b614fd8ddb333c1b7135197649a09e7cfd3ebee617f4b08753fe5011121330", + "id": "c9eb7cb21ffed293a6dc750757d50b459e5bb98dde6080ba44ea984bb5a3bb81", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1262124265749, + "fee": 0, + "recipientId": "APZXrDufYPZd4M6KK6mU3N9i1o5kypmeFB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202dd40b2e9929255d731e525e3f2bc1d49dec2151eb3d65a58424d346b6e1ebb7022050c87541ba7b5da3e891df8b9148b9cc7f84d5e3c185172367b76afb8544c34f", + "id": "9c0e58750a4580a5efa8c5dab95ec4948feca82e6750647945584406b2b777c7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1267980213012, + "fee": 0, + "recipientId": "AW5XkGBc44rDNynQgVwVSCSyUHd8dt2ap9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202fd98a801d3baf19fa8a77e2e0e5b8d684547744e26856719c37f37ed3e5af3202202b3f071b8daf7f5e57ea7ff8c4b54e46dac73204a72f2ceba6e5de7121f0de82", + "id": "b0ac53b52314830e2fecf20cac13d0e3c1c33795c55cc15fe575db1caf5e59a0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1275943681122, + "fee": 0, + "recipientId": "AekYhbhVD1qNL6NmWL1tUZTyopAjEjq1FD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc37876c3e04ed282fdab28d5412486eda4952dd02d7cb625cc642409fa0262d0220263503fb14d31513ba463c6fa2bab58838daca4a19dda7604c7708002d576073", + "id": "17bf45d5a938ff3f0e910d5a75c731196d39553d66152b74cd4f8687241e3c49", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1281318617272, + "fee": 0, + "recipientId": "AY6ZLmZrpunUg3XuQZZBSXQCqhUqwdV7Pg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022033eb26e2f3bf589d76033dbb4ab8a726bf495a26068e54782aa26f86edc83cb70220639b4a7648c43a4aae0e4d3cca922b7274aabdaf8096cc1f99aecbb93379cf70", + "id": "027298b8d2a4425c3b04df6e1200a87e504f79fa7ddf9f8e1453be427d51b5dc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1283452028431, + "fee": 0, + "recipientId": "AZUXnt5f7UDcqCTn8GLMqrqxFumWzc6xdV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022061a17e5dd43b0fe5b0bd9f3a10dc22a004fa2ed372e4668ab3fe162444a4a354022025f7353468432e788a5859f589e4cd840f107b908d496bce9e08a0ae8a48f81c", + "id": "fb90e727a1fb40e9f09caabc1bdf855f85ada79315a103e6201f4b05b9bc1554", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1290283529798, + "fee": 0, + "recipientId": "ASxqjAvYhLchFzgx2jwju4GsUCSQEH3AEP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ee3204265b27ca699266a811730b567a7cc018f07e789156820f4b55bb5dc5a6022037f1b07874363ebac4373e04aa81cdacc8e462d0c8615dec4945254fd78bfdb3", + "id": "ab1293aa990a9bcf79d2c390946ee548564abe1961227cf01b6ea0e4f99caae8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1305366622447, + "fee": 0, + "recipientId": "AKoaU94wDMKcvDdon237iQ4ygpqVEP13Y3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bf1fcb56c3a81f457103bc035df6f52aa8f37b342db0d24b37d31ab09b345d85022033ea972e903998fd0b87d127f49638d31612c25ddbf176a8f28408da823c5382", + "id": "e33edc4c6ae458da431e7b3fa0c62bb9f04b900c071887835f9e7168cfe57076", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1307153045859, + "fee": 0, + "recipientId": "AYbyHKD2741E6HT7dM2YFnqM9mMPS2yYTd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eb0c6b3da5ea62ad2fc5d1d80b88627f9f88de9787c2369ad41d035fd4e984db02207b78c129b934570c69db9b52b5c47e7e922c64d9c0f6d05beb8b93507e232e3b", + "id": "b791858333e83013d9a2bc47ee3f32000e2a4b5425093c89e3615af4033bb90f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1332727985890, + "fee": 0, + "recipientId": "AMwPB8oS9vsN2mfaDVXSBZpGPx4cos5Mjt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ed8b29399b29160b2317c2eb075647fdff0f477844348d0c2419f6a99eabc5102205753d822e07f472849206cef2b0bd4e55f7584e793eaf40db6b1a1eb478a65b0", + "id": "393c15a74a9b38eafdb1b8bc58171ba17bfc61674792a0443abd4a4706674b0a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1353732126985, + "fee": 0, + "recipientId": "AeWDk3U68wyXzwYKrG1eFngLb1PmjEJ4NU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f99a88f1106de48bf260252af1d7c94c0b128bf3a1293b39fa6aa3b39e855671022001c6a107ff261eaf289562ddc495ae5431f2c03eaae98e02d31d186b3891044e", + "id": "677d0d56c62618704636b51d569670848fdb4e838c1d127a643ade064730d7ae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1363961613487, + "fee": 0, + "recipientId": "AYKGjy1QdNyw1QtuYMKEQiLeeim1PsMGse", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022072093aca8550c3ed5c93ee467e06dda194f02c5434da8b51e8ce1f425f4d6a0e02203b3969a7a176fde24aba664534284790c56aa157e3ab8a6144b2f82e14c328b1", + "id": "7f8fdbf6de9d41fca6389436ffbf660e670356a289f9b823a335e0c6f33a5fb2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1368486431867, + "fee": 0, + "recipientId": "ALqNN4gFAQ4i6keQYWt19VGVdf6srRw3fu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203fa40db925f8076b84cb54c0442f866c882e25b67274df69d2e668d1713fb702022029b2294f08730eecb92ac2080c7f116ffd3b587ce00ff6907d0ac52b544188c3", + "id": "0ac5811bb68f5a2b9e1f9516414ae78b9b19d68c9d45abd95a83b45ee5b8245e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1373148336615, + "fee": 0, + "recipientId": "AWBBsjmyW9q9Eu6Esu738Bmhm2ZGZiwqmZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200d6fb95c47336c8107cdda4217db3bbb5c7e6068e82d0e78c158f422d44e473b02202b2c57abf8045984d6a695a1e7396507b47c0e735a38bac8b4731217a618b922", + "id": "0d8c957621281c5dc06c45fd28fd69d0ae5bebd809f991f077d061e692470b25", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1414800000000, + "fee": 0, + "recipientId": "AYsi3kyJfxa5LvhTms3yLbVjLDcwZEuQFU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201f2578fe3f6799f09c4e21ceb01444643676736794accb55e66065b5e2c7823d022057adec3762d37b685973d42c40d03b2c8ec414d3b4d2bb04499816845a358312", + "id": "0cf6ba6998cc228d806efa6663b4e1024aa417a15e46b5f64225dd86b2e1a2ae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1445037440539, + "fee": 0, + "recipientId": "APNYtJ4DsTAPr7UzmPoQFsdj4GaBT4cDNw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b55aacdbdafbf73aaa8ffdd77638cd52c2e572f68c82cce5a21182d8edef856402203605dc48ec3545efcc2ae67e9dfbc440a3e3fb89133dc2eac0969c65e9d7ea89", + "id": "a0f5649db4efa028d6eebdee0636a191c1981a80829cf6c2948c0cd3f5c14ff5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1445600000000, + "fee": 0, + "recipientId": "AZ82o4tRxNgRkjzV5GyBDkuaAgnLnUDMTx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220713958c68f4b3f560c9592fcbda9dc2a6be93678f7e08ade6e96370c4eaa26d402202c862c934e996d1dd75dd49ed7204128b81b54b78b431076c997cf97e7747cfa", + "id": "2fac0236a0c0a9a6bcaf6401293611d1452f338213641627f8db909d47bfd8e8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1454528664622, + "fee": 0, + "recipientId": "AcCh8h1ZmQN5BeJ74rFJKE6hG9KZqbesYT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ca4227b7da265016be665600bc3e7bb20f75d7a65d72c13da8c8d9ab4ce383e2022038ff78d1c5e601365c28d9e6d2e4337a7d9d7d0dbde45c4a4023638fa07e87cd", + "id": "8ae411cae067cb9b9f15fac37ff5e9ecf9642a135de60053c3ecddc38a498f77", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1466807483534, + "fee": 0, + "recipientId": "AFqgu6rcFy6QTXGfN1FxC6sUHY2xCW7uc1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b2f227538d4b7706be16ade8e17139e739a09afe03282f64cc5c545dd1e2dafa02205597c6a4d88ced4a5f1a2773b27b256a9138310ec40c62bde619a99f6b4914f6", + "id": "2f4c8dab500b1105cc9545363586a417fd7946615170bfad8928f68b145e067c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1467651315664, + "fee": 0, + "recipientId": "AHHrxPEi2Kj5VP6W5ZKegVh49PCV5n1jYG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e97ad561c742835e8d25dfe5e8417892e5b73a5fd29200bd36760092bca344c702202441a37b935457517e87484b64edd8b4d8343eee4e6acfb4a231faf29a079c9e", + "id": "806e0255fad866d11ce328cadcbe4002bd5a496bb152984bb09344e890d566e7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1468250973203, + "fee": 0, + "recipientId": "AHBAMChcuEmR4GEqhyx8Kwf2uSadixGJ63", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b2484f242f0e10ff6a3c0a6ce4b32c308153788c8d844ea7b8b6f53eb2bc6013022009b277ce199b03c13cee24d7e1f0d0a519f097e783053ed8fb1e85ff174b5533", + "id": "e024d4271d53d17eee174f952a3577ce724d9f9896036a3c2cd3dfe7680b6853", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1468680421064, + "fee": 0, + "recipientId": "ANAjpvQ3YXDaQBdh3uq9UrCzpgdKbyyPec", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220329b1550fb0f30e6e506aa2a818bc28db4f5f83510cffb3e92c5237ed612e3ba02202b595447789bc432db35f5795a9f37a41dcb7c610dd1148fa46748291a08ef74", + "id": "04ca01893a22c45fc969173c7ae1d0b8f319b6ce02a52cd8439fd40bd7092452", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1468680421064, + "fee": 0, + "recipientId": "AZdWJgmHDvj77EmtJeV1WvpBP2iotiGAm1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022022abe78cf54b66aa2968b866a5f2c89b5ba498a10716b87a4f5b1b22294268b002203cc887e61beae4a9b5d1472a25fc5e9e19fa21195710159c19b433b9004fb33b", + "id": "e1a59241b4186c667e2092a767cad17bcd1e348c0e7507e57783ecaddd8b59a8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1469630945593, + "fee": 0, + "recipientId": "AJduNtdjCT13Xmn1HbRG7DA4H4g4Q6BvaV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210090c7c64fdfd574b1a2e65c070b231e9c99a4fd26d4ab2046d985076c42fc21c90220481c1d8d1458f1dcf7adf39a52fab40a3f3d25830607ce853e20767623fa691c", + "id": "8bfc8aa67c36db1b6c8db6f65fd89687b83bbf8b45223be0984c084d2d507621", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470003556578, + "fee": 0, + "recipientId": "Ad4QYPyJaqhXRoMKbezNvCREbK5XPa3zhy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205188ab1d4c98d2f7c02b3825701e03b310655fbc44db5931a89432b666ffcf3f02200e1eb5e6220abf42c61477be919ab32c21626dc74dcd14a48c5d6078ac155cec", + "id": "4029404e82dedf7fa4e8df1e90238546b5fd8009d9cf6457ad8adc7504a1ac00", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470150571634, + "fee": 0, + "recipientId": "AYF11aoSpagfi9wzfzY5PWdsYw8erLzi5N", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204973fbd8f15de9230d6a4cb6f9f3bdda3dc7e1b66abb0d5eca95ad2ab43b4d750220132d7876e371bffb26b076bcc1b68ac7b8719917b25d9f09c597ec58e37bfda4", + "id": "3112c2354cb4f918d6bab6f765a16084540663423d129abd46a7f98292f6e781", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470150571635, + "fee": 0, + "recipientId": "Abw8geGxqiRSNtAZubYj6CM7NPeWK7VU8t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220532893083e7e86ced90c28507bc86376998b7fe227c26d44835905dc2c4c3fe20220784051c6f98e86370580c94789326635a2737a3f1d9cbe553c9af5636e4c7a15", + "id": "508f69085efd61c4ac263c576c0da0e037ca716874a848a3ef14d7cd8fde3487", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470150571635, + "fee": 0, + "recipientId": "AHbf1KCCkWZZYww8dBFjAbSK4oi44ATaNj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207409532c4f13e05ccd6236cc3230bd8c4d8b9fb3335023566af2ec6a22ad545302202dbdccf7e42d4aade9dff05a5a16c5c96e9b8050dd5b2437f3471097f8ad4211", + "id": "dc3acba04a593a641812a6390fa5b0e63837447f08914576425715f8887c02a3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470150571635, + "fee": 0, + "recipientId": "AP3uaTgqBZiPz6TFYNJ8zNNcZiNBidyLEa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d44633e422ddb30db8bff2241ffec1290600ac1307393452dc7754e7c0928177022024cf31e3fa2a243218375cc5e66c8f0dfa1df07ec5480196884309b54a4068e8", + "id": "9273a1dda6e37f1e98206d1c5c89f5c36d869a7a4e59d1134875cfb923b9fff1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470150571635, + "fee": 0, + "recipientId": "AGuHXDf1v7Hwfobh3P337B2579DBPpjSVs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220278be19de5f1b004e82a31fbb872d3bc14f516c26b56dfd10fc69fa5c724252e02203dddd4e05320b906a2a3945e52b412e12760c0be9e64c9286afa2ed1b907a43c", + "id": "6dfe84e6247aee21e6df75a25030ac199eb74a7079b01df62095fe507c0f3b53", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1470738631864, + "fee": 0, + "recipientId": "AHUF2TeEq2KfWsdKJmKkyb2r7rn1ssxVTD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c5944532bc50d544fbe393d8067585bac4c00239d96d2cec920b16b5e58be22702207e0594792578262ddd6c2ed4f443078a59b17412b835168837f58ea03268e8f2", + "id": "4e4ec69496a82528dd81743b7121fa8f8337d95a8c960e035ca863c9e9e945ed", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1471620722207, + "fee": 0, + "recipientId": "AdDYWwFY9CHymn4k8dbnShiEJZyF1gCTbe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202febe57e819afd13b956afbf988130d00fc6894a72128595b9d3c5c10b7171ca0220127aeee9f9e67b4ced063e09300edb638be7001c3d823bf4873f2fc07c1a8052", + "id": "c3efc99b197e710b0db3510f26d78676fccff157f80a0043ff70f862b99bb48d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1476719471690, + "fee": 0, + "recipientId": "ARPtgH415JQHtdLmpkyfXm5QvUB9NWWZRY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f9f6c9667ac8300ab05a51955046d8653cdbf93d28678be87e4892502aff5bf8022045b45cc9ba1a05612867c00cb08e881ceae4dc43e0369cad40789edc8dd3ea66", + "id": "5accfa3b6f5bde03070a900439418bfcf6e333eeaa78a55dd38dda5281f72359", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1501867950683, + "fee": 0, + "recipientId": "AaZMdjLc3oc2VU79SWzjaM8N6QQxD9JXsp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a4efa1b07b43cdb06620c1ef7cc124505d440620ac4533bfd20d609d038b0018022050a04b85c435ad086578e698d11bcf35256cde10769c69945e144ea5b369a5b8", + "id": "99e166bd676b85006a59c88b96bafd199145e0464e120a72630a18886fe93347", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1521031173923, + "fee": 0, + "recipientId": "AHGrL6VPzSfw2kGfTZWFLN9hJxh11NLvRH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022009ce1f620f2978fde9d02d346f69178a5339d0d34d229c9303a6bf3b0d390c7d022052bd984b7abf23e6e1f71f6c532a94eb92fa99a1b1a6683249ca16c93610e06e", + "id": "cd80da60be2804c472b529b4e41b3117d2c54b37047fe32363431735f21f4771", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1538544082336, + "fee": 0, + "recipientId": "Adse4g6EQzevtFtxCaymFKNTXfiqT8q3i3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e4d2f7d9ee5804329bcb4a16f1a925878dcc4f35d2462bbbc0e5dd63d6c5d86022039db901cb2fd1af4827f7c902cbf9d3758d56abaa3c2673f9a36eef29237bc4a", + "id": "16d0f95017cd07ec7ee5fb6b9a34cc55b4f3a43b7e551a8a2117fb22117cbe13", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1543246790015, + "fee": 0, + "recipientId": "ALreqLXn9X6tcgYfw7LD1WYYk7Pt77kdUd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e5d85a41b1562fb3238d3e5fee2f4374b794aec61600ecf1eeaf745ec90b699402202b87a4c0574ee646af802ec7f4ccacfa7d6163c70f169b03384bbacde592b398", + "id": "b0078a99ab5fd8889b072e2022171a7eb345aef853d5d097a78b761989a260c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1544792613177, + "fee": 0, + "recipientId": "AWuvoaPLVWvzGLAdJhSAY4gdLnTB5uEJdi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a74e828d11fd810da6ce5be317d67c6795cb1bc02ebef637ff8d0f9de8c00412022073c5c9da3fae7a882d34d29ef370cede0e7b3fdef0ee054e72c659b0dbb25b71", + "id": "eb3495c47848de6b51b7d60f0918727bbd598a58625a3e308353dc27cbe2987b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1544843298795, + "fee": 0, + "recipientId": "AGWDijB8AcjiwxYTB6eybHiV9qeaFXPmsq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201c541b0daa00bdb881cf99ccff32f8c04575e14f55e0987ddaaa9e83079ca420022042d986fda9b4e961d5487e602f78aa3c982bd4e0f9601ce33ca5b0be2cb51332", + "id": "cb64a6bb232fe4a160ef8a96b6f34bc76e8d1f1355edf969e0e1956886384aae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1551677103335, + "fee": 0, + "recipientId": "ASqxPCEkpFj8ziJ6j4FY8pC3AmMXXT1KtR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ecf1d28c1dfbc54be58569ec5f273008330340ab70df7629243b096e32357ff502205f9b0ed9cf1b7d7ee02b6e7a06799da79566c831cbbbb663b56b52b56f2f8632", + "id": "18fc455a58a3d42ae22c76540f497b5c1e74887612b6fb6128f6930d466fb6ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1557630114327, + "fee": 0, + "recipientId": "AYi6DEzFoDBwrRjj7sT78hXJGayRm3tZMm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008ce7aae30ea9cb91237a4a646db6408a52917eef521c4575bb94e8a4a95e25b80220356cea901142679c62987b9d6f36b82029860226a46fc3e056c8120588658144", + "id": "e76471ed188cf048233c7114f4cf26d21b24e77b4e4c4e297cdc3e02645ab56b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1590480695140, + "fee": 0, + "recipientId": "AWevRPHEGJfM4rn2BRV435fKi3AAnZZuxP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3043021f73eefa8d4dda995031746278feb82015905014306cab9766abb3d428d82fde02205852d35a40e3cf3a0113295ae54c76b4465bfbe46b67eaba24b2cb2590ff4b7a", + "id": "03d82bd53b9b6bb359d6773a6ef3fd340664e1946f8bd02cbda99e53fa35ba69", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1591288549068, + "fee": 0, + "recipientId": "AUkNoFqcTkMpPbvSx6FH5HPssnuWeUN1Wy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6ed9dac47067252fcf8bdaee46e90445a60b818d130f96755cfef8aed8c03830220139448b9a28051a55cf48f54f3162f7279e13e6a987a15b020d3d96a7e9cc3c9", + "id": "cba7cd24daeb4cbb94a63f729b6f4628bba3c878b09af82c6d81dcee220e8bd2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1598561495493, + "fee": 0, + "recipientId": "AGwteprikJgukHmkfyLepCefswocmkeJts", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203fd3b520f78acab6d4319657c1995b3abde676c85271af58fd8439fa41ac256502204add7191ae8d36d8028151d67956c891f67bf80b1cc3762fdf0871087a2ce8b1", + "id": "296c9e5c6c5f2fb5d7008c3b6d5191a02aa7410fc8efe451f8ced624d60de865", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1603639652925, + "fee": 0, + "recipientId": "AGQZLKtm74nXFBFvQX954zsXpBCVz6Vs6W", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eae62745fa946e1d39d8f12c6a0f965c2dd95e709a728bd2b4aa800b8174ca0b0220529f3a59374eaf4208b8f6273d90af22dce3d183624fbe50b0ec49fa3dfc1519", + "id": "fca0223018570612e9d70ae707868624c6c7b9b42209077bfa7eedaecb136b86", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1605551439283, + "fee": 0, + "recipientId": "AH5EBGfkjr5UewU4Pkdk4X32FZUacp1wib", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022020d4c8eae1b41ba868e1796068e109d48cc208a5eeb374318c448b2aa21003d70220323a6f9fab9bc56481e8d6ff37be611370aad7957f8b0506688e20084466460d", + "id": "069a53a5dfb9ee6058e1202769e25903a014b12df9e4a815874bb4675e95ab37", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1608266337418, + "fee": 0, + "recipientId": "AXsBCKFEyrq1TmAyBHBFX6Fs2iLfg7qYsB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200fa692b85aabb7c9cd1c23288ab8c3c51707598b7e4067ffc018f27a2428e37b02204dd58f775a2ce3340d1ece51a2133b628cf79e26df11dc75669c2f44b2e58df7", + "id": "399cd9f4dcfdd54a47fff7d51568cb6525e54332251a623a94c8497b228a88bd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1612755177084, + "fee": 0, + "recipientId": "AbjtH1c5sw2cPFu9D7cCDNU5dchyWERKV6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f78f0db05853c6639d8ab877bae34a93f65ec3001b62a313d0790c1f08429b9702200133bc555e10405721c43638af4b77eaa4c57b73c64f786ee40df2f4827d9f7d", + "id": "fcf8f52e8b0a6ea3d181ca56067bb44d93f744e28aedbbcdb8e06ffeef189db0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1615695478227, + "fee": 0, + "recipientId": "ATQZen8h9DzHAt7HgPAwzcWfAC6CaTH4dp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203b9df7a00baac5b1c3b61b590455967d9a38701ef39fbc4e3e37d1c74631f0c202205625993ab0006b2b2d65b0b28871765a4345a6482cf2aa42d0d1ebc503f6ac93", + "id": "c13207f4ba76d22fe47a1d538019c73ac6652dd7b6351a4222ddc158950e9958", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1632724842016, + "fee": 0, + "recipientId": "AMbioVwHwrkTYHt4zqt1r577Z4Y5tgWwJH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210084ed4b704b13b31aba705cdfc9df95fa552f0254e97f89371e9488d672aa337d02206ca6ec6c2cda84cc0534d21a4e74d8782f11aaa5690668ab17768bd97abafa9e", + "id": "18ae7a152adcb4616d7f396119eab66060b48e66553a4562737e95244dc2cf8b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1635164836036, + "fee": 0, + "recipientId": "Adkm397wm32bW55AE1kQTGyk1t6NCz7sfP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c749b79487813d5201670106a0cf10f990e795c5405801c66e3fda6b7bb8515f022012a06e81726ef353b47297609051d850caba385828b5d9a4e044f1e5edb32471", + "id": "e583c7aa0cf2bb4da4d9886b55b893ce1b1052ba6d79ee510e7f58c273c8b1c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1658329844804, + "fee": 0, + "recipientId": "AGvf9xsh3xgCMSnoCYLembrzthHqGJdwbM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200a16ffd1d50f85db3ea81de649a38892df46548d4b69ac6ba8e1cb610bca9be6022005f0b9d13821099d92a8fe5662389f439c232e3da0a024dd43cd5eea7ab4fe21", + "id": "aa4d7b90b4c1fbbd4297a23df89b89a84c9c912f03af1a9bb4dcf77dd52b794b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1672675483771, + "fee": 0, + "recipientId": "AMW2gACZ82N8CNgjFL4q5bzC6dRwpave6f", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200fd4245350f29e79b767acd442c893689dbbe3fe5c6d0b1bb40d3d7d8f4e0dbc02200f0d67c711afabfc3d38324236f051384229ab00f1f9a698627567b8f73460c2", + "id": "a8870e6e74479438af8e464c62dcabafbe785b01824e078e1e121bfff01e880f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1738465434518, + "fee": 0, + "recipientId": "AKFT9sF88rSg7rvy3gvMEJJ8LaYsbEsYH7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa6c33a957f7b2b26549f37208c4ab1d968161f531cc2c681c863acaaa90627f02203138faffd5b376854a9e6d7080b2850d571f07b1a2c7c58113dfc105c6318f54", + "id": "b416130c8a04526c058e7dd56d79161c989f0cfcd83aaa798e6235ee6fcc3c57", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1742839839455, + "fee": 0, + "recipientId": "AMXgwGKJbUt9NT5qEMwY2dxUVQquQDBrTv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220537c4ad8333d6c27ae2d4b5e3822242efa493084a984db369f4538f61fd55cb302205a054469d04d99e8d3e57f47ce237178014ea4c7c33c7e05f5bab627901b1e20", + "id": "d5355bc6a2601705af7f537514f45a838246dd11a95c2b33bd3a3bb2461bc89d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1775549503277, + "fee": 0, + "recipientId": "AWGk5PUpJCLBr9GppUnNY9ryL5RhbstthX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cd7bf83928c227b964f34cc5d55483743a1ccdf17f778f14bde1bde676a7051d022034e2bb957bdf212377580f3b6a7ac3024179b1aaa0dfe57e9cf76c32f37ecf6d", + "id": "ee657127722958204f85897e7825ffec69af1504e20ade4076b07aefdeb263cf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1812187469576, + "fee": 0, + "recipientId": "AReskqQAtGi73GHG6tLyGxpyZKAw6kyhko", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f7d097824b01b8a9d290ac4f7c80cc8bd5f32ef07b22768ab289ae39048b2b2902205bff5fcf23fbcdfe1f60cdd017fe790153ee783facc851af6a65b7e2cde0cbfa", + "id": "3641ee56bb7b499407222f68f9b0432c80b7e6a32a8515f8c5c08ee38bcbdf8c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1847900000000, + "fee": 0, + "recipientId": "ALCuA5ST57RnThByiyCGYti1rFFpy2vaPy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210088f32afedbe40f6ce661f0e828f6815288b96d78409c0067578bc016149d05c0022018dfcd5230d3fca401a478120a60a153c027212107b380c8f428e3991775d067", + "id": "ad9154e66ae59ac9f80f524568452bca4f00455ecfbfc25ab89652ec84f26c08", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1849124693198, + "fee": 0, + "recipientId": "AbpeUAgMG9Zk1XsLfPcNshwumea7mkekBZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022012c3db569ba5f74749d21752f16fe382493c639d383aa30d9bc85ffbdb3dd1c002200dcce7a238a59263badcb59d57e531933ca1264161aec6f04e44e5c87a6767c1", + "id": "f5615283d55e13b2b690b281bdcc412f9d87493b212d2b6b76d529e54d0a6b32", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1876941234807, + "fee": 0, + "recipientId": "AdBizdK3H7zfp6cnpAsAi9uR2fvJNQSFd9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d595be37c3d0bc9d54b9746e84d92ea5d28f1a39df6c0ead2ab145191f95bb4e022020c5ece72829027083379f47cffe67674d4f4ee5e98f9eab80ad16b2fb44a4e4", + "id": "8bb71be1a515fcfdce9a10be706054e87bfd64cabc0e2ad958569ae7a03068a2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1891934510958, + "fee": 0, + "recipientId": "AKxpcA6iVjFtxKzcW8H1EvQY7nz2qrkGFT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205e4a8eb13b197165a479dd3c0aa6ef0d06a011ab5fc429de4b25021fed9024ab022061c25125f58c392d7d10d9b060edfd3a55df16009b3b50ed6b7825be84e0c375", + "id": "06f4102c8d21139312432f58fc981d0e183a97c6726506f8ea1ea71e5957c4fc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1895283091114, + "fee": 0, + "recipientId": "AYR4E6VBw7Ut1ygyvwLxbwDYtsLHrq7T7Q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022012c4b05c360635a515a257733ed66d9f100fd39810beba85b4014c21c068bcd30220090c79f001b853f4e38c3df7f043fc8c7c2c683fd2f2572a01e38d11f4219d88", + "id": "bb54551895feb42ba8c85355084adb36d7dff452ef87dd411558a07356989cae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1903697975211, + "fee": 0, + "recipientId": "AbT1VtooS3gA8M6KRT4Fy98tFzaTg5N9Gk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a905b417aa0cd3b7f1b195254e15bbe303e0e59d7e2f7007889e0615bfaf945b02204050e412101f519179944471aaa65e6d32045138a45631fce10b18e31a05748c", + "id": "3f1f30abd5977c35300930eb6a8af9b16f78ee602cbfeabbc3870149d0144c57", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1909607394110, + "fee": 0, + "recipientId": "AZt4XTwHPBLEhr6vkLEYX1b1RKmAN8Yit3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f5fcda2c42c4756ee68e460366d4ee509299d67559a4ce904dc22adb03d8422c02204be79c434dad458962e524da2e99ab10116c61374ac17bc880dfc5061827bf61", + "id": "8c4192958e2a40b7ac6886d39d6324a17d4c05c9c7e96da6bf6316742f2a7b7f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1911195743126, + "fee": 0, + "recipientId": "ATd8DeNiNgb7R4uiQGyGNLYkq1q7FhikGk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e2d7e0d4153794e41d90c9cfdf873867f46da8fb73d4bf5df8250f782d4a4de02204c2c78faf8a805d97119e09e77c132630f7d525bed291fcc40b019e71bc85490", + "id": "cddf63350558f510f3bd2bb86009c71bd39f75d36ea1bb245bf4b3af761f2e1c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1940598754559, + "fee": 0, + "recipientId": "AYeUdkzuGw84sPXgFBdLjfjXtG6SsJ2Rjg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220621146cc0eb8596f176a16040fa8832112c2fc19b49c5a4a1ba830de141df6b202202f1a8232b2057dd529355fc4aec34ca4825630d203182f02880de2efe342d736", + "id": "00f4cf49905ea8c652b71c522fb8bd19ddecde1d2e8feb1ee2961fcdf7034a04", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1941422708032, + "fee": 0, + "recipientId": "AeVN8SgGb4mUYCjHVCCmWfw8h7WHb8LApw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203d2d268ff5c91f54eba179cdc53b51726f6eaa3c1dfc3598be7e444c12a5575a02201cd9dd296215e16ebc511146fbda0989c8a41051dcb07d62f9fd4622bfcbf27e", + "id": "fb2a707333b3286f4d44b61db19a2e617bbe3592de77da45695444b62fc0a5fb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1946861217599, + "fee": 0, + "recipientId": "AGueDNGxUQRDsGUa9aSPw53XKNgNPjUAia", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a8a10c8b659087d985de4bbd6166a9f2c5f8cd46e28be26b2f9babe3a2d063c80220460ce29b3f0327359a4028ad600d25bcb25634f19bd9b3d8e4a3014b2b9636f8", + "id": "e127b243ad79cbf966ab4b1c77b45e6657fbb5f49e4cc3a96406887e2ce4637c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1949845628984, + "fee": 0, + "recipientId": "ARsJyHCQ8YnDZYm8v2sE51e1qtGnK3SdMo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f82068304b6f191b9452c1160caede73592d36d75a00beb74a9af9f1886715eb02206b38b09daf8e9184cb91130165227fbf05e94b835a7399d4ba07ff8bca6c1179", + "id": "6ed82d8c0071b52aa28e9674e012ba28759081f527a793da1161fc32d058bcc5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1973042309220, + "fee": 0, + "recipientId": "AML7Pg7WEuKaEWAM52rAUfv3RWE8BAbp7i", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204806c034f7027b71d645ab6df4134ce5c2861e479b9ce8a608669221fa1f9991022052ff1460577ba1674b6a15ccc505b53749bd6fdb8f0d1e3dfa596ca91bc956cb", + "id": "5d719b936209fc04fbf9e118b78c688ae6b586d0d9ffac1cf88225f47e2c37e6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1973887957498, + "fee": 0, + "recipientId": "AcSTQCTesrVHnvoAK8r1bwZ9CpDqiPmRK1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201ee8cf1f90aee896e63feb920df725b6897c6d4cf0723198abe4dcb65fd6359702203e5307a6831a54072cf564804482661789e2c78b9c189760ef1b33579de60d56", + "id": "5b82367f3b78997169ed9fcba1e4eefa8054c96b64d2bd30d12f18f2a364444d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1991239524027, + "fee": 0, + "recipientId": "AGhLhXKXAUWDZRS3skaEVMzpcyeqCyE6as", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022019f81fc2e04573a9ec9ffcd4c6504da8d206467a742c8d9cbf96d1980ab77ff2022050b0746effd9c253e61dc8f89b015b4256764292860db88eb9a6832c33812b66", + "id": "b62ecd68c562fa705d41d59b3f871934b7a5594a89354cbce472c9772e100393", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2000000000000, + "fee": 0, + "recipientId": "AVMX3Y79qXSHBk6tUrcLAeKh9C25xtyJVL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100edcdf6bd19aa85b0c2078804a9a021847592dce74d2dcdddb38a0ece549a080c022057316f1c9f36d89f08ad9020385c16fed5bc088f12e588166fc984e9b0c6fda4", + "id": "4a26a119361e812cb7fa868b10ad2c903dcb681418113abe72c610afd4c1475a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2007149179076, + "fee": 0, + "recipientId": "ARLoK9ABM7bFmaqLMaEBbfTVvsXqyYaeXV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e924d9016e8ed30d3e06fbea56d70c05b670b6a9d6999a039b93b0d436357b25022015da3def5243cdac0d0baa92e8f695a5018e06da5155d17f7f48dd7814341b3a", + "id": "f40dec591c02dae1e1eb176583476d7a7e291ee5f6be1741b9ba3c0e97013f1a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2020119321574, + "fee": 0, + "recipientId": "AL4youLTt6UXwxXWmqzAxznH2aPEekZCpf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b4c556e5c648045ac2cb50aea26bfc158506a494c66bfff2d22941ab8a3f6af902206b6560e4d19bbf8b770c2ea0cacca973f94e683b38df2a00d81727d4bc0b41a0", + "id": "394ffc5b4a91e6dd81621246ca0a1070f3b451aaeca3f1ad48c4a4186409079f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2048474511235, + "fee": 0, + "recipientId": "AGsx5dwwQju71REuRJd8iv8AAo6wuo2Hhj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210083dab3dbab91f91a9f7bbb3fd91b16c5457fe84e918d475cb7e544d09466c8380220504deee2a01460cc9dfe7be2531f92ed2b14a6923bc6bdb02ad1131803fc7b28", + "id": "0c1e0d578f3f6afcdf8d0406ea5478ade1c52d87b31153fcde6c6345cb0db9c2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2058848207492, + "fee": 0, + "recipientId": "ALcc4t52pgbFF1iZ1dntq8oK1TjprrckKT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008ebec56c81cce72ade2152091e9771c0c9b46c411f17c49272b1647466b4d1bb022022bb7763cfb7af70ab4d97d6301db9a96046225be25b50234a5e1bc868e978e4", + "id": "e3e18ce2031b8052143296bee5317ba5013b6df080a1a222fcd41af5a3263398", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2061097549268, + "fee": 0, + "recipientId": "AFpxF8SMNEMaBjhw5NKdydhfU9zeDByEnK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100df0a94e388245d9cc205bd2739d9e17f02964f957707a88a51aaf03d029fb58f02204fe22271ef442b7364541ce62a5d833bdf061d8c6c3d78809a243e26428b2f05", + "id": "a91fab928d30719452a7890dc5d7b4bf323642923ee6e99c7e12aef8eb5ed821", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2061097549268, + "fee": 0, + "recipientId": "AbfyMpZLZXJukJ1jCLkQaDdxU8hRMCroHM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203b386391a09c598649acf2575dabb917adcb14572a60f1ea8cbdfeeb4557549902200d3e0c146cd9d726ab91b215b817dbe68adc25dd8f506dec264445062dfff1ea", + "id": "0c1c747f9e680eb8199584b1f2a9b3b855db0f700e61ef3041f4b9388bb0c826", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2066612412189, + "fee": 0, + "recipientId": "AMgiiG8wXhd16NBjazW2je1hK9pfanLyMV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204c5afff450f8735039b04fb34b6324136842b66fce3e1e87fb5cefbd23d1384c02203364cf3f9b63c10e14909c7ba9aa6d442da124bf257c707277a8167a05bf894e", + "id": "142834112cc331aabf1f30dbcec6c6a538c9b47927eb71ffc30083f8322bb224", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2109666070297, + "fee": 0, + "recipientId": "APJ25Fek6dNkA3jEExkyG67UAzrDRvPxf4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022047b3d2953eb0b6e1834bb77053b207444fdf49669578ae639e062eaccb721c9b02204d142ead55d393dc0557b1d4b417e71cef41ef65623ffb5bde6e56665da2db91", + "id": "439911c5383eb2e090858e43740e9272b7053d4709c455a5fdf21ed46569a591", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2153677032409, + "fee": 0, + "recipientId": "AXtyzwP8X29Ddr5KUy6GuuxWibW2eNypng", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009ce7072f8d3bf7866b357aa36b475f3eb102e31caeae5d8d3c6a39d8f3c39a4a0220636f0f46dbd6a477194025d39f2db234449195b53798fbd03ce3e2ec3c86edb7", + "id": "dc9c7ec06d60f03495f590e1e46a6283db8cf62dcfd2186fc65249f4ddf51074", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2163834169022, + "fee": 0, + "recipientId": "AaJxRN92BCtogWtzRUrHJC8TWqSswRJGA5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207120a932e96c536e2c2f051546d173f3c58bfc83946fbddb0f991431da9f685a02202ebb91ef9238d05341afd2012ba2bd456a54d2bfc20d22ce3d10d4a618ebc5a7", + "id": "fb426988eabaa074956707a42bdf4adf3a0c10cd719a6c4cd6fb2828fa9b7c53", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2184588569625, + "fee": 0, + "recipientId": "AXKzjecNQcpJvwpm72ZE2DDkf3c6qV9FQz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204dc5f6f772f7843dca653edee6da99c709d3efcb585092d3b563fbb58ef9ee93022011ad3b1c4226208c88fbe189f9a40fc22964d8336fdc290d3051840a20006499", + "id": "7853c4e80e57ad1152b9b7c8f7e8e1f6d5aa849ae1f0b5a3732121ae11b929c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2191161363601, + "fee": 0, + "recipientId": "ARV6tjuLPdAAJFhTeho2KbZ3caMXNChvwK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e9fa2dd98b2518aaf44488865877488f3413caa3f38d70d690b8693519f96a7f02202959afa89b20dfc55786c82a636edc53260290744beb427150b506b666414da6", + "id": "0e73c449c0116a23ef5abb5993be907794353557601e0a39e96da882a7c948d8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2204931827339, + "fee": 0, + "recipientId": "AaUvDwmuhLvZRwmW8epsDmRQfLh11ufUzH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009ebbd2c288878988a5f18a448ec7c33adaeecc203084620f2f98b3c74960ae6402204014eecc985db4e8d94b92e94148453480f526521a604c2222b3c04b90fcea17", + "id": "8e95a6db2d145e700e36e08a413e0d0f2a6f23d0640f468be65b73b56469ffde", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2205078842396, + "fee": 0, + "recipientId": "AJuvjPUQHDYkiV5R9QQrAmKQqMsdCiyW9k", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206083b54d4879edb8e02dcd5828e6117ee97624ac9f1381da575ca3654d7da3ad02201effb3b78038dfbb482cc8d60a8096c77fb351387cbeb5b1fe7423d1aac34c67", + "id": "bf0dad6cac85409e568de7d2ec72bf9a649a5a78e2f2d49c2096800e05dff8bc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2205225857453, + "fee": 0, + "recipientId": "AdeDz2SgSHtNSS49qk89UQoosmM9nSn8yW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022060fb58e2b791d584bb622cbfbb9ae89489a68b63cd292445db2bb82c60031025022065952795086ac1cd075d4b2fb58356579b3000948628d8a0cd91170eff57a83a", + "id": "7acf01f865d9e3b0172fd107b066a11d10e561a233af85c325a1891ab695b94a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2241213029168, + "fee": 0, + "recipientId": "AcRFukzbp9dcnpAfhUR7tKr2T9TP9ysr8E", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c9efa61ad9f4ac61a62a753d773e5f5b07c68348d41f651ce11b4381403dbe9202205397961b89303df542243d52407a2f3d1d1314b7f419818fef1b6a5a172a7b9a", + "id": "68995bfad121ad8d25068bd9f6a1c7442730220b39cc002a41650ff3a38221b4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2248243233437, + "fee": 0, + "recipientId": "AeZ1HHc1d6eg8P42i6fVcJVKsXw8Eo32hT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022047e447051f960a6d1e554a72640858c8557099de95f891b692f44b15ca6b85d102207a4d3e483e26de4de2de0c337ad2bbb581312cfcb77f806d320d88103a1aa227", + "id": "7eb9c09a76d966babff5fef1db9a160af8885f4545fa1da2cba9baf5b64cebe7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2262177091195, + "fee": 0, + "recipientId": "AJzzwPzzyDCiDc23uQ2u7AMywkfzaVCcWt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206772491d6df99e5475ed8609d3a35a646b77fe85b81aa0cbcd5a79ea725a0f6602205b19e267638b99baa4990e16ccdab0ebfbe2a9130618c83c30ff9f8fd750eb2a", + "id": "fd90da851810e57219244396325da5e2064640daf9ba6114102aabff880414ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2266868808469, + "fee": 0, + "recipientId": "ALNvcAUuj1GRHaTvDcEj3J2LJrpbigMfTd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b0e14408162136a3d9a103e113d860643cb583e260bed70db986b548167bdd1d02201e3cd6711b3a5a47b07cc79a2a0fd24d701003ee54bf4e96c34feaae11586bcd", + "id": "997f9a8497ee44bce5675df192fbef70e582563d5a8d6774a10a38c1ffc870e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2271096947063, + "fee": 0, + "recipientId": "AKU5SETsMF21C4QNxSAFEuMtucEVZbHrbT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220405439f374319ae7818197cd605c29f981ec33ae5e5d8ff63daa6c28b79a12fd02204afb1feecba0339d1f6858f00f45053c4fe891a041a810df553c0274b8da8837", + "id": "625cabdf0b3e0ec7461e182b037d5050566ed0cfc96daeee199fec7372b428c9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2272050883436, + "fee": 0, + "recipientId": "Ad3NrB9ak5ASf4k7u8UsMUw62onx7t7c5T", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210093ef0463dc9e57cf30e412631760d1f8c9546fabac8d02a81d4450c1b0225fc7022034d6e9612a01b53cbca18c08c95ded5ecf9e16486772e910e857e32dd7fcc8d6", + "id": "56ec4d726f2f6eb3bdbf96201d7bd4ec0dc76222145e4aab469a5a8031bc0897", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2276774784522, + "fee": 0, + "recipientId": "ASdJV1rBGrv8hr3n6PAzyEstMMaeKRC9r7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022073089b31090a9893b58f7d356e2ab39709f213bc753efe142dce1028a18fcc85022027516343ec493797a210a0eded5d162f612c19221ee758925bd0bff3b9a38a4d", + "id": "b2f66b94ccca70109adff57f5b0327c01fb8b1cf9266cc5d34e67c1078619b6b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2287751179042, + "fee": 0, + "recipientId": "AQG5AyRdBMK1EiAxjtzumHBKTz5fQziJaQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203358a1b09882d61785e37ac06243189f37c429a08c245dcb9cd302d67bc23a89022036c0830ea03b7fc64d9d673d83d99d5001a39487d5207983a09a6b2bb45e8b70", + "id": "27f99bcaff31983a34701f2db5614995f09298ec428508c0ef310abca3a17156", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2318734742927, + "fee": 0, + "recipientId": "AHWbM4U2eC5G8GqhEXjKjyjQ14gE2uz8WB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206f65c7a1ff3c846ba9ecc55cc3485c498ea01d316a456d7044e801dc910edfde022057487ee7c70aa9f4920bd4a32759569e2dda24d493d427c1a1aa2cc92b6f860b", + "id": "726d10604f15f9e5007445f0b8f829d8811952a0b9f81895fa673d45e579a8e4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2328424475356, + "fee": 0, + "recipientId": "Ad9T3DbNPoF7SnCuhBEMQwHuiKjEXmhTFn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220725c7b21c8cf49ce86093c81ff5079c5f3ad6a7955529a52354e01c8b93db64502204463fa21a31a3cd7fac42e62f0e304ad7861d71c7eed6114e1c64cd37ade9438", + "id": "66ecd1311f19281f119c8bbbd34d6f32daceb58f2c747c32bac3821d1b22bc36", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2338875909420, + "fee": 0, + "recipientId": "AH5nQnHDEueDsMA1oL4AxTYgPkGgzrPPv4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210093df608b5fc963f1724fbb3f0e3e3c52c1642b17330988bfa45d7e28b143f7af02203b04d3b802038a17bef863ba2e2e17f1a288ae1764ef61e7410643017643f054", + "id": "1446fe4a270106969f516ddb59b0d145c4df5ef0c58aaea92e16d4e73d4edf17", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2356684778844, + "fee": 0, + "recipientId": "AeNyCDJoThuMtgGCqo36Qj1RxiLzp2wKfX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c7ae57e20b74727add16c03acabc3f2ee6e8e200651ed2dfdb973f3137cbad9d02201c33c46ac75f9d1183ce4a60bfdaf72021415676828992f5b4bb4a15bf8b06a3", + "id": "8e4d777e325bb6e75f240dc86f0def43fd27a1d7db6efeae8af457fb4a0e5a7c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2366781260994, + "fee": 0, + "recipientId": "AGog6GdAzTgGhYXuifWnrKvK8VAPaEhe3U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210097204cf3140ccc71aa07a31cc969dd5b532d8ffbbe936581b08914982e823033022063df68f995e5b8f76d391cf7faea9b34fdb2b3b26db5ec42dc94055efafb86b2", + "id": "250af0610058d8455a4efe16382bc4b0cb0d3206cbb288333ac3550ceb44cda9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2372499252388, + "fee": 0, + "recipientId": "AQRccct6eua3CnzgReDsYVdZhjJEGa55fM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022048fcfeda36ed20495c633c7b1e671a306df8229e2a7d4a75547c81b8af509742022066b8f68d2adae68654bb0fd01d56c817b45f1d5fe686c1a180fb53f9109ebefb", + "id": "72c59a55be7083859a7fcbf1e4b9d5e3f39013b292c812fc61a83b07d44aca4a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2381643926049, + "fee": 0, + "recipientId": "AGvdP9o1xDU5HB9ZfFEBKTN9Td97rka9GG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a95fbdf4d29b7d8d00bf352f5c0faae88fd0c6954119c1e7de8624c539efedd702200aaa95e4924771e70dafc892933be295c372f3d7614058f34f6568955e60e3d7", + "id": "c4be55dbb512485ddcfa1a44a64b77f72f6a623ba28a1ed74f7e164759921b69", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2411046937482, + "fee": 0, + "recipientId": "AS4BtWoaap3AVmgxh8cQj3xkcd7dkWfK1J", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d2efa679287190ad29fd7fbe91978ce07dd5819acfc9f08b12e9aca27c5d9fee02207da29d7168e36a88509b6d73344e461635bbb009e1203bff8c6ea31068f8b2c0", + "id": "dca8904b7776d845555706aa639fbaae9e6ae4d6d912c796a41e793dc2030d6f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2421793988476, + "fee": 0, + "recipientId": "AGErCuSAoSpZNQ7ubeV4HSuHrwoxGQoBbq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c9947449ab96e6ebafcae96a6c080669ce3045f711e0ea1bce651061cb054d7b02204bd9e3e1fa0bbb0923571ddafa6943c69ca2096fd82dde9a0fea4e3eace6bbdf", + "id": "c36be7fc674eef93fff38d284a209599ffab56eace2e569f9f05db0ebbc98e8c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2424820646199, + "fee": 0, + "recipientId": "ANgyLRoKJC7a6RLUJw4nkkrL1Rni5eYs5i", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022040b3f827c57219cac01e956cb5aec623fcee151b57225ac06edf7fbea1d86521022021808f0d44f5a50c7f35a25e40c0034b7831deb627f8c2922ded0b406a00cc63", + "id": "f3f300b5fb8de1f81c863737617f45c4b67f80fbfb7b72cb4a4e0423718df75e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2440155918800, + "fee": 0, + "recipientId": "Ab9oevZRRtixxzvZjVA5jJrjxi1kwWhUN3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201a6214822f6166833f7fcb539cbbca80debc0e0d01ca91c0f87a64a5dbe993b002205bfc9366366bca57d54bb5c389edff2e0b90728fdcb58ceca89984ba9aa12e66", + "id": "a6fdc2fec0e799ecc73e8310e8285cee66c1420f3edb9d915cdbe7414c23b824", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2471141445854, + "fee": 0, + "recipientId": "AcQgRCArM8s3iCzXmaNa4LFYcPwPUqR4Xw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022014be56285973e8fa73c89a4de8c1d5f06c30bbdf6d69c9286e19e18d3c00cdca02203a8777b4f3cb0a28cb42af527b3a13d284c8bb5a8c6a97b1f04c5fa3b7d42dec", + "id": "8dd4e91bcad96169a7d3e4a01fcede8898b36e45797a4aaa6c318f1c0ac02889", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2481166956848, + "fee": 0, + "recipientId": "AbKSfhS4rZQXKJjnF1egbDqBh1fnkoDADx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bd942f63f791c0cc5f90d50edbbb6837a1445b053c599abd9cb8bf89e933d96e02202e1620e229f7657fd1d87fa97c076b42a5a8a4b5eb6992cd2cf1308a4d2585a2", + "id": "cc51d42bf081b422663bcebf7c9d4676a1ec5fae1e4df76e7ef63bc9184e7c50", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2484636536440, + "fee": 0, + "recipientId": "ANTd31Ts3VHYBkd3BmbAv1FHdrYjVU939o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202aadc401035be55c7a644fc4204865a0b25c61c8ee7557c35c8d6908ae51748602200eee3eb04aaadda8f210440e269c03868d46fad3f80850c81f7f526471e7090c", + "id": "d5f9faff17330dc56236696dfb08548941a881159948c6aa100136fd416ae834", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2532393949328, + "fee": 0, + "recipientId": "AeZGayWNSzKRkeNxkHJFzHEygeXsh8eytK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022036bd56eef8476da49e4c226f6d5bff50a491a4b0d077f0320df704ec731b3ee9022057b53f23797d0cb167b534f88cb0004961c2ac0ca25adca7a83bb0eebc541cab", + "id": "b6f7fad4820bfd4a97c845918c3f7d39a9cce601db5947b30f0a8cf515346fe3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2576371936586, + "fee": 0, + "recipientId": "AZMvWheiHokcgXW1UBvepmCiSjtLESNoid", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200923d3e46b3595c3f9c728deb6f8f34cb5dc49e32cf5e9668d0d74e31181d5ad02202d6079625ff697cb987da8782d44333768b883c10407af365fb4cc9a5135aba0", + "id": "1768864cee1985d3e4440b710f7cc8dbb7bf095f57f61af22f4fe9c1cef4e1a8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2584344277365, + "fee": 0, + "recipientId": "AUpLiwm2WVWh7Pqeizt33LgjTfLxaeKQZM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d3cb2ef1d65dfefc87c26a36c625d9c018924faa17876acb422727fafab78c5d022054abdebdb5079c36789fe3f7d97bf736ef5a314555aba0632ec3511f6b72af7c", + "id": "644c4cacbd139ed34ad275cc8e2c5d147a4d3a5e8ceda34f93c7860cbf3285e9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2592139332838, + "fee": 0, + "recipientId": "AWEVGHr3x1e166sLRYWjub9tMuRmrwv5Rz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dba9d478044f5f59fe1c85f8528f1e0fcbe8ff1fecd0cffc13d09ea320a180480220086a1fa2d5f23c120b0b8e49753d8e4c9a1772f8b2449700e9987f7679acbdbf", + "id": "27a27a46a268bf1af2f7309c1d6d4594513c1e6188d68216eadb573ebaaec3f0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2596699852052, + "fee": 0, + "recipientId": "AVpBNSkQsqp9VtK1hZtf32ZBszABP6mPxz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201d4b144431415a853dcb795f3ead4eb9947a0780af7303c7f8f1bbe95f0bf470022025539723eedc56622cff150b1f6d65e5ff10ee29a39b5f29b77af51d455616a9", + "id": "d6adcf3c3c7307e26318198bb541bfa38f3b12328b4b71a87eb3a6eb760baad0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2599032418076, + "fee": 0, + "recipientId": "AaQDmLECcNXVzALX99oWP8ed8wrz7E6vQV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022052078242e033e71cc764c5e6cdb1f240fa434c674d8bbcf5253f17b2e4bdd95e02204a1095961b33110272cf1d944ff3cf147cf784f02cd37794205369f75aefeb09", + "id": "06f66303e2b00608900e64f219579cccaf060f93a50eb1ffb83a6af3374128ca", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2619541018549, + "fee": 0, + "recipientId": "AM1WQJyB6ENVcBM3qumifK1iyGgmxAtfky", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ec33ce506f26896c7cb3f5730859b4da1b52fe9ce4fddca9c17976e7522d82cb022040a6c88430b555f187b83324a99cea223f56be138e5de4d860fcf2a3315e91c7", + "id": "105f4cf709516db516b88e985518ccf82eb4939a886c1b8fe356ca85b2444325", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2625323003381, + "fee": 0, + "recipientId": "AbYapv2W4GTvn1XAQopCFPxHTmGuhZ7wrU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cbe7e43ef7a56ddda2308f5cb60204611e24dc16d1cd1c681a4190bb109ce8ac02202b41f1a599c6de7bf37d94b8105c618460729edb0235d3070948a002d408a315", + "id": "706cb7d860d44fd5ca836fa5667d81e60dfb86c0e8190850a1ef042da330e2fa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2646271028944, + "fee": 0, + "recipientId": "ALGc47qvCBw7dL6y5ruLrpWXdghQSMjRX2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204756f3e295f5dad6ffe44c0bc09e7a8e215acf01bd7fbe85babbea5e1682971802201cd1a6af8e4d6440405631ffaa0b77333ff31a4068675a4ed9e32e345e812c11", + "id": "a7b9cd14e449f0809494ef6a4e94e257206e3b339196dafa6cce9a5911eeccfd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2735324506659, + "fee": 0, + "recipientId": "AW9ZtarPjeMUH3abFtVvC2ECNTkMS4w9ZY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e1b443ca71f70cefd6dfd6cbd2120881c7963e60b9601d6793d46587bf64f8e102205519f08774fb4a7ccec42ee49cf10cc1e60c8bdd4b8f9b0ff3d403cf009022c8", + "id": "26d5fd2ff3c8975785fcbbefd6ad746f51727c273f70f5545e95d3fd683593fe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2745083256244, + "fee": 0, + "recipientId": "AcWyTqpf7XuNnptSCJ4cTyw2hqc6PhDufV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e1fe873519ee311dfe7114f754260443d272cb2faabdc67d5e13b3e22dce40b902201b2372a23b827ea4c3b588b9e55da4cb09f9f18f201293591bbba3791f1fe46a", + "id": "3ae55c8fc62086c2ab577dad2cc85e9fd025d8e035ccdd040857f25f3215a29f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2764221209306, + "fee": 0, + "recipientId": "AS2NDZSgogBv8K2PdDNYwWcU8RphnXARQo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200d7315132954c4ddfb3a09c6204a1dc0be0a415d87b5243093e8ca01c02057f70220377bb5e835854b2a1372aac469e103ff59fb023acb7b05abb259083ac0a695c7", + "id": "41220183a52980e1897442d243a14f13cc5f6e3a9356c1fa229b6999899c152b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2806744646340, + "fee": 0, + "recipientId": "ARsQwGzjkRCscuwe8dQ2MHPHSYqmbY4iSU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f71fdb335a27b7d37d9e07677d14e546e91f1bc8be8c5519179853be3f6ca9d0220712a67657afe2b13a1ed42f7d29f662c625b55b577a87d032e5a513d3a33e875", + "id": "5a83436df7e6bcea1e1221846e05315cf36f3c5c4b06d785344e8010e48cb64b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2906876974000, + "fee": 0, + "recipientId": "AT51Pc6EAJKjK4Sgr2tf3i2X7Wp6L1Nmef", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210084c2421ebaf0f1cf5c9ebc09454762d5e6b47291c0b88d8c644cef634c92d4a9022000b3c741497725e2659358254baf0cf87ca742dd189ed5b29160e72e9a7e8d94", + "id": "5ef7d8921886188473d15c50c752fbcb5db7d094efbaa8011b23929409a7e642", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2910898131837, + "fee": 0, + "recipientId": "AST9oifUBT4nv5kiNZwFrkTneZEyUshbti", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be3ed06ba98f260ce44e0049f1ea20d5efa532ec97e9c286a3dc6611f72048bd0220562050948f72a8747b5e9407033c083ecd60aa78621b0f8d7c57283eacd286f7", + "id": "f04b90545d3dfa0567f8b277d172f32c9cbd9de645036edd4d5e0e74c77267a5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2940154128213, + "fee": 0, + "recipientId": "AMZXd6g2nidHGd1pZFUqZVpJnNrpzqtCKF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201396009e8dc5376c341ea496e7542c2b96514819dca5aa23ca8dbf8efc1175ae02205a6083d4d66a15d511c672cf521d57ee61ac55bc876f494ea6450d2b7b5f6640", + "id": "46f24f882e0b0a1df1eeb6e2b17ad3a37ec3b9eb413d8aeb2af5827c1a16e286", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2940154128214, + "fee": 0, + "recipientId": "AHwkTdboEC2Nm6NwXoURbTzoNRPmMVXCqc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205f1d63f3ad6bb0ee39797711bd1939b72f6081abda7a3249ef9d690c4b2de86102206022e684106c6e47c8441c5cb2ca2e583b3e79fc2f8db0d6885ec2590cf88c2a", + "id": "f868bf1d98ad88b9fa263292382355e6e21513a7614bf35e0a413e94602b4f80", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2940301143271, + "fee": 0, + "recipientId": "ASBzgywUJ9UtciVtbYc512CDrgTDhcMYng", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204939d50212d6492e23e7f3ff583044ae1ad7f01b173966b8fa83491e2c94b7ba022033f1a5538b4790cc5cfdf0d52a093bfc9e579b4da08643c3359d3ef0f39bb34f", + "id": "202834102dabee749fb707c8f620ca53d51c5f92aaa4113dc1b86d7989433ed5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2940301143271, + "fee": 0, + "recipientId": "Ab2DLnDBmKqcwBuzhQUa6wWdCrECZ7MpTo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eac45b9e454362b7fa3852c5c8ce5d2ca19db0da27428f75543d6ef075c91b4002207a49026e26676748eb003fa34f5244e0d9ad7cd881e90d9adbd158ab322e82cb", + "id": "f608e70123ec8b29777cf6904ff2e530f8c150d8f59aed648866acd8bd250710", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2940301143271, + "fee": 0, + "recipientId": "Ae7NncFsoDvMMmVqiJPiTmibiB1aW8XYk7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207767c2f983946bed560b7ebb36101222275a8bd64f0c0b4f8b2fa2727f344b0202204222fa85950d3ba68b37be153d77ee9135a12d1f3a4ca7f8bf201d18502aa12f", + "id": "56e775c567f7f7c2c7e728221167d54bdd021f722acfee6aae640ff888c50801", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2940595173385, + "fee": 0, + "recipientId": "AaYk4acmLnvcKiiuYbxQBB71goY5Tn3Dm3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a0546e807d1390df8e97606cf30028b6145d287a93dfec9f5c7b9672d172afb102207325221156e2b4e32ad04c80691f073e1dc6402b61fe2aa7296cc7d424d584ea", + "id": "89d8e7581c333ef01ce0ded3dc08da5580c358e02444a6c37bb2637b53e747fe", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2941331203166, + "fee": 0, + "recipientId": "AKJdgCXk6ymHHKaVoNhkgwtsS7zeSHg323", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220172d555b9f5ecc5268d9af740ce9aabd35ba38a1e011b0ab05783148bce1f96102205d4491c92d5bef700518e4ba909025b6e0da94ac550af7f33f14a2a47189b72a", + "id": "09b3d72c0f9d6051e30ec2f6dce7512820a02c0d95edeeaffd79dcf90e461fc6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2943241444414, + "fee": 0, + "recipientId": "AeykjWcAMwZ8jf9UkS7NoAEX7W3kJiBWUZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008260a61c32c694392729d954b56d02ca72468167ebfe0c2ab4f96999606656d10220247bbc53dfc30f0e6c0267e7cff73f44f3794a77512b483b3a16a78f95cfa609", + "id": "524c4e3f8d8c26f3b78ffeec170648af3410bcd1c4ff3dd91b783cd9a2f229ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2975619527639, + "fee": 0, + "recipientId": "AGT3E9aU7PuT3qF5dGVQFwAG8zuXTd7uAp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220051f525cc7336031431dffad29a63ea2aaba824790c6db30ca40d529d77d1ba402200601f5acc9a39b41fda57e49a3d2e4ed473b323336f82b8c39be54cf72611de9", + "id": "dabb12f952a645ab435977dbe2353cdd2609dc6ed1a672394d547f0880d93cc6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2982935509848, + "fee": 0, + "recipientId": "AKU1FaKbLTBfonNqSAUidf6nWS7fyaAs8d", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a399bcad2f889782a418d8c4f43fd50545a9762a5afcc2f1c0a2584b9cd9cd99022033c4e961ea62a4db1bedf6dc894b402dde87328fc5426bf7080f8181b0220316", + "id": "ce7293fefd0f534c30b3c2845c129fd297d3fd3d4ed7c24d3e0ed490801eb0ea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2985301143271, + "fee": 0, + "recipientId": "ASgKLrpaJGxArbtTNuKHTQNVWyGitGGpZC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202de43275dc393daad998ce1dc35038299d94e82655584b05bf1ad3d88ed4ad5902206722a7a5753f18a2f280180f411bbe0be380d0fc48d95007ad5381f367ee3be7", + "id": "9a99bf84368d99e4a83fa780867596c6be71a181ab0e4d80b99e843956435c4d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 2991168353049, + "fee": 0, + "recipientId": "AZePtHJzYCXrdG4tF9SBHY6k6R25R8ZBn5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f5b8cb767a75c487bb2d1bc078baf5e9bc1ffa024177bec52cd34de5c87306f7022024806722c1dfa0212e49d7cc22c1251ed59d4c0fbd9963bca459aa0c0f64f521", + "id": "a36430d45917be491b50a09e86615f3d1a67af148173e7867509d704df2b1367", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3029944047194, + "fee": 0, + "recipientId": "AM8f4MdYwPFB9XKqvki7wt3MNBXK6d1EtU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022002187664dbc4b4203779ce0717e3efef35f94aa241477a5e326ececf4897e3d0022029c1e31d1591c2c54f242b43bb27575b94561bbb82fd726f3c54738f96c9025c", + "id": "e0c8f8e46c5377741c86b6854281d0056919cbebffdf4d641785826709879a51", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3030774378583, + "fee": 0, + "recipientId": "AJVm2UANQSZBH6WhTLM8ZBqTNDwmYf48GE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100988c50ea74333df4771619f0cf7396f7569ed020b6d02ec3bc3ab1b2e93e186502206adc99ee4498664293176a09208ca5631fb03147f6fcc330d9dd28102f088e8c", + "id": "90090e44f39b81ebfd82a3c3c68e11c81c22757c91460e309879913aac6e915c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3043211683285, + "fee": 0, + "recipientId": "AJi3yet6WjHiMNx8j41pbwNyoKfZtFbzwE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f0d5d871e104cb93b22fe9937872219bb52695f3be7508144d260de8fd33cb43022064656b7b33df040d9a49839e45250c44780cca7ee302112a123694fd5ddf2a81", + "id": "71b958d40ddcd3e70236969225049f99b99e012608253a52102926154e8e6eda", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3055154128214, + "fee": 0, + "recipientId": "Ack5VMNG38f4DCpn53a8rXNoijKCpaEEqq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200fc998d1c31efc5222de1447a8284cd94cde778da61ef0f13ec333196318fabc022049cb07ea8df855b9712d85f334e2c2fda9ba07a5d7065f978b677bd900e0f899", + "id": "2c42da9e75c92d6f66144898ef06ff9fcdc35853a8036788f3a52fa7cbfcf033", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3059124967808, + "fee": 0, + "recipientId": "AaFaBMRCVAF1ZBfJx9wgpZNxxANpJdJSYs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205fb4f163f5d2276083b58ce3c7a5739f74e751d86050c6d9bb10b2be84c343870220407388a647c4d12beac50154d72dffa01125a3a2ed5f4c26fe20fd385ad27316", + "id": "a05319b6d84c6991ade6f957ebfcd3193aefe5937122e7afb1bb7768eeeb4057", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3131025807748, + "fee": 0, + "recipientId": "AKuqp4G8AA3XWNe8JwMhTdNFsjhrWhQ7ZF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009c9c994a2e54b52e7dd638337ba112c43bc2150f740f470de9d5b72b852947d10220050af097f8ec27f51f0fbc9d052e5864aee99e7b6082a61e3f1488497febb899", + "id": "e521e3dfa3379d083020dc9c8ab5856bdac23ed0f412e1568d9704ebd73f4114", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3136161306270, + "fee": 0, + "recipientId": "AHWAQFqCmJ5RrKvicKzg4qEvSStWN2f4PF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c2673236c613f050e22ea59478067f803201b9c88b51a4e0215270ebd6ce17f402206a586fbc5b2e0e5b27edf94bca44e9b136fd35a7be96f5d12c2c3030286a8196", + "id": "08a2b9d852bd06e8a2158c95142c88624887ae9ec667eddd62b02036993c3d48", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3141084221939, + "fee": 0, + "recipientId": "Af4K4T6Y13ZyZi6hQxSuFwWHVRZQEhP1D9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008aa3a5f09a85a7e94a23beef8c95851670ebbaeaa7d251941a92a3f44420951e02204070c008e27d95e14ac9fd50741a889efa464ae4c740cb44791edeeab2815227", + "id": "391d96b95f7d84192a1f319932167a2d2457624222afdb0dba66eb4b3ef9cd69", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3182675621655, + "fee": 0, + "recipientId": "AY36AeEFHXi5uevkukSSY63Qv29xLtBNzj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c502e26db7ca217fb755fcbe660da9ee6c29047ddbcfa3962e6cee1c15fd213002207ac54ce32e991a0c654e5b3c684d0d2efbee9d2967f4edeb80ad823f4b19f3e6", + "id": "f5c46574a98b4659092e4193d2c05405973cf80be9ef391e824eadf89003f116", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3208001085999, + "fee": 0, + "recipientId": "ARtWsVUYotKEXTXxx1ZxCNvQUrQsnXaGSB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203d060f9f1a25f865c48d773b58228dd2ccc8061b9903039940c507dcdf895f5c02202bb37da139cd3d4a7cf5f677f1677202e28d0be38468601e829ccce2cb925263", + "id": "8a6dd2571b665a50bdd9aa12e5908b8b7bef290dd7e77365593830c0c3504355", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3234184242541, + "fee": 0, + "recipientId": "Ab3SAMJGK1fTrQAqoio6uxNiQqUPCmrzxj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210083d0d329a80d6ee339c024b6a8143dc1ccd2a4ef9cc36e2a2290f3edcdb37afe02203a76762e23f40fc99ad1bf9684a19d3e5060ab3de016fe025acdab08546ae83a", + "id": "ab3ddb2107eb42d4b92b1ed42c5c60d6ba432932db57b3f250c5ddef2e5f0b40", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3234331257598, + "fee": 0, + "recipientId": "AYUmyzDXiqHcTnFjtKQC9GHJzFQb76rTjv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022017b50c51bbc0d381d134b7fa916d7d2e3ff07819c217144c1969a993af614f4402207aa346585c21d6fafd6a31eae748581272ec2ffbcc4cdbf2cd12f258a113340e", + "id": "47d84153cbc76cece88fe01fd661a4fb388a40c05495fd2d4bbe8af7b650e1ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3240854690572, + "fee": 0, + "recipientId": "AKdcJzAriRFVftFqfHkrdZ1kYKtSajjVGe", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009956a1473b920fc46270c1b8b7b53ee4be22aad70d53a9e9059305f1a8d3107b022035e82d31b43b221e7a7da617454ad1c27a504ffb107cb4fb9efe88539b886e4e", + "id": "a599896c8dab5a4251366d83c44c108d21f2e82922a01214d24857c5d965c9a4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3264823516954, + "fee": 0, + "recipientId": "AMnGLCrRYXtxmrnzsx7bDzEx3G2W4igcx5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cc08ed2979526f2e9e0876e177871deb20ac16bfb094b240475efa11cdf5642b022005f4dddc0a80d1c9df8eaf04f3fa4dff3c5b349e5b4315d6f037f80ebf95aa74", + "id": "0b33f80be4c0145ab42eb74dcd63c93ef5dd8093b8cc2358182e644f12287634", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3307427638563, + "fee": 0, + "recipientId": "AH66WwUgJHCYvEbX8hTFpuKpx4UwzoaQEz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a243814491855ae7abe45fad6ea7c7e939903f198383b224091a25e22fb357f002206e4849d25af6dc5bb6e002f30ab0cf7f8cd939528fa4780256abe3f9f38f4419", + "id": "af232d8be6ed5cceba703d618a040d13db05286235b2610de8c4cda5668ebe8a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3324317837586, + "fee": 0, + "recipientId": "ALTNeLaT2mvuvMohvSjg8FjYhVDNxbeMEu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009f8ac3837eb8b6363c0ec7788dbaff01a07d9cf68cccad905a67a8016ebd24ca02207acf56e23a7485fffa201ebbe3c515b99bb4121170da3b0884193416881a23fc", + "id": "577010da36ec3f0cca45de28e26aeef46a3bb7fd932b755609aac454e9626fef", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3332461324329, + "fee": 0, + "recipientId": "ARLUcoV2hyhKCzfy25Lk8Y9VWix6deVAYj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220724da3de0142e6139d36004f68e5ab4eefe84b786c5c80d0374b319c3be8d6f402207276502244f50eae18d9d8cc7ef1636936809fa9f6d72461a5bbe91db9c8c04f", + "id": "65306f6058181b91b964039ad1c18f711fbe7b7d9f318d33a02bc1acdc7618c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3333461562845, + "fee": 0, + "recipientId": "AHFojRfYcbsAhEqvKY8MXMsAc8MEkxwZJU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220179bbffea812db28bea6ddc3e3863b6798af30cf9b2789d5f827201fbca974d302200ded60e75ebf3d9eb1f947bf5dfb216e8206a4882eacdeb7269c728fe47c0bfd", + "id": "705f8aacb1c7cca2391a4d432053872f3dc302d9ec36f1f399377e1b10ab1347", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3337241797612, + "fee": 0, + "recipientId": "AHRETqnSboGYrrjQ87Bnu5ovyKZBvfuS35", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009eefca56858fd766265713c444958cccb5d615b012747c011c6f5f355955c60402207cab37ed16c6f328ead3ef60c35257cfeff84c0e36182125822142f2f4a3c471", + "id": "4f16617510a64b071be9c9aeeda35eea4947d52625610d7c8af2d47dc144632e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3341104284118, + "fee": 0, + "recipientId": "AXNH4NZ1wsrkZ1PPFfSQFucDGwt4sfsGor", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e443d4e4a9d3f91f9fddccb71e70555b5376d5928da292c4466f0fb4bfa7f12c02202b083a62b3e4d139c3bbc09c162259ea43c256fb0cd25870b724cddb4d7b074a", + "id": "7664dfadec3af8d62814ad0baf545d16c8aca1be546758a259a05a2115837cb8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3359667184998, + "fee": 0, + "recipientId": "AXDfDrajcMoF4ssBKAj72AefFrS32A6ErV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eee29af59dce2e725f1441b665b737c9bc22090bd4b37a68cb79c29e8a041a28022038b10296b17315ce07bfcecd521104a62a7c1598d07ef17fc397aebfebb094ba", + "id": "388dfda809f3802e4f7ca49ae7da2409e195be0b231c037c0c0ee139227b5e74", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3411370798936, + "fee": 0, + "recipientId": "AMg61zEWGdPqgRcebwuY3nABTXcEf5ursg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ac107612d5b3790a6640e52d8d5cb277f32c7ce79f3d9881641b8e7b613213010220353d4bd4188fabe59e5d8ac78d4135b57faebcd5e8095c1c7ab8d9c1cdbb6b41", + "id": "a16f6f3d94d84c9e83cf9ab117cc17257632d3b7548fe779f21bd02e0cf3db90", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3431641346204, + "fee": 0, + "recipientId": "AV4uwLo1v4g78ZWWEfZWt3zssgjS3HUUK2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c6ca22e986389d94fa3da2973a1bcea26492e76c8fc8ecc47d2f754ee21e0e65022031cb3ffe69ddd0d63c0c40b089fd445748c9b0527e71d71970f2c1e987670320", + "id": "71ae8506d64400ce1c01558b7dbf6eadb9b915b8576f8024ba54929f56273d8d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3472495650202, + "fee": 0, + "recipientId": "AREUm684gmTkURsYWAb5XFSWjtUpnkHXM1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a6977f3021aaa15636b427ecff64771bfe698cac3e6ab1fcf1113e927656b44d02203bdb670754598eb986329abbf9afa637afced4dd83415f117ffda3a40d7c2da1", + "id": "e03a176681d276f4c1a44b84923d8ae4458333954e8917c67cea9b412d743ceb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3485679678910, + "fee": 0, + "recipientId": "AeUU182Jnq996yw8TZRaDAPj4khgpxBjKY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022006a3492b8a039c733c6860409ad2b6639d288f4638863a10955502162aecfedd02205035b3a64ac33b34cc81b07bdec6d74dfd14b28639df434b97cb55c1eb15a9f0", + "id": "aa9f40571467e5776f3ff8378598356aa77cccd4ae6d20b9911472393a3aaa23", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3485679678910, + "fee": 0, + "recipientId": "AesYuSHWM25csWzyJE5aiXD6yL4Vb6G1Vw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c4de585a2f48a005b07f3cce3a97ca0ad829250496930b3bd2410b0740b8ae2d02205f46e8dcb4cae8879992e1352a3bfa5c947706c36bb8d288516169b536f3299d", + "id": "0116ede2fa14adc70cf6286d703f9dac6d7d29d2121b62a382a197f2e0c9bcc4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3528361371925, + "fee": 0, + "recipientId": "AUxFccM7Y1rX9rgzi7ashGogxk3Am2A5px", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220750199ce472faa98d3eee8da3efcc0e4a381473aa1d48ace4731d7dffa78fa1c02203c91c647378029ce9944d5b647776989c67c7b3e153e7f3af0f33e709c9d0954", + "id": "8911fe01cd1cc981963d244e82226027890ff383183acadde7023091ff3ba196", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3531301673068, + "fee": 0, + "recipientId": "AeeJBq15fTimUFojH6XpR4FUAZmDubP43u", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100933a2422b0d1ce2180e046a6071652758838311737a3424a23ace27474eefba602203e72e72184db462d5af7d862d1d2d20b8eda36d9d5df6cd9cd308b6868a7604e", + "id": "62f6c02daf915e73a4440f65838b9bdc7cd4af1cd3361d7e3ece2755643a753c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3535124064554, + "fee": 0, + "recipientId": "ALfWJY9kKj9UQkPG9WpmaD6ty1xzy19MH5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022056e64e74a7762a00139bc688a97421ff9ef58d1052019cd594855abb279de55d022075b33e73eed827a243c96a46f97bb3ad1c3998407d84a8d557f61f3e93fb08e1", + "id": "785e5330cca5c0a53d97c3746427ad1dd4e72d75e64567ebad677d00bcb3b8b4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3556280867780, + "fee": 0, + "recipientId": "AccgnHy72MjgSSByZcWQqmeRgMstvJcuYb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022063057051b56093a07affaed97bdc4c80fcbf7e3e9f1ca81d68347176e39ab60b022054765fcde969a830d58d67dc80f1e2610ced4e6275d3c261b44542add4ff74a2", + "id": "fe4ec15b065062d5662f9d352d1e1885ff2f907f2ba0c6a1528e98bebc2bc32d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3557938075605, + "fee": 0, + "recipientId": "AVEz9XVtNWEBn7DKziwGAYRFUcaXetU2y7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220603807fb9a4ae361dc3743551f4cec9f78bfc9a5e6bfd00a7576196f8970a1230220586ce37ff801f638feae33e0449cf80c4a389617fbae8712a50e03e45b6e11ce", + "id": "f0fff2d878b1fca856116cebb41cae5a5cc0aff75d6b1c986e302722a81feadd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3593815899623, + "fee": 0, + "recipientId": "AdWUn8FcTbrtck26543q1oGyaSjZFWu6no", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022045025274c42fbda104625bf2cf9320a01045aee16cacc809074ee6d315cc17f7022036b821e1e26e19cf0a34bb6ba82a3d1e5c164990f1257119677f81e019bb231e", + "id": "e2ecfc0a1f1e5e47ef99d7de7963e51d2af78f5a3671859487a835447cdf9353", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3599965360713, + "fee": 0, + "recipientId": "AGYEkwK8grFmYGrrQkNSsEbQTRxSv1QGBH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d44a62be1cd41baff04021452b670b11fa4466a50b19624e918c56950c0326d302204734db4e45f8c2fb42f41fc3488cafea5187c051a4b5c21820b864e46336cda5", + "id": "f851283dfdb3e819303fe22d6accb80c935a06112ee8ccd6da917eeda8d9e2d0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3637230969298, + "fee": 0, + "recipientId": "AXtwLLdXbcAhX9j7YadfNoAbEAwmzUDGe3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202d65e277703ac1505ec5becca2491ae4cc63b47af9e3c9bfd4610601b14e17dd0220659e5031db6fe356fd2297cff629fe3a67f153f05fecc76a37d12ec81c7f77c9", + "id": "737a7570bd435e95df00dd9aea0d8dd9ed6a2d6715919814d240104a31cc29d8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3637230969298, + "fee": 0, + "recipientId": "AFtvTj3CU7Vr2C3bDwNCDXDpbqE5TAR7UD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008d5661fd7a233b744682e4df941f880b2a499996700246100d94b196db0297e102202c1086cb609d5d24d960307f3adf858268cfa85608ac887b6579f48fdeb2efae", + "id": "8c2a012cf5e9de46fc73d5cf899a51508af66e88951d8d17d9bfeea7b53cbd7e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3675376429087, + "fee": 0, + "recipientId": "AJqdLZvhCLntP4asFsVa7BfxAzvm5vL1TD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c69c239352a791354a5be8627cfbe1c5d266c11bd6113db1d7a156f33ae5f819022053aaa645c823507cc6bab83b87fda3f3e66bab8e69b4ca2ff29928be6a7b7a16", + "id": "5b19f5b4f515679b9e1dc6a3624a2dd740595f48dd236076fee85848ed65dfa7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3675376429087, + "fee": 0, + "recipientId": "AKrejHN9mQwjGALapuuf38wjs5TTDZGYPD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022058f35b0134f4ddca74ab3e3d3494946276c15748ac629b4949c1132bbb754dfd02204d6efde7d964d9f74327077b2f693642b5652fa5dab7de8da16aa5a587c04c1c", + "id": "6388b60ea1d6fca35d97d365f06309d93e211362aecf97d0bf4771dbe192006e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3675376429087, + "fee": 0, + "recipientId": "AXH4SRRS9WQqYW6tr32eDScenDgfmZvn6z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022078cc3fc5584871fc50c9df131c162cae857088ed96cdaad082aeb2bf73a7237a02202175cbca5a39833cedc9c394e0c896b5eb5c4369748e021b588af40539c80e1b", + "id": "e64991d57370d9e29549f930e3c72ba1d2963fea228c69d07da985f20889992d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3675376429089, + "fee": 0, + "recipientId": "ARuZqMpJcTxfyqs9FYwN5o4vHrH9ofLvnm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207c4cd8d49f9a5babd6d545162f31da577ec194b1783c1cb7a518ea922c589b78022053d81b6610d827dd8e4ee4be48d6ad9f397c28711d8225981027ac283aea127f", + "id": "bbd93094994a8367c48099c29dc4619dcc5da73cf63caa53b1e529395b6b9a6a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3697295037611, + "fee": 0, + "recipientId": "AZE3bGd41tQLJqtP7kmn1SUQLe4ULUpNtK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b7153b2711d4d93933b0d9de3fc66ce2d459464e57fe7c6d502d8a0ca6c8dd3b022027915527227c59c301929b18c2f04b65d58d6dac510d9f9720689fa3bf8bd180", + "id": "c8cf09babe8f3fdfc44ba66a538154418bcb3c3b4c960ddea6d710e893bd4337", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3706851016327, + "fee": 0, + "recipientId": "AXMnw2SyrKVtEpkos4pYLqLyKS6nzBjWL8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207b5080ed8fa34486c079d94794804e2bb8fafab6c643507a322fb8f1266d4f9702207d80e5d261ec51e6c371b2fa59926163828d91856d8c0b482bd23a03a991e32b", + "id": "8dc1c97b5fd6cc3002868749b97114243ba2a950e9b2beff032840548a875f13", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3709028616718, + "fee": 0, + "recipientId": "AWjDzWr3whEepeRDHMcLSZzfrYykBc2e2V", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e660c72942d1e4722f23166e5a781f5ec8ff9c264e6841695d9799934a9e859b02201a2e7e073c30e1efdf4b1fd17d3ef33914276e7931cde622c42dd3a90dc3f7e7", + "id": "4e83f8b8b7acfea0f43dfba0f8cdf8a8809eec7717d4519f8ba762e7328bd338", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3822391486252, + "fee": 0, + "recipientId": "ASf6GkdyPRoxAQ9e6wNu1eTcGzdCxTwU4F", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008980846f5d043bab9bc69d533094c47209a08b6604052b9097e97d3fb884828002203dd82abb96ec6873149fc3a1f274b04792c30dbeef84d15d274432f91b53ebc1", + "id": "b25ef9750e5e136d27cd99248823856666cb496d15c45af37ef77850791157ae", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3841576429087, + "fee": 0, + "recipientId": "AdJqCvp1xnvHCJKRBYDm6Pps7Yn1Ly4gnJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ee2c9b676bb97f604b5cddff9c35cd2995b9ed514d71eec031f606fe249cdd5902203cdadf32092513c27107651f170ec3209e52e2fdbf5d19fbc7d6cc222ae367eb", + "id": "4069a73c1653a4dd78863adc651caf01c46b1ce2c603656fabc4bef4e15cc243", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3912040733393, + "fee": 0, + "recipientId": "AR4SrANMGxGvnQNzBtKo4PMiW6gpfnWvrS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ac2967b732aa360a24d7f5562b138970d165ff8f5bd6c9e8aa8e45e69ed94b0a0220360bce61f2bbedfb4fb7f12ca83b1d9cc803a1e39512b1fc5fe5667f12ac740b", + "id": "2eedf249fe84cfd6b31f3090424050ae839909a12a3c1e39c777bc2e98897b7a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3918939514059, + "fee": 0, + "recipientId": "APwMLxdB8EHM5XXKG3gfT76Z939dLz1JCz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d3efefdd5f23c7d432cfd10d2519ba3dec0cb225c4df2cac9435db8a71646dbb02202e46cc5d7721e3dd8ea325ca859b01899359a3a36657fba4c93f202ca58ffa89", + "id": "d51629cab0261f943ae8bf7c72d8f33220cfdd1cda45a5f4fa5407919e9af420", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3937230969298, + "fee": 0, + "recipientId": "AU412AFph2KBNp1ZUNbMr6UQP9o5NSf55s", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d38cb807e50f55665d84172ae9f51ad7496e6afbf2128f7d79ff6b245b2dd3bd0220315d0693ad470da6c87599ccaa083645aae0bff6f5ecf9f6c540f2b333e0dae3", + "id": "04de02148852008a6b34df8e5c3ec16c21d4d84acc7176ac5a4f828bc14e16c2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3965251953261, + "fee": 0, + "recipientId": "AGQ6aRNRA165aF71WK26Wd7WBn26d928dC", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100996ac1644a8e8fa34351933824de6c784aec3656fecf58b00ecc73058100037202205b82dd95182ecc1b5933392cb43106c56a93faad5da958145e55cb066d055b66", + "id": "00887bc4d7329a1cd596625f9befcc07758c39b425cbc173f70338a62455f8dd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3970035705567, + "fee": 0, + "recipientId": "AcvUN694h6sjffQYucMLacGpPuq1ZAupLp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fb1f077325d32c64f6047e8bcc8c4110e079af9fdc76c171d1f8a0abfb32c43a022063f5bd3195cd0985a836b925f56cec34f3331d1fa0a9f0c0243583b7f785ba01", + "id": "db7c86a83a6e9fba3775696e6d06a5051cbdb20b329cd9877358d8e5bf3e1545", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3991462828751, + "fee": 0, + "recipientId": "Aes2M6fkCxfwRyguG6R7K234a8aB3SiD93", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022059bcfb711cd879f837a28c9ab64e342750b5a3bcda836c1aa232ee472004eab50220609448b1560201f56d821c2bfc180e08de802a8349b0f132c111cc5ee7d0e6da", + "id": "c797c9e6646543db243df8ca6f595651745b1262062762393342dced20eaea82", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4000000000000, + "fee": 0, + "recipientId": "AcomwPvjAGZoBiCx3u4e7pnHaXxeLnn4MD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022024236bc5bde500949d5031c98e8b36f823e00fa915cf01773baad25e20eb77b802201dedd50e48b684a7934b341f4e961bcb703cbdaadf3047bc3a63179992c80367", + "id": "65636f924d10c01ba4289d064623dbc2bbb19b7e6f5da944635edc38442aafd6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4001132109901, + "fee": 0, + "recipientId": "Ac7qjfgoPxS7CvKMsGgPY9qPjy8MRsxtFA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f4622b4a0e92ef2b2e120d951f547a4bd22afeb383700cf21f66cafda92d752702203d815814d38b4885ecd59e747d6fc0131f3aca76ad6b8f795b8d7d05a13fd8e6", + "id": "4b66a47cd8eccc4247e661526a5010aef5156eeda176cf8e0b1471be657790e7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4015989936987, + "fee": 0, + "recipientId": "AaHvdU5zBUEkqUebphZdJuRQx4K7QxGBc5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f1843d7b8f388f8fc2130ac2f5b5721b98e6e1905974cee0e5bee71f7bb5359002204ee81e364ba06b6ae5fdb9ff4c3772291dabe596664d64cd8fdc7f9041386b9c", + "id": "cf6bac6a58bd66f9820e8b92a2b7a093d3c874d652f81cf3956c11d785133f82", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4042914071996, + "fee": 0, + "recipientId": "ATBpKuXuUDFEo3uxRDJpZYCYvAuRxedMQP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022036b9cc8a055c30695316e968334a488c1daaeb796f189549ca5566ca677d4be102205df0433a63bc80d39594ec5aada62412a8776468c5d8996d7c670669f0819070", + "id": "356ddf4e0040d24014d624050eea27429e4ee5002b07977cdf08878e93397531", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4048316026508, + "fee": 0, + "recipientId": "AWd7KCkz7PAP9K8noozmqggx1qdWNHxoa1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022064ae748e8002a9118a3e4bb16d56dc25a8aa37260cfad3b5ff30bb0fda6031b902204fa1d3f9faece8ab66054c47647ad8a863759f0ce3c3c36edcc1e33162e6bf80", + "id": "d37818308a718d3d8ef3c3f4364a10b354b63af68840195c436dbba2fa18e344", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4131665330066, + "fee": 0, + "recipientId": "AMuFbxddTGnj41RK9QRCVymbSbdkxrJBoK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e2de836477641275c567bd1b574bd277216d355bbaef3574821b70ee55affda02200e01aa3604454ef602992243324c66746e0ad04b91dd65f0275bc349f4329faf", + "id": "d9147524e74c26eaca62684a3a6a1876cfe97e71cbb85b95dc5490dcb1ba094d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4131706645547, + "fee": 0, + "recipientId": "AZ9hZpKCaaJ8c5GyjvNqWVxKoiHj8fUkxj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220618744db99463616257c71774ccbffbad439cf0cb6283b556a8a7fd98e54da2502206456d52a8c767b60e5de2f3280f852ac049b66dcec4f9e389b8e801ce00aec07", + "id": "265d72b2909c0d5500b9dcd4346f030c32be27ee393d755a4dd017cc66094e4e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4153588271639, + "fee": 0, + "recipientId": "AK9RL2q4zGWGbLtLViUHHdrzGbY19JyVWp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6adccb21f415791105482edc5d6a640964148330fa534d8f7e2e80f24b4fcd70220246d7278626b4b645018240af6d11d558a086e9baa7be5f4c02e47cfcd28043d", + "id": "be1d5b2d28236cb2e1551a702bb633e003582007cef416286b54553d1b09f13a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4209041086592, + "fee": 0, + "recipientId": "AQqyqd4wPnAceW8SNg2Jwq7kw7oJdbu8jX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ad4d36816a2710a030966ae3dfd7964a36e3d6600877bc12a5787877117b160802207c3bcb1abf4acadc60182ba7c5f242cac9276382fdfe31f8cc5c1d34128497e6", + "id": "0657d38310031c46d2207d04760de4dd9768f0f88643c189f234cd0f09fcf33a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4241270774064, + "fee": 0, + "recipientId": "AQ77PDtC953vqib9FNM1QxgHS4c8hTKbY4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202152e05672aaffa4dd6728794f48c789449778ffee43732fd04a7daf0ea10fa502203d3b1e6d34399f84c96363adbe3e486c4b766e402387b1c88097794f5d681f47", + "id": "faf38dc7c24d054c7b542ca52119cd5325bfa8e48ca24488e259594c72c4b6a9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4335474035753, + "fee": 0, + "recipientId": "ARNrAn3AagtTUXwwPgfNMkG42om4y4tjXp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049deea24c707fdc7650d34b049bf69911105a2591b93789cef09ccee23386f1a0220693f7fc8a1bf4ed2c42fd89dff05b49ba16efa2b607dc040c99fee81fd94fa8c", + "id": "4321cc2b30fb9c56a2d3dc0e68cb711fb3706c2bdea33bf06f8d1b532bb15ba7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4362372912987, + "fee": 0, + "recipientId": "ARqijV9qpJv793nYJpSuzhJJtgGt7F5vMh", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220330da9b78b7aa2553cc5b832e4780c160d6c2c9177a2c07240f7ec4aac39f31b02201b6a1e5f6d7003d1d2428eb6d89c1b4dd896a083fc37e136bb7a030322027d2d", + "id": "ef30241456071ab5739cbc01b77c888de52548ce718d290e17ed78dad3ce9de8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4395705996499, + "fee": 0, + "recipientId": "AMZueSSbjv5eBjytCubTZy8gkkFov5GQJb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220201fa8fdb645e5a6d8867dceaac2c15a03323f7df042a1c32d2cc0500451dc9102202980c35218796eabfe7755d019448caca47b7892d756548585cc2f10803ff878", + "id": "b12f8bef995ddc79c9ab8b62294b59ca171e59af53daf5b45173758fef8314b9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4410451714906, + "fee": 0, + "recipientId": "AeeTLvBUBeVaWNW6WU4dQ5RaM1XkFvAz7Z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e82506b38a0e8e95f0bba2735b71ea2d00ec3bdb4a1e11519960933acb202f2402207e93347b0fdc6263d6a0725cafb8a15cf6fd82dca3033a5a9520408b37dfd3e0", + "id": "0f41c6cab1711f15519c238ad3913e0f99f45604f01181bf312450fe142b42f3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4410451714909, + "fee": 0, + "recipientId": "AeVsSShGJR7KWQg1DHKEqev1PDSDREiDfy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a53783236e53c4d54ae16e2172b9152cb44d78194a72a404849ea9d36e783d39022030ea460212b5b2ad93542eb7e27806e6b6215f9d005863297e9a55814071bbcd", + "id": "41666cc004f1e16061027115720b89eb3fded76b0032c3db25e90178fa158ace", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4411611467971, + "fee": 0, + "recipientId": "ASp6DK4Fcrji5LHRFCwx17YPG9wdnXUnq9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220456e45ce3c5fc8ac7ff423250d7ba6248b29edce819edf5e1ff2fad55702e70102203f1a313fe9dd3705f217ca2a2515cc7e26e316ab7bd4228dce7e600a54f2d98f", + "id": "0515f9a1e521fc8e692c197a0467ab01b300ffc3a4780bbbd5faef682ce9514c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4415891272021, + "fee": 0, + "recipientId": "ANR1PwuJc4o8Wp2qpc6uUk6wWjX5QRhX47", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cf11b29ca14aca61107d25fac12d9a994c965392dc0ba3e1177c9f91559132ba0220266d7dd8d55b8eabe70e95190c53e9cb606c82b3d4c4f4b578afa933873f6b20", + "id": "ccf658c1b13a22aca678f05fff271fb533459d73fe00e361d1b1b96b64fe2d43", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4507507351910, + "fee": 0, + "recipientId": "AVF5GuU4tJ9qd6YewAzDbN4sZkzFDNFgUi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ef1c683d99d3c0228c8af10864c6981991f0cd493262303514d907e1a2dbe46802200c087f8aa3e4f2aff028e14664e5760873d753d7f846eb0fe1e9e30fb885eb13", + "id": "450de1b4d45c627fb948d1badd4cc8a70c89f3436fa5e745b795545b1ef06927", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4531651714906, + "fee": 0, + "recipientId": "AcKGYwT1xwcWveaQE3dBqqqqTT3nnfpKbg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c71f196cf735ce57f667d19e98ca7296c9dddf84bfa634318315edae96a67ba5022062cb8e1fec6e86e0f79e50b9b0c52a943a772fda7f5aba0cac56eca7cbe7066a", + "id": "d679cde12bd6e8ee711e0979e8968a1b97d6ceb9fe19634835355733b0a012ea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4535436795104, + "fee": 0, + "recipientId": "ANPpeB8JWX4gfskbcev8a6RYXZSSaMs3pv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b86b575d515ed5ba323eb805882031a307b11d63c65249dbb52d20bac8f41f6702201ecf99a70e608349febe6ff6d33da93336357df5f9255757e9096d1bd1ab0743", + "id": "d496570ca0432b0f7d71fa40c94fbf7f9388cf27d7bfd1c18617e70c96546be5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4641265354653, + "fee": 0, + "recipientId": "AXyPubrCRRig234zKtDMzzSVrPKoTR2vNt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220312e3a6206e914414d40c7011162de5061089520ed5dc02ac0f88e20ec1a52e2022069011ece4aa149869d180c118870995de0280b5343791f0152705094f088cc4d", + "id": "afc269e369f0a701457aad91613679e5151e0457bdaa3ca386359882cb3ece67", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4680692119983, + "fee": 0, + "recipientId": "ASZgEyR6XZQ9RXFoUtuyXY7gqHbC64vkU2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203c43b599138e87457b214b1e3d1c1e83bb699d06a01b01e6add07df59b4c5b42022079f8d76bcb6bec406e86a5acb5cf2e926af71349801d6ee20ba89287c432dfc1", + "id": "121fc9d4fc8e2cc2f95508394b6eb20ce3593cf6c297fd4e0cc7d48d852719cf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4691051871764, + "fee": 0, + "recipientId": "AGa6Frh3DWJMmAsd3Nkn4bGt7n6bFwG8qw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100808d7e940940c758d49ef8fb12660f15efa0c0cf5ccd391805a490ef877322da022005388d608bf563be295af2d05a993b2b1a87c93ece23ffa0f29c1bb412104b85", + "id": "0ac956b61b69b38a04280abe2c372042dc63a8267b67c4a6f23b7870f88a978e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4697862065215, + "fee": 0, + "recipientId": "ANQkpXR4MwEdPpgbEdi3BaxF3qQMTGo92w", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220522fa6db9ad2b0d1a899e533b97002774bc90d96495b2493c200eb777a52da32022049690e22275a24280cbc7df79b99c92731c8b9b95261a07ab01e02c295f2ebaa", + "id": "fc6e5363cc3ebcb775be0446dbe8add45d7642f58ab984cba460ab5d966116be", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4807392369248, + "fee": 0, + "recipientId": "AXYbSuKBYev8pU4TK5GkcwxWXWsqFL1Vvq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ad8d8f2a9ecbd9f954853681c27b5a46ac43b0db909544e6b23763932aad2f4022075e36c1e9e5260f5099f0fef9748071fb3e3202c82b0f685f6e56497c0ed929e", + "id": "a0b9708d51039b38e4285f869cc4ee2e29f620f84837bf3147d09e7dbd91295b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4900011855261, + "fee": 0, + "recipientId": "ALZLdzs2XH7Ma87nS6qnGBvipdXCwXEcom", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022055e239d43d618d29581141d520ea39e940a627d113d417d39b5e8600965ea1fa022013dabd576ca157cf2aacb55588570d6abefa793db90617f9b52b5f8bdc5c0bb9", + "id": "bde6c2c3a29e7d20ef59a478faba012f38555663d3468c12748c1d0d5e70e292", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4972512330701, + "fee": 0, + "recipientId": "ALT8t2gVWLdEgc516c1QTXbajnsDxTvrMS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c4bed4d0ee6867797c4c8e3c3a15d169602302dff9d0f60a683881ae0a2cad7b022067e97a1f63f524e3037d8f29471b30007579fc372551e0c8e02f2924d8f5cce2", + "id": "c2f507727122bd6a0d0b78f12a8dcf1169d9f16187877e64bc24daad9679e0d9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 4977195555049, + "fee": 0, + "recipientId": "AeNTL1HCB6ZBvKLHVMpHnGqVksTDktSCfx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a0a35c51f045c7b5020d616cedf80411457f899f160d1edc5a326f827b886e4e022009084b2462322f577fb92d07b220eb0d5f2c17e52838faa7ef0dd9a0913a01a5", + "id": "c0d6bb7397e069bf8fa73f3227860c1dc2f262ebb274d8ac79bc70ee5e6c5129", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5000000000000, + "fee": 0, + "recipientId": "APBJH5q5UPjfekwkaMemUzo32WsAGPLgC7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220668c6951f67ebbd0aad2929ce9b00210a7d9f669fdcc1de95d713264a0630d420220630ba108e8db883d532c16fa21901ff3fd2a150bb6473d083a859416164fc862", + "id": "595117b36fc9609f6012d0afb10f3c072301bde2b65ee67d4bd5e00b9ea5b456", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5086720977858, + "fee": 0, + "recipientId": "APiXiBgZ5Vk47zL7r6nXbb1feEcJw37a3Z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220532e792b6107cc509119c9761433c93ec9ffda5cc5f4ebde8b0add5b84d9960b02200ef235d5bfb86c9083ba1917160fe33ec880f547845b80c456f4a2cd2ebdd380", + "id": "2e02e34a648a1136e0237953a75deb9dc336bfa2851b764d3b419dea851b3688", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5145527000724, + "fee": 0, + "recipientId": "AQvzCAZgwfEerwR8Wbi2jtLRZe1xckgwnz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022046c8c7d959a7e4a01250674d44fcfae8d2b1a1d1beb302061f98fb3baecbcca30220795103528d96a6b86575d359c68af22b864bc6e1c412e4501bbf80d55690164e", + "id": "29e47540c5c0663a2a8612e77717256275a82a29084a03ec6b0fdc940ca3b792", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5145527000724, + "fee": 0, + "recipientId": "AUNQekNveCH4o9EGHfuMAc45P46vNAkJWQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210096a8e326a4c0ae121e4ff4428d3eaf00682d0894367cf354ecd68ac2063fd14b02200f21db687cd6b747dc4da81857fe7decae1dbd7d1e1570c8e609a27b6008d17d", + "id": "fc43db98ac4c5b9f1b3687e91da1f922a1328e4a33b3d5fead709911acf6c810", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5152228598785, + "fee": 0, + "recipientId": "AbFfKLj8cbZCpD1dFcnx9M1quKbGyvY72S", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200108edd763dc24457372d2be73c2e0bf08c551e81646221bd02c298222bd3f6d0220304c7a242cc0b1c97956e7ffde176e16c89bf6bc024ad2f85edf0b531686b235", + "id": "ca1eaa9b36e044f32ce3d08b3d2a25bf6fa71d52efaddff1fa1712eeadf76413", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5165668063555, + "fee": 0, + "recipientId": "ASxsKmBbAojE934PbqhJ1mhXXpkdrEMGi2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220218d87057621c85e71f76523b123fab334c76307c5ac2030c572ded289d211b702206a80d562a887ad92a6ff0f5ce69202578f382e55a79665526ae3a38af5908f46", + "id": "fabd7438e73fef81feade0e11ed6da77e2eb6e0a4962b9f945cd6288139c37e0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5229284067939, + "fee": 0, + "recipientId": "AV5Ap4mdBEJKpbfUZQYZWxx6Q66yJv25ne", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022039474147574e5fd182277aaab990a51c8254d41ac8d8740b646dc26c2877a2d6022033eaf619263d1b0e415c5badc403f76a4c9851d2de87c35f7ea0c63d35840a77", + "id": "e03816fb6db7a11b0bd2ade86f9a85765e3ab7a94e2906b6c0e30e7d8e0b5fba", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5292542057888, + "fee": 0, + "recipientId": "Abos1R3ZsDqgNa3T62XQ2gsyE9ogYs6fPi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ea11ad93fe457b16aa68e3b89751a8556aecc737d0a5472695c8731beed5821c02206fce4f51c29956e7c996ce0b9bfb75702c90983b4a8a59a3fa3dc75c06a20ef2", + "id": "05ec9e7cdfa7944660f3468045a013182795f23b066e02d0706be47cc49fa88a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5308261029410, + "fee": 0, + "recipientId": "AGqBNsAT7ndLy25CcGrsaRCrXRzwhR3gLY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b2253589018b40a1f81e3a216adf75f1b6498cb9761023f6bbc0f120c21ff20f022042596e6765c285aa0905e8d173880704cb843b1ebf97de0e92ac285d972bc2a4", + "id": "c0ac0b134286a830d4f2a84e8e391215336a5f1e46ef05ce9a1b7f47da0729d0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5312153866513, + "fee": 0, + "recipientId": "ANZCH2mAMCUAA9TJByBgt1R9PZp8j2dnsX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210092551cd132785e29add9309d136a6883834349f2efa084138f259c7b500dc5f102203a39a5ef2dc305e947758bfa72d6d5ebaa3afa3e36cff9b17f3aaaeb0079cb58", + "id": "c646cc3909e6032cd9dbd8df7d5827abfecda82eadc0155d7f2667f33e37207c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5357843265955, + "fee": 0, + "recipientId": "AQJwwgFQ4F8NxNUqn44d8Kx6rB8AmJFiBN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210090279d7a8f908e661b3651cddb1f1690868b9906c0744724356747ec26e1d4db02200942dbfcdd9e72db4cf2ca3fb4d20b6b94133a781fa221ebe47d964e8c03f977", + "id": "aa45b2bfbff8221f7b5809e7ec6947f39e57754d1866a25d2b855b8b54080ae4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5399929874642, + "fee": 0, + "recipientId": "AeRzn9WbFJkeExpJ1ib9bcqPFGBz6fYha6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202c2146192848bd5fe02d0968ed6629dc03b5531b945d8e7fd344902c46074a5c0220677a6ade625198cfc73d9e60d9735841111dc5fce252a8ceea6ef5aeea325337", + "id": "d8396e0dc6eff0baaf523bdb9ec0a0f683ea01b2e7b481edab37f679959c72e7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5452743873172, + "fee": 0, + "recipientId": "AK3xPfpuLgDxvcUgvHE8G7nzgJ9TikonCM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dddf3ac76223dc2e41a5bf9c19b0e4e448e2f90c1e9970c97c4642c13892f68202203e5e2ad64cc817c835b951212e7a65b09e565f47707f030ae39b770694fd6f6f", + "id": "96fa04274bbbb82e73be6c4baff991523c86cb022cc8f4b0ca267e8f2b5fcd29", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5454583384732, + "fee": 0, + "recipientId": "APrqARoKAGvFvSd2ipxmhWt8LMNoJPxHDX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205017cfe8817f69a0383d53361483393f92c9275face9004b127e7d3ee4730ecd02201259d5100eb0ed196778b34bdef3e90a59b34dc0d61949d56890c8c1c994e400", + "id": "12514530353f15e198fd07bf42693c050c1bbf92f1094b9060ec2a940f1bae19", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5534027654283, + "fee": 0, + "recipientId": "AURkidZfCN8uin9FX8ByFwCpCpqAs37uyK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220752c29c7102b3d0967a2a369eb8ee0f8fb09bff978cc00502069626526a6b7150220351f29c9591b208740088abc640391b793dc7fdaa56a084b1a12bde9a3ca8ae6", + "id": "d39878fa44b4eacd96c1dde902d6ac74748525845fde03ae3bd74bcf99f0e14c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5596708324890, + "fee": 0, + "recipientId": "AW8dQKTqagJnXcRfDyYXiPBJgtY82sjSoG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100be71df263fcfb94fea34c0ade0751d3f7b3fd25406d995555f2692179b1100ff022000c3c93736cd0bb38fb59f5a4336dae6486c26f643c8cb8fdbff1b06d8f7bf5e", + "id": "5e016277b51da7b156b41d5790536d3f643297f005202b5e504d4ea588451d31", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5613302182608, + "fee": 0, + "recipientId": "AYoqmnDvAruDtVpKpmJY5XGHVsdtAsfYxu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220171cc8f28be49906b2b55123b0b5e1dba8a0a5be228aaf517c3b5f03d1202d2d0220559bab26591db1c5348b69ec6b801f607ec8ba8b84030759b6edb6827d3ac0cb", + "id": "9c1b2c56282b48749139ac851a3a991ef9e4ae3926e600cff88a4182cf9b3e19", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5623070875236, + "fee": 0, + "recipientId": "AGtJTLKoLnx2oVgTpfi9briAHR7SrT97PV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210080d87159f97654d34b9cd2ccb64b5c3b44824405bf464462184bff3cce93d49c02202e72887f5a73adfc0101cd5e4f0bd019ac66e52c029ed1790890389395c7b16e", + "id": "66ee1e6db3ff2989dea803fa06d2cd01981d1833a98b960c79295b63c653d3dc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5648505606296, + "fee": 0, + "recipientId": "AFsy8pLNJnnq1R36WeJmQNrRygxPZ4TBv7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210087518cb76baf699d909ef78187be4e5918f47d90bcc88b64612201fef8d672b30220668295a8f17296c8992a8dce97ccafb840b1a939660c7793f76c55ba4d5074f8", + "id": "1eda98af243f8de2c3cfe8cfd2b2de565e5e5b8af91998b716edea0da85607c0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5648906556452, + "fee": 0, + "recipientId": "ASjpXv62BcY7wLgJLkos781E8HhDjfPZ4c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206f1c839e8b95af5b74909a9febbabc652f35bb9cc6afed4dfb1294fa3a57cabb02200a3e9a2ff90d7d72808c5da906d873984a07576d593343a520279d5d81053904", + "id": "450f9e10debe655d9786b3e3a181b79380207e54a5460e21d27c64cdfe48bd6b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5667502986102, + "fee": 0, + "recipientId": "AMMB8GyfXAnSjbEQap9UV1KxfiQD4SFhnZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022041f56e22efaa29c67f715270a3ccf5f1fefd2b2c9a3a3473afdaac2731542df802200d42682d5a7f7d578f3d8065395662cca264afcf6c8941d558a45caf3528380d", + "id": "3eb6c19696d81659c7bbd979cd9d926a6473f86263b262aeb0ee10ea6c71a57c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5680013606036, + "fee": 0, + "recipientId": "AVgqPjPJ4RjFpw2bqmMkvHGHhTUnvrT7Sm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022030881e7cfb479dfdfceccfabb2acc03ffe03ad5e515c79fee770be69e107d75e022071b7098cb80f71d6a26cc8aa3b62df4d1dc56ab67cb86541c3a217ec9737554a", + "id": "9571f17bd17068f34b02419e07be1c2e1a2984ead5800a4b666b38ca8b235816", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5880602286541, + "fee": 0, + "recipientId": "AMjo1KKaWVXTKxgY8f7h95pBGi2wjX9kuW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022077e2def23f7441b5eebf718fcd350c6a5af8207e01f549932dc78c00eea12f2c02202697c2c7aa53ce55a12e88f4d28a9a92afa6d2e0b03da2c81de9cbeea5945c6b", + "id": "1f79270181b53adc2a6d320fdda137a3a2b0a5d76ebfa29623b7399f1bcd1cfc", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 5893686626629, + "fee": 0, + "recipientId": "AGWQjb8AZSSgin8H2HHHHcMNoA4iS1HweV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205097acaa5ada5c48df6a182a101fd1ad2005a521ed387bda6a3cf48a93951796022001c1d933f2d00539a616151571e013e10a22c25804ca301ddc65652164a2e58d", + "id": "394aa80cac2db93a5aca32bed46ef28feb1c5e49b0fc26cc220bcdb6a255c58a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6008981564334, + "fee": 0, + "recipientId": "AKX2t7kQ4asoStjoSHQKhGVBuDLfXVwtHk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f53f7fae41d09c34b7371af87d33ccfad7b3fd29df5644dea82ed1e45706d620220027cee4f90fa3a0e6037a4a321251a53940df0b8d44564343068187d19e423cd", + "id": "230f86676f4336a0a640ee78f3534633d1af95728a495c47bc0ab4ef1d826604", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6048199451708, + "fee": 0, + "recipientId": "AQQCMjGnEgsutWWRfL8BbUrsGe1z5HryH4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220211e94fec0b2a86f27a27009c1ca016d618d6415f2745e444ffe1d601963f2d1022023829c5346a27ab73f3f70cdde7200df4fdff5be708c6f965874fc200d529a18", + "id": "b26eba2e7164c76a09a874efe75237c6ea80369105872855350b1d3227d0249a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6062051615496, + "fee": 0, + "recipientId": "AdTSi59wjy3uZTv4tnenKL9fFWajaeYsCU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d5e6a1d7bdc9206e118eb2552e96893ed4af47543c1ac692798bc2085ae43fef022067cf6d3e916ef610dcdf6953c778f24306a989041bfff1ef3f1f599b90f11570", + "id": "6d99eb7d36ba0a942ae8e0c870b31312e6445392c2a05dbaa8734c56c249f1dd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6099257525671, + "fee": 0, + "recipientId": "ARPxAfyT51W26UdTgSbfVRdjUpzeiLg3Th", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3043021f4867133c5f9563bd0e953c12c2c7ed084e469b2933da10ba51eda964f3f74d0220527964fd63a3c23a6e140d7d2c142a0297d02786577d91f3a97c863ff1f43f00", + "id": "652889dd48a67aea94b758b01f454c7126398882189dc752bfb11e0a7d06602d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6114940797408, + "fee": 0, + "recipientId": "Ad2CVhECLL4ekRXmFVrYjGjALmyFmDBV82", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa235782a7afa225cc198c424876b1271be94a5cbd18b8b11a2d3a6e5b1ea561022006309b82f6241beea5cbc446d902ae570cb18c6847060e45d6287a2d0524302c", + "id": "2be98fdf7807c6d82dc2d8e7263194164b9f55822de204b1fc9258b735cb9003", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6177278671897, + "fee": 0, + "recipientId": "AUwfer8xFsv4Yk889R3n4oQF1nxAKts1tL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b8cbb6b32084b8ef479e3dfa560eb6a99fcd8391c26299ec9d7339e98bbf3e9b02201effcbfd175cf6d97ffc072c22d3966ed4b22ff771dfd78f7ae7b03039e00bdb", + "id": "d6c0d6f8ccb456ff951f74e3033b503433185415be2fef77b700b096a1dfc233", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6233789537763, + "fee": 0, + "recipientId": "AJ8vfPKvPPLJFdqVSQh7BBcZRF5GSTvBQJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022073c38a4d26f344b6ead47c4ac5f7e6d96a8946d423da13bb14b24c84c69e68c1022071d29be6bfe25b7b4e33b7b6d6757bb435f9ad9d00b1824b81cd5b16f1e4279a", + "id": "277669ed0959b0a322f658dc6cdc807ca4526ef9b7fb07f7b2c55436cffe85ea", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6249771580151, + "fee": 0, + "recipientId": "AaXTDvrMHd2nW34ENidytBxjtrALaB3uVF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206c0337c25c9f541d130700ac0475836bff0a69b427ad1a352fa18155212ceebf022022db04421beced42fd6fb6723769f5eb19443f4cee8146a11f2386fa882087b6", + "id": "5de7ff26c18b4b4ad9ccdf0e09a94fad3053d7f61a4d07beaec231083324a602", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6336348963748, + "fee": 0, + "recipientId": "AGeS6jmsQfmvS95KTbXLCFEKqpwUoEAc5b", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022002d5c9af048e1efd8bc37d2ad0e0aa64781bb3a738bacda828a9d2c3cb7affb302205a3594eaff82c4c46a6386089df34ce45aab71ef0ef1123e623cd64793c5f4d9", + "id": "c13088a3d622fd2925df99c39a4b25eac1632c68b3386f3351d9ede44b4bdf48", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6347464915470, + "fee": 0, + "recipientId": "ALvAAwn8MHupoymbgiJSCDYiqjQrWTEQ1H", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203d2c5ef313400dde6be26935d2bf2eaa8f7daf90acb6dda71af277fab69b55fd02207280ed0a4f4114db9dbab70b067343a36ca6361e0b00cb9064a39730e9162b76", + "id": "631288a43196ef9f2d5b214a93c261d1453a532d83f3f68a441905899c5e5165", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6608432032957, + "fee": 0, + "recipientId": "AcbUExwmdGDJgA1GWAjdEvx5VZv5VZT6ck", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e9c9364a47769112d10d48b398c5190e30c124d1c22b6d3bcd751976bd149bf70220297154a4646628b181af186fafc2f2b89c64b2d6b91b472f14353a324663ed63", + "id": "3def7e1baefa19fdfaa298305b4fed13eeda8ee4d8627304754edfa9b656e403", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6690095694899, + "fee": 0, + "recipientId": "Acj7HFPqsAauRBAmfHqAcPiscUCxdiS7b3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022034909f75febe76da183cd50b5f7e0745a50b6317d1dd1327eeddab9f42701a9a02202583150e149038dff6694c382b45949ec0d052d1edddfee4cc31da83d7c39311", + "id": "e912a67d2b2f1f5579c60cc12b09898804fd06914fb6b7032add9807c7666bd6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6816039047765, + "fee": 0, + "recipientId": "AHBdGME8KmcvG63JkaQ1FndwmPWyuBWTgZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ba60a9d3173dd42720e271a63126cf2b49f83f0846083bc0314d047be29853cc02206d2fe11664db806837fdf975675360c51cf5429a2a32ec88439f4969a40e970c", + "id": "4d1f9302f5b7ceb7682750384d414bf0a5f6b36820de505c8965720f46874df2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 6845037580681, + "fee": 0, + "recipientId": "AXFnbzewz6GyVVXuMhvAm7cyMLZRijpZXZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210084adf931c399b8330219ecb1e0091fa02e7e400e8f44abffa0011c3d5123a8c702207333080ef7f923e35c53f55fbda2cd3b686fd30606b0d6b92fffafc0990a51e2", + "id": "beaa5d25d2d5156209f7a6bcea44f483080f1a3790f72e2e7405a1b7adaf7d83", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7198886304127, + "fee": 0, + "recipientId": "AYidBk2SLnvrtgCu9ZYX8thNPweQXKoBgt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bbc9400997cc61072e38bcf2a3b4a600275332a5667fcf9ba463935309b6a98f02202fa932ee91e6f940b9d943255e1ec649517302c5a93b54fdf49847ee5c0abcd3", + "id": "3e725df96a6f88901607d832bad39c83e18f7b52e689a3590869c85cc4dff5db", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7274098215498, + "fee": 0, + "recipientId": "AQiRjkc6LSFBc5JgQHfEtRYTBKzuibCm9d", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a0d5eff67117daa369f3b77839d2053ab16c5b659dac2f3457a75877cf346ad402206ef582d8394a23e3f535385d02ec135d09bf6aba3c1d812228d59052f3cac48e", + "id": "f13e17151b9761aa406da0d987c8790eb0556d93eaeaab95d5d2b91458a951ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350458828062, + "fee": 0, + "recipientId": "ATWCEtjbAVLDMNCyHmdRMeoiqcHPahb3A5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206fd243d047585330ab1711f38f4a5d8de5d283df73af280628aec73c56bc13f3022035c0af74cb2df381b3e3ea2056d77859d085da0105c2e85edc6d607a6ed6df57", + "id": "9492b0c5b908ff768932a67731efab51d12491d654ddf6144307949596c4f581", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350605843105, + "fee": 0, + "recipientId": "AG61Zuk9tUxhu53EfvBBNzh6aLiHshmbgk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220289eb9006920d7e06fdf963d3a6b1252528cbc897bec005c20773c2a6fcc1d5d022010e275229dd9e83053e9cf8e931e8d6bf28c8b5a7a40275f6da2d08e68f12445", + "id": "a2e69e4e5391966a019444eb1920d8e6c8793cee7c6778848f14eb8e2a711d7b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350752858177, + "fee": 0, + "recipientId": "AJ89PsZmopZgpAEJYiFpN4Nsf37AmqGFEJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b1d30a5b801388ff04e1afbb0a1a08cef63e0ea4f256826b49438559914965e002204c6311d26e092f282985591802be6819a159e368f86f8bc72b39ded94b3bb93c", + "id": "6372eabeb335296df1f7dd2d62aaa635bcfe1259195ae57967a47b1121d16578", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350752858177, + "fee": 0, + "recipientId": "AcxafSyp9wY2zZkqDSfX7uM1ZPsVsBw2mT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100af2d76096e5de9e708de3bd6d68f3144bebc43dfbf09272a9f402870c2581e49022017a11a3726d0930d47a965393455304dee143d0eeb992fba84880474de00a211", + "id": "05418f5b2eaf40ebb39646280a9f1ba7850e96727bfaf4bc4379637ab169e7f6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350752858177, + "fee": 0, + "recipientId": "AZXTaxgDD7YqDPJLTkWwKgB6XPZT9L9Ata", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a54c0c0c82ee1943d9fdd66b10a0a6f70f8697bf5c2e5a70b108ce26669a639b0220463b72006b1f45a7403ac406181fbdad7ad4173d4109f20c25c890551851994a", + "id": "d311075ad854a0aabcea25b63cdcdf8f4b81ea41aeecb05e09f64c5e9d9056c3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350752858177, + "fee": 0, + "recipientId": "AeX9ZCUY3TgYBmxhT1eA28buQymhzcRkdG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202bc46f5c4714ddec901a698d0ecb09e4eaedeeb654ca202f55f3af3256774a97022034c060851358a005d60fdc2c88a4706e0cd4252d1ed84f64c42227bea5fa0a5a", + "id": "f5338178edbbfef4715e79399f932057179aa09bca7ec7fbae3c170bffa5bded", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350899873234, + "fee": 0, + "recipientId": "ARnH7KwF2dFqZBaVtg89YRNpiFoN9E5s4G", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009a0f64be9d209adf953456872e1bb098c29174f219b4b620287a624663890ed802202894dd9517455c765ef6868b12239246a2b4a862cd9e5c5e53cb7e24b452f5dd", + "id": "833780d0f9ddbdd029d0a4c3fc15248044540cafabd9ea99f39ef268de3e559b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7350899873234, + "fee": 0, + "recipientId": "AboFjwBTGZYhpHeBSRyX1gRifpmGaba1Ne", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009f02dfcee31f851c53ee9ba7747732b8395dc294e22ce326bba9087ce845939b022067c32661f0ea2f62133dd9367ce19762e8ba976ae5d9fdbdd0b5747880527ef5", + "id": "da390944985838649b7fb34067c7335926531602ab7fd04993f3d3dca8cfa0aa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7352075993676, + "fee": 0, + "recipientId": "AHJUzj6eMQ9331gQSJ8VMmxwyTxXgojdUy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202efe05396510be9de3a85403629b8de370c7e239b9aa7ba8274a3dfe813b7639022019fb6626e456e050efa3b49ff2bbe5d53ff9c7b0f857bb46a60c4574ae9706e3", + "id": "6baf648e78dd4f865452d1b2a10008e9ba53067f5f7b33689715e1012e11b920", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7467974903126, + "fee": 0, + "recipientId": "ARBHK6526c75WtrSA9twWuFbcvxSQXh1ZB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100820cd452944f0b765c309ecf5888f897a012290fdfa80ada1d79edc8048b068002200a7b4d0de4d6da32ca4e02527c9630f08e2efcb2b5d9a2ad9fb2ac59f9236058", + "id": "b615051a5fe01170d7972e4cc06eb635e0ac7d8435f7122873308a029d64b029", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7596490265803, + "fee": 0, + "recipientId": "ATDz1si1NgxYVhwco5FU8rziXikgzMswrL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203ffe6e56154cd9fec39d3b5ca60ea6b371eb95dac9857a4365b807b329156fdc0220733a0c695b65af96e3f2b8b92661c6a91e1667867580abf25f146ca2362e2545", + "id": "7c77338e20a9478450a35a4ce4a503ae93c1268850c4582deeed42b2ea202600", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7728600535371, + "fee": 0, + "recipientId": "AUzNXqYNtmcbPUesxouPuLnAqcvxeBv62r", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207dfaa07820dff0518a6781b5637985188caad8c308a6716b0eb5b8e49a399d1a02202a44037fcd6142cd558e01469a283cb7abaa6b63f383eed37425a830a38952b9", + "id": "98b95f131466f769280d7aefbe5cf7fdb7aeefab4dda10d561fd22cefb329e1b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 7863973424054, + "fee": 0, + "recipientId": "AcGpDjv1Ja8gybNf1Kg1y5wfjRvB65nLJ1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022001916ce368bcfc70ab18b04a0972d24a4a798c1bfe1ac82af88af3e57f05109d02207d702327da226d245e3e73db80fda6ab378037e21cd96129b5eda4bdc44f0dd2", + "id": "8da4b1228b425051845def8bf958afddd81be64966832d56ae85f30c09b55298", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8061487401793, + "fee": 0, + "recipientId": "AYpKBgSEGotwZF3BvgpXWxGKXsawYQEr7U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b566d3021fa2bd2a9bac9320149fdea4f0aff2e710affebf98a8e4e132270f27022036fcfa529097201dc793d55940b20861d2a359bb14d82e8fd0a1ae17359577ff", + "id": "7f88e5d7f79c63317e0c732c03b451b1ff2714bd5d87900faf43b979e551970b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8091708746281, + "fee": 0, + "recipientId": "AMgi4FUnRNsseqVpBVQLcHtk2PxkRFkv4M", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202750a5d5fdb30175ba7a0e8a417026664dfde32cff5df156e5d73e25b26b5f0d02202a6ecd37f045c321be0526ce5b854dfebd8a7b29e646fed77e98a126458a62e5", + "id": "834bb22d473469b193464fc80c52058042e14d82d6e3ed85b463cef6da9e68f4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8150156945826, + "fee": 0, + "recipientId": "ATuYepEksWdtwk3PTdYEdjytkTKv1L3R19", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a2f40bf9457f828ef8c32f1bcbde320417f0f70eae75ee0492509a44986b97be02206a1fa49f2b1079da0af70eab644a2e161c69774bdcaf10d6b924e3b4b0730a9c", + "id": "2e66f27a9f97ace05f8168b01858118e23cdb033ee68c84ab0c5cad7d9154eb1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8360754246642, + "fee": 0, + "recipientId": "AbA6wrkyQS5p5iDZF25BXcvuUgfZm3Hy7A", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201477fff0e08a35db8bea53057aa0b8cf323ba63d32e3ad28bed7da6d79007ed102200a5bc18c359443f02793e2673e615dca45013fba0dcf11d9719d80478e8d6e93", + "id": "c087ec57f646b73353792010f5dab487f12111ba61950c3cb4dcf26c51daefe8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8820462384640, + "fee": 0, + "recipientId": "AZf55Hbr3KnRwHbZn6zoEov7TSkJGhDHWU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b964e775cf55c8c3c174c1a06ab32919bc940dc74121b06f5f985145c06781550220452aed6f7d901c9ea5ccdc9a3c9538fd5f2fffdc8b4abd1778627f54674319d5", + "id": "206c25241d5f0bf068e1d532c0005957992176629f09a897dd39dada84b7d500", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8820903429812, + "fee": 0, + "recipientId": "AankYCzRAR2mK7z2fRXmXxzUwJMknRPkDq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022023e35b79c2cd4429d44e9a152371760a704819b464132b8464313c139e8be78b02202b5fcd45b45515a5fb41a997d93f55c089e65090a1ffbf31f07147445181d5a9", + "id": "62b7ad211b62f4c87e4dcd71d72cc6d9aadc8a4077379d96377e3a8fdf468b78", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8909427869508, + "fee": 0, + "recipientId": "AP47UoDcZ3XMq3P7VBLpJJnHGjGaD7yGyu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f5619dd275b2e1857001e6a803b2cc5978b562f947e569bb61c66d57a381075702202087f6b32d421f28c766fde6853ce0aeffb672903a9c065f3520d4111653d5f3", + "id": "8a8001b5fd69483fb8abc89b2c13626c30adcdb94c04a25dcdc1d81a5bc3214e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8947739735763, + "fee": 0, + "recipientId": "AeBVmaLa8zk43EApXSKPcTudHKRqDBYFWd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100846c22b8c3b999a50686b0df62755762c8f3d6e856d30ef3e467ea55f0755b9402202611440aa0e7b6c220399b990995b991324b3cf2fe8eee8d3724736edc00be2b", + "id": "e434a3e0a1ca8c488797a9a7e10361b4f0889c8528263f2d5da8de0ce2f923b8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 8987732597729, + "fee": 0, + "recipientId": "AYtFa1PZcaXinZSwvsjoym27aAAKwEAnaX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100871e75cefa2f4aa5aa3e2602c3a918a98589889bf157f72e7f550085423d3300022058f068d7af9ee807c49abae45edda26876e1205504229609397e42bf26d8b4a4", + "id": "89f718ec38b7ac3e6ed5c353e8d6c6b7c77558357fb317e4ad45f00a593b4d2b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9019606582562, + "fee": 0, + "recipientId": "AKeLaHxdcSBzMyM5bJUvThFcTHsVxmmatz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210092e17b11d4c617baefc3df8fbb432d34ff142e1eba516f363507e6f7d5dd655902207278a3fc300dabd87c6103e680d0b4f9a9a9daa9a7ddedb372d8a5039a5b8118", + "id": "b1a04d1ff1bb94dc1b0b1902a803f95f29b4cb75577899ec9ec8f0b3d042fa17", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9073460596513, + "fee": 0, + "recipientId": "ATNoXbqJPxJLUxNrPoGDYXo4TL8dsetmgz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008120ddb52945a8a06d38ed29d2444d99ffe8e2ee7a6626ab6444b3484cfd94f102202f304a833c539fe335c91ec20005231362b5872d37f2f716ef8705857dd44bc0", + "id": "aa0f4fabc4952d57a65943c6d9a4c6ffdeb77ddb984aa4c6e73918028e697816", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9106661500215, + "fee": 0, + "recipientId": "Aa3rBGChkK7iz2NVnkUx7pkjwgtgaZRpVo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e61fe14e4167951da3bb22c81a12a387602ca09796354cb5d2dfa34b453e31190220493edba4cce40195451c2ef5c260726dd80a81f9e53a2c0bf8714c9b2cd33b14", + "id": "9db14bae790c26d0073c5cb21b54d8a860d79573f6a24004d5938db55917d20b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9116425808613, + "fee": 0, + "recipientId": "AbSakR7RXYAEWqBh4btWuVXCG29HTdxCgw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009d069471d4566b87598efb5d7f4550a511b7ce0d303f8eecae2532ec3cf1664b022072ffdbbde754ca2d1b57600ad8f0bf8db9d0c628a4f761479ea547af329091b6", + "id": "a3ade2de7783875db21cdb97071f1cadba19e2a5258fedea37396443f7dc88b3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9176679868148, + "fee": 0, + "recipientId": "AKWjaZp5KyKzdCnJq9wQiF8umWCJL33E6B", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f6d4983def05d2a75eda8867eb47c6ec6cef8c4d13d43c85302161685ec7a324022055c69ebb708ede45540d87ac55fb4b240f9a1160a5ba8dd1aa7d49a4587e6572", + "id": "4fae51a3f14bd18dd23869bfaa3fae58c31142644e18d4422ff46f922f68ff1e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9212629406283, + "fee": 0, + "recipientId": "Acj1Rg3SaJrZbiPsTRN7mfzKwHxh9ZLciN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204dbd9c143b3e6d1335ccee6965087fe0d875d4d8d9c9d3574e018317508e248302207cc154f6ed0baee54eb44551e9a3d67b89ae5c539568c208b6e6243e881909f8", + "id": "5aa7d5de2c38dcc27f69d679349af0ad153ee494ecaee656a757fbf3cc1bbdf0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9261801586245, + "fee": 0, + "recipientId": "AJN6KnBY8VxHfiE7YDyAwneXCwDVtdh57q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ce0a2d090fd6bfd170616d273b648bd862a37d30e5dc365af8e70dc702f0763502205b95aeadb2b0e3e02a68638e5e0ddfdf74ada07ee5630e00b22e58392144f3e6", + "id": "006c9df41217c960f9888779a7fe79f5f7e30dfc06d450379c54aafe01820606", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9431021629737, + "fee": 0, + "recipientId": "AHpsBCfa32ufzETjsSJj3c3hJNGhjAyq6f", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f26cdb6fece9de8ef933318bb3af05a349e888ee8ab47310aa829d96f2b044d602206f97eab55c51cb15bc25e66da6832db6a4b436eb538aa727a5264fff4ccbfe37", + "id": "f543ab6ff8794636fe7aaa81367bbc5b3c47bdb7f9080df77d3fd09067941b56", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9456800520174, + "fee": 0, + "recipientId": "AXt8HyD1jBZsYE1c6x2HdD8HoVJsjHuqjL", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dbb11f95e9145884db5207db7a7a45b4eb04bb4f0c833c2949b2019194f7c7b602202d8c32008367ff0d486a7c584eb23a9feaf533188ad2e38d2a7bcdb912561ac1", + "id": "ddd35a61d78d3ef066178e240dee956ba70f73b0ff41aa59fba2543c01ceee60", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9529256927883, + "fee": 0, + "recipientId": "AXmh1DXiL4g4JjqJp2RgQdHnycZspLeztT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022039c3d55cd46134f05af871e13604c76aff0e92381980fe2d4e5e61f63efa891a02207b707a5e64a03e5ec1b6cce3372da6da6ca0ebb2e2bab0cda4ee104aac92d0ab", + "id": "ac0915e202a613fe3d133a3b8e0a4b86737e0d06bcca02202d583e5e3ddbbb3f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9547731294407, + "fee": 0, + "recipientId": "ASjGMDGXLfB9fViy9qPqYGEjgv1jeuwWox", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205e2ad7e24d33353687c030186542c4ecbe2602a35cea56d4e7b241454e785e51022028d425089617605caac7b1827be5cdf18abe4c8235c2a622669c97af32f965ac", + "id": "d31f907d222323d8e5d6034811433ab67dd7dd334e0a5c6bbe24cdd3accdb501", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9657132907103, + "fee": 0, + "recipientId": "AGFfszsJjBPwUf5ko5gQJaX76xFVoKbh2E", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cece660ea46f4303e6b76cacb62da21ae4c8a6d11481ffbdf1b071e49d498f4a022034ed3118a40013cfc73123a39ced55c65bdcb01dd8ebccdac9ca689faf8a8514", + "id": "2cf431f04c0124d8149a4493ce3b317257512684868568884658512f5bf02d27", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9720973358155, + "fee": 0, + "recipientId": "ALxR1nKJXgDfNtEuek1jaiNJG7gNtfZ6qb", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210086691e951494d4808f416e82c1d346653b04f39272cbcafa934b3e643447dbcb0220292d0fd8915227ba07bdc17bb7a7e946f6a50c4c063950fccd47a73433f5b42b", + "id": "f532635980304bfc7afc3ee2873890f529431ecc3217f8bd580f47f1369d0541", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9861574288956, + "fee": 0, + "recipientId": "AYAYsDGPzVarAcqXrpUE8eYcftN9J2UNtK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d7531a783114009e2775b63dfdf0123ba0bbadd193e284a9076b4e02ad1063dd022041f3c3c3a9dd40d17483c4c01d2615eb89762611a5924718a517aaa0ce09a829", + "id": "cb3f0522a4824486a1277d54d509ea9956aeec80cc2fa77dfdbccdfc7a888a62", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 9967620875688, + "fee": 0, + "recipientId": "AaxB3RG8EhWNB1axLbDrxza3v5E9kphnwn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c9f22c83fd1327fab869e27902fdce90ed952126e7c43c7e9afae0cd9007591a02202141f6f04a4a4b519fcecbee3afa90cac41a090c31393db048946d780735d33d", + "id": "a47732e4feab4d3817daa01358f6f295d9e4f07ab1e383f08b09ba0cb755af05", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10271698022691, + "fee": 0, + "recipientId": "AYDYbKKZ22ym3ZpXcyzPWc7bnvZXRZdiem", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205b259b711e8a7cbef4e8b103cdd7af07e6aed38f65d0f91ce15f906dbf6dff7102202a0e181a4c64494bb7a679e0d86b397c644e3139e0ecd8c90e38fb7dbc798811", + "id": "a46b6b26e9c55a9a265845d4109e3e03ea963491b5efa4756e49a6aaea94d91a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10298404754306, + "fee": 0, + "recipientId": "AcdSagvBLHcUuh1f3GbqhwxDZ9kuNgt9Lw", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201deeb7ffb887271b4ee12f3feb9b5c5ef4ba4c60cb4a1bc6555474cc96e420830220542663416f5fc69b7797d6a4d231041a6d4467d77d1598e19cfc7a64d89632df", + "id": "f9006c06167e9095b7398eeb11f8195d715b04be4689eebe59a5864def4798f0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10305487746344, + "fee": 0, + "recipientId": "AGVBHkaRr9XWBLZQJDdV1Q9RXcoB67PBeG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205601083db78190cda32bbc1af11fffcd7f933052794e7d4c95b6292db63c3f2102202bdaf8d58e7cbeab12ce6fa0d34cc70cb02f381273d533db553e15f9b2cd2d08", + "id": "66092335843fe18ab2c4a8d137fe0e341057060964ab4169383b461df9f7f385", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10318455720429, + "fee": 0, + "recipientId": "AdbuHGsffes9YYyKBp9DzKrcQofg7zuSKQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200a8c0a7ace259d0393f8c3662af8f8e27a0f261e69fcbe00fe99a095193965ad022034badec2675471d2d846e28aa0f4cc48ec9d1b8f6879f2dfd769300496e0dc78", + "id": "457678f9e0c30fd116dda8f82fa4e479b23a0139d36872bc4b992fcda5fa095e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10360354001447, + "fee": 0, + "recipientId": "AXkBtARJ9ibkidfLMhzVSRCAuqWBxRaDhj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204131d85f9493b052192d281fef2a87ecebead69c4e4b72b306c967a2bffd013602201786ecd3284b341843cf434ae92600de4bd044840ea306ef36372da4f8fe7e13", + "id": "eb6590373de4b7155bd45679378a312473be8ceee6c9c4fb86f2ebaea4c1be1d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10445981844431, + "fee": 0, + "recipientId": "Abu2eNnxLNUKimhqKJg6x2aAcsUJStuCAG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eccb27c0cfc2b60f4ebaf647654132c4e7fab649368a77241c55f72dc8ca3025022016463ae4170d699d3cdd336c992057ac3d5c2ace3be998adb189840afd328d20", + "id": "b4b03eee17ce3bfdefba3d60783e5fefbfa99f5e2a3ae36d3a77f42dbba37837", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10446382135839, + "fee": 0, + "recipientId": "AXBkJGbRzM2rUD6orebcoP3peg2xGbgT7j", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022008bd51d33b28766782018df754e853f8d92672e3ff570d1261fb1cb2f30fc9640220022208c3f0da9cb972e2c1888f1fb720c1e977b7376caff753f23fcad801a5cf", + "id": "818bcbbb73b9470f0e4d0e9606f937ae2281dfec46f55251ac7b77d8ae545bd9", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10585084115774, + "fee": 0, + "recipientId": "AJXuBCyg1jivEDuJqrJwu9R19C6uNG3rHM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100887bab7c2e2800b93cc9df58e418ecec30395fb35db6c3598d541295cf4104580220128140bf3e75736d5710c9df703b7f3a9818a6a1caeae847c0ab154645bd68c4", + "id": "95ea9f02ecbd2722503b7ad6754a29b3c743e2e114fdd403fc96b16097a3ff31", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 10792241696323, + "fee": 0, + "recipientId": "ASSd4LMA4BLtskUS3XvPbQYLe84qCJBF5V", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202885acb08d8088274b1a8ab4a28c796f5489b82412344c5ddec9f21389bbb21b0220525fa2842e1e3b91b98a5f8326cdc89d880e543da82be9adff19d923a87b43c6", + "id": "872e270c3eb5dc0d487a040745293ee5373e2622ddc9d34cf3e4ad793f2c6a25", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 11033661805931, + "fee": 0, + "recipientId": "ARo1r6758wTHS1QR8LnhJiyLcudybWPhb7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bfa945a5eb00deeb1f4774b9a2d46a44832d38877976f385be3fa88628c00ab5022024546c571207800790fab06037d3292a46d31e9c947acf318a27f67db8e42ead", + "id": "1e03b495436ab9cad5aabd942c84cb315f7c7796a597a56a6aaf9680f1124173", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12262151610133, + "fee": 0, + "recipientId": "AWFxQB7mKARk8W63fNRpp6kZjVsapdFsDa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a31f640dc8ff053899dbcad3ba8b4d67b1046db0c9cfd2dbe8c15a1b7fb7d920022003b089de0b92bdb55701c76ab22306636acd88e933f75cf6d077bd4cc7844c5b", + "id": "e550f7a823f274dfb83ffdd148c2d18b573abad654583438af7a702ac6de40f1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12274908823760, + "fee": 0, + "recipientId": "APcaeU4K9uQXSRLRGEgDM1G8gtiBG5tCYZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210098a25f2a406512474e48e14d7d009cc8e7349e7b4a51f2ebcd63f4996c2507a502203cda6a22c4fec125995d1163ff1ff725ffe060c6b720f91ae76c20a7217b2e6a", + "id": "736b8f9771bbec8794353162441c1dc30720dab02dda5f17721f3577b0f7b454", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12417330032449, + "fee": 0, + "recipientId": "AHLhrDJwqQmHnZnhEPBJqfEHYHegt9Y5rd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100edd8d1aa1c15c0249e1cd88ccd81edae73250b10df8c8a32549632404b5f0c560220441b966469b37869d5415cc7d90205be883870c3895db4c8b5ce2b4a7d0dba17", + "id": "9340ea9bdaa5155914bf58d74b856d532f1710efe6c569ecee144d1c6ec51ceb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12465317334603, + "fee": 0, + "recipientId": "ASECaxsSkxVDWry9D5fcygAY5ZuutNBvPG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201ecaf13c7ce1da00ef9f8e28e95be0d15d26951dff1f742399e5298031337c8c02202d756de469c1f1ea127c4d785d723ca5c7f6991bdb8b8e3a9e7be2498f5c9377", + "id": "6902bbe0e9cb1b9cf2faebf4f96d27c85f4b1aacf9ef7b4dd52aa7d882b4470c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 12637331924100, + "fee": 0, + "recipientId": "AJ8YEJ79noVf2n9dm7WktKLy2yJPqmUWiE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b4236f3e9b43c0c3f503906f146a6c736e8572264c9da1507de8cca20d7df08902203004c4ded33dd32eec7b97384b8edaec3cf17c8b0f813084703c289e17572cfc", + "id": "695597762e9a98153891ef3ce163835b600d7d6c8d622b15b53f70b1da3ec08e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13156450295530, + "fee": 0, + "recipientId": "AMnJCV7YZg3aQ9XFH2Wm48wX5Q8KP8yf3s", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220798836f686318188a82f5a0b37e419bd52a94582e4bea79a981bfef94485a06002205539539bda455d85f826a824772f5a068359d2c0af5f8fa94876fa7b6a83f14d", + "id": "dfe1528b1eda46e77923bee940a97c678ac3ccf209e146cee8b4d9e590d652f2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13231355144718, + "fee": 0, + "recipientId": "AYhj1z71Ajs7gQPb7TQoK1Th2HWne8Dicz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204e4e7921958f482891c6edf82e87d577bc1525ec26a1606239dc3bde80c7e20e022036dfd51919362e104d7cf98a1d2fcad1517261761e780569d60503dd87c85b9c", + "id": "d531efdb301103524d38dd7720408e6692c77d4b2581c46b44e98352952e2990", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13433119945670, + "fee": 0, + "recipientId": "AJuwxFoWbhWGiSghWDuNLh7B2u6orxYKEr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e2e10a21a73b7fb49a51c9ce6981401bb7041ee97b929781e9a33c55ba85f8fd022045871513d5cd53061f7b21ba1638ea8502f17cec0eceeb1ef66715712d3a2d92", + "id": "c37d982d2277da42cb03590feed99bf612911df0df4d0819c410d906e28237f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 13711299443014, + "fee": 0, + "recipientId": "AcFJgpMAH1SCrCRcsgWfXgbQvDiBh7suEd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206fb5cbaef0ff5662a750ac6a89b6090cb5e54934f86e1852f75b65801397fbb4022052e7eab49c5daabe9b69fe1ced745c2cb722c3c221864f9336584082bdc750dd", + "id": "96f9f1ef9345ec0a80a1d49ac3a366ce130174c328432db08758a69f694ef2e0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14115873577166, + "fee": 0, + "recipientId": "AZLm98LrEyNEmbfe2AHV1q5V3FZza6x6fB", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202d808872dea296887d825d2939947ac1ef11f72f86dd8d0c6725f280d611ac9002205b08d41e9438076a7689cc07f0d74f3af73158568cda9b0469cdeb4d631c1373", + "id": "60df15ce23bbf98fcfef08bd24668c1951af818540910ec3ad9dcbc5d2600995", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14159732932194, + "fee": 0, + "recipientId": "AWiJP7L3ZvRPzZyc6mmM9qqNcaYbL9ABxx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204c66842803665c4bcf29c2043e1d9714e3724a1e1dcd935e1ccefe6228704675022010f26ee2519ced03bd61a8095e0830cab83d159c56928d7f11017c67b8c6247c", + "id": "9d5279ed2f9c0e85a47378bdfd168d29b83e2ee7d6ce8813c3a7b4a68fe50d85", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14302397057832, + "fee": 0, + "recipientId": "AcRE9hpuKFJtyqCSGDS1QcAFTYCU17t1F1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022019ce5205866f88b78846a07a0862122d34113cd250df8abd884c7202603af10c02206d05348f0438e537108ae858798cc390f104c69047eb00aa47455bd89fff0b22", + "id": "5dced5fe4b20327ae767ec3902dfe102e6764a86f20fcb9fc75c6356cbeb7385", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14627998187772, + "fee": 0, + "recipientId": "AeDZmqf4hg8HYMvpxJujrVWqCyVUyqZ3Zj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022050e3a4396de2c6bbbc7cd1120e46a01863b9d961bc19ed42f98a18750e61a8bc02205ba8fd0e08d26df41708ae78280ba08fffe1dbdac48092b464da8b77649a716e", + "id": "816320ee8653dbc97e201b24ab45af6d3c4d182ca9d59b96c60f218803bbe76c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14701505716353, + "fee": 0, + "recipientId": "AdTzqZEgkihPhhqn7isgQNM7UmJCFjSRaJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205fceba8d1f594a4c189f6939db2567a740598184cbb2787a82560c7d33939191022054ec9894c1f430554cadace94c584a6c6ea5f907f06f5636de53d01c9937667a", + "id": "bbf0def2ca931ae79bb421b00044e5873840c3b761a24cd6db19161a069d8575", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14702758612028, + "fee": 0, + "recipientId": "AWSCxEf8ZFBNNMturNTbyrwaHqJHTAz5hF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220415ce13dfa1e2a0f62c4a1b452747c0818aeb16cf852e7c649a7e061b522295f0220738dd8e58a0bd7b0cb3035295d38aa9c164cf1a023fb69c2512f964ff840c0b1", + "id": "39aa37fb064becdf8f37a9bb1e55d3a1e8a96beb6be1fc1779967690a74dd7b2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14702828851868, + "fee": 0, + "recipientId": "ALcRivRn5MpE7gjYPfhvdjkT7EidnM6AsT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022041e7c81b713fe8ebc58fe4d9db338b89b3663afdd526ecd31dcb0b6e9a8f8b1d02201eafac17f41fdc7dfa29c748261f9a0e79bcfac5e418a1c144b0d820dcf54ab5", + "id": "19270d05eff4baf154541b1e9acea7f70990c9fad504e043c281d384a856314e", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14708415424040, + "fee": 0, + "recipientId": "AbVMJrPSe2z7UCqxfebGwvVnCPePzYqqPX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220429ea8d0eb83bc86a8ecbde1777798182f219eae5aa56de3cae7bbdb4d04e054022051e6769e588884e730ca65b1886452de1d2f7050a9bdf758675f78c0606db368", + "id": "a116b90dd6924989225c4ccee132706146ccc6a508c8c0f2055349c71d01a375", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14818222131272, + "fee": 0, + "recipientId": "AbmqyXrS3NaqZL1JanAV7pi33cxKHewwQP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b1d6a14c5c53e2c96d5080df55c593d397771c9d4cdd9c87a35dcb886102e7c202205e2ead847be17b2d5c71b93b7b50acffae974232c1164971145e6bdbf912a85e", + "id": "5b864466691fa06bd64c97204b10e4cc4de6acbc4f9f02d9183e96f53ae9e6e6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14822705716353, + "fee": 0, + "recipientId": "AMwimBgD3PLbthyD2pw6x5kr7XdAG81u4Z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100cc44646a784672d659731b5c7aa58da1d4f010845bef874c5dcabd149d2c871702204e067aa1fdfcd05315ff030f488bff4160bc6d4dff149e6d0f248cfe0606e5c1", + "id": "ae29982e256bf9213db1e8944b73f2c98942fc31a4b107c2d5201f67595102f4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 14895015891637, + "fee": 0, + "recipientId": "AYJiw1a15awqfRNQWXXuDmxpQ3DNjWitzM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402205f487f6903a7767049c69f2d80637c45ddf757d5cc2b879df4b12e421223e0c702201ff2ad7ab479099433d5cb74b17dd647a0fb0ea38fe72950601d4510910b730b", + "id": "4b00501c8bfb24939da2e8402e40ef56c58a5c61ab6b4eedfc528b1b28f35157", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15082558701296, + "fee": 0, + "recipientId": "AQbJg9r4c5TxoPPMcywzVfLHavxbWCQs5f", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220502e111dd7864a3bf81e5dfa24abe4f74db23747b2ae7017cdae6cccfb72567e02200737316bb0d540184321f56e7e1099d857c56fb50d9156f2fa4356c56b369d6e", + "id": "430dc9734272db7f88762940c12c32b5063b5ce5042e8846a48937d2636e11ee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15241050976143, + "fee": 0, + "recipientId": "AdYqsVoQVTUto6ALnocgCvnssTbDWnPAbP", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204012639c893ef39e7d67a0ba9fb133a37120007ae3b9d8ab7544cb2bcdda32640220232cc268d227799a8ea63d58697cfe0a2cc9de3123ff825ee7551a894d0b6d3e", + "id": "bab6b296e910810366f4ccba38ef01545ee34cd4386c0c1f806369c8d71579db", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15458231619516, + "fee": 0, + "recipientId": "AcXhb4DBzzmBG2USvxuJY2uEuFnMr7Qfhm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a2ce8edebcb297f1948498412a60c8b736320ce1ad347043222bf034b21fa69402205e098d716d247f85e9c454b7b53b43489d55a91be9f19d792ef92b303b48e9e9", + "id": "d4a58d3b71902d221cfe3cf915429551c77a0c4d3c73c92a2473c3c151c1f9af", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15525088530753, + "fee": 0, + "recipientId": "AXDwt4SoPBiSvfjSKgZdV4vMTaNCgaFHMK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100eef9c401c97b593bd75fb52828a9df9bbeb82508006ff0d683309a7c1119e6f10220040995de4c4246197bc51ac7abc5094d9ac8ed8b1a8562ba85634bf430d9d866", + "id": "f3e07c51b39f93978aa8b6e4f133400e33ab808f17b3a47825afa75a58bb76a5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15609782909903, + "fee": 0, + "recipientId": "AaT8V4r48jE2MtWySFmdk2gqz3oz5RkXvs", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b46dbd720df1b60f59d8221ba0f7af053d7447acfe378b0763edc899300e035602206723c53b8ec4b41f1c374efe7620b94e62f0894fc90174404fe2d36c190ec396", + "id": "c37f2b2b221265b74534bb17e8791ae000793bea52102905f35c89a5fea5bbf3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 15840600000000, + "fee": 0, + "recipientId": "APpQDTuoqkWQex8ZjLCHxNf7xpYuEdWpJ6", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b6d5bcdb2466f138713b14a398ad38c470affb52f714693f836574106ea1cc3002200bbe4f8923a286f2e677e660c7316a815b444db2b6f46849d73402466b12cd98", + "id": "4c20e8ee483a76470df958bf62ddd99ee14e9a2536be56599145d28afe1700bf", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 16008869346988, + "fee": 0, + "recipientId": "AeRmfbh2GUoEitySmsQRACFRkPQFh6XxCn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210082cbed89e51bde2a686b62c96c564914f843ca0771b18ee0f57a1266eb181d2402200d87f5c50a8be268afe0210c67a394dc63cfc5bf026db1b58c8950ea185dfd57", + "id": "9ca43cdb29bb7b775d487bc7392be4f6da70b6065cad1ccf40483b0c53e0b23b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 16171656287989, + "fee": 0, + "recipientId": "Aa3E6iHPVW89sPXamzE5CUfAYhEp9arAw4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c1f8856ceeae2243ebb9a171b36ab449c637536365e033e244a6ec47688922530220565bd9cea70b34e2f7a7fe5ed5b6b4fe2eec554def7c32b400df035c5dd9997f", + "id": "e370cd7fae15dfee56e44b6b602c0bea7819758a9a61fe0cfb5387cffbf5c747", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 16171656287989, + "fee": 0, + "recipientId": "AYHTbw2P8oKpk9JyPFXKuCw6auvVtr1Upn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009985662bfaf857f58295def71d71bf62109d4e7454eb087bf0af4509896161e102207bce078f3fba1bddb717c318f6eced379c983b9830d4295ae2f6f90ba2fdfe60", + "id": "7874cc7ba980cda4c93a50b415074b3406415f7a84a10efb43db211eb3bbe794", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 16191556704498, + "fee": 0, + "recipientId": "ASUXHuCsmbvLNURKg8bG33iPKid3dvyX7d", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022061886ff0eba8d281f18efe0bcdd9ee543c91a19fa82e539aab7a1bd83cb5839002204f05a0c30ab8083a8fbd9d1e48f376b52aee5c23470c0f9f270e04e88fbf854b", + "id": "ee246b6d6c7fb8aad48fd8dc16e11b86a796a21d24068a87f9822f6fb6db857c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 16746157299652, + "fee": 0, + "recipientId": "ATybACcBV3KhQEuhhJC2CbEUeAX5V9UpGy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ae2122c8f375f70a1d174ec4d2d0fcd25936177ecb38ba43095d7166fc19da2802206c22f1e50d9e150d40e1d77bf1e81b153c546c3f55444e9073c440f5f9a08467", + "id": "bdaf7fef1b17036b8c7e15f7bd23c975166b368dfcfa840b5e569e5e95b71d49", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17763076895273, + "fee": 0, + "recipientId": "AGycSD9trbo9XRQh7cFjk68dniLHM7AfBa", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008ca39400df66d8192aaedf44bc2cd791bed8ac3e0e048f3a94da6d56e201b6a702205fa0caa9f2f27a0d5321e06b0e4a68ca30af1813f7e1069d8e70afa378f794cb", + "id": "d5102ff88df36c6c97dd02fb8ef8e178ea72e32556c91a2e36f41d2363cc4e7a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17882280422877, + "fee": 0, + "recipientId": "AYzPTsYgUcgZXi1GjA2wHzutz7BMEcbm5z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100da21d666d853ed342f3cdc1f29eb6e6a557ca202c8876723637b51afd38499200220453d1e8130029e2c9b27bc97babefa23385b46f42b25dadd9467e867d2a6a66f", + "id": "d45a0dad2d8010afcfb30088104c4821dafaffa0c4348db0004e95e4b330fa24", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 17882662903696, + "fee": 0, + "recipientId": "AJAz37b4PJ9J5tnqBoQy2TDoDE5TiN8qnR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220796c225df7d57a949b38b5dce6ec87cd4e8c1842826369e19ef2fe672c8cf3550220061c7b547e460153c1be12930ca3e9d8fa39edc2cbbdf550d3286ffc1bc2a628", + "id": "ab25cc4069078357692cde0f4cb9d850988fec28be74f7ad3e97c377bacec8ba", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 18446724051788, + "fee": 0, + "recipientId": "AGjo125YZU5Ssop4kuNTUYSeWqxMqvKA6c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fced646103cd17b38cce5ff86201c1426304b6ca429e5231422d69e1db3ab93d022043255a9791928cd34118c57b07f1f587811179f29f8065ef35efbc8a698bd61d", + "id": "197db69caeb74f3de59dc23a8c5d65878964963e785874dd6b8474890106b791", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20225218326294, + "fee": 0, + "recipientId": "AWDLp4YdsBUdxtqBjC8J4jNr7dddSCmbDJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203f5912d44a52659b0a679ab303f5044ff5413876a759cd20974afe45f0041b3c02204e313f1fd22299f8f811fd603f7482ca519a8ca9595694bd28d0416a10952fc1", + "id": "0053820aeed9059247f556586e9e0a4109f4579fefd5245cdaf52f9fd901787a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 20741626925528, + "fee": 0, + "recipientId": "AN9sqKHVipVtacB39BFQkPa6dDhdSbUJ4F", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207f0cc0e15878619c6a0eb4a321492c62cbdb57ea87c5d38bf8c45fe8f188178a02206312324c971349b94bbbfca52ad71928386d6810194c2e1d3d0d7ea9d1f0b216", + "id": "1eff5e79d10063be3e29e764d3a6e4f64cd681a0dabecd2568492e25355bd2d1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 21951221806397, + "fee": 0, + "recipientId": "AS7WWoFGGutbnpBg3GmzqSLapSRxqNqoTZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c98da3c32cd3fc557d169c23b454a294d7c226f5481593b22e4b4b0168b3110c022063e54d574b1580bbc056eac3598f96b3c6a6bfc4ff348ae3861835e6457eaf2a", + "id": "c14b7e1b79336f7c4b26ccd962d0d594c39b067cfc7475a2a577725937e1017d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22059609327388, + "fee": 0, + "recipientId": "AHDuDQzUhW9r5AEcxWYLg6rtnAej8LpjVg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200645d5b06ea85a4712b6dd58dfd40731036928dfd3c72142558b8d45d3889d2102200b2ca62f33a473edf0330d1773c505561e1334452d0bc1aad4eedd4a9771e5d7", + "id": "e5bbad079581ef00c4ade09d30c649cdd6b36b69437c0690561e7b950830a1c8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22212505397146, + "fee": 0, + "recipientId": "AYo8E9DEoYD94vJuhFSmZ7Z7JdL5vUuFng", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210080fcc91a7c3aea6ab14ddb92132d820f48d44d08f956c6463a40139cc731051f02202f0dbbc598dfa8830d86d23ac58ba13a2e91c283590dcdbda9dcdc7a5ddd9e52", + "id": "f8e39f689d66d7277e0afd9c5e8d8e90582abf5eb2517ada993d9db8b6a4d5a3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22232699614369, + "fee": 0, + "recipientId": "AL8BTYeajbkWG8r7A2uYpAsKoX3LMk9uga", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206259fb0a490e9fc37569a4f1fbeec01d783a53a18630b70d2ebba2bbe0f6d270022020025b4c0c7215410bb5ab68d5c551a377e7bf7ae7e364454b7baca54f1b045e", + "id": "500ce8f0b6eb93696a6445f97c26e559f93f043f80ec4eba7d8e59ab30cd6220", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 22622858823224, + "fee": 0, + "recipientId": "AWEmVoaBW9Xs9mt14YeXxmqfmSJ7iLszQu", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207d52cc181b70569c0f2058e68603232d5532b5ca9863ad0464ab0af2c29b369802201e7ca2e873ff0f73427d1afb837220a89f17204c04c41c15dc5a32b57e1262c8", + "id": "74f0e117703a37fb072ed990224d3173a02ed8577441faa8704e767f9dbaa517", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 23205200171281, + "fee": 0, + "recipientId": "AdCphTbTtmBouY5wTen7MSzjy9fc84J9M3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200ac08f44e5c63a577d4dc5536ffb1bfe437813b5e79073d3118f66011c78bb41022034517534731c508f7c6d87d44e5848cd38d8475802a453295ec332ba79fe1366", + "id": "7622484aebbb489351c45157af39ec0e1c2f1c75ba8b6d3cdc5bbeeb87cd32ac", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 23285447659034, + "fee": 0, + "recipientId": "AMnNU9x52nTd9kEu8rDQu4wrDm4DPB3SCi", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202b899c4adaf2d1ba16c09c995dfeb02489ea611d07ed055662388ac291f12d3b02203965814654985d31c688c2678085328126fcc1959913fb6cbc1cf0b671a85e54", + "id": "d2174364d96891d86b3de6457e579b53b5750218ef9bbda87b5cd9fbb0aa24c2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 23749226298416, + "fee": 0, + "recipientId": "AQFdF9mpwxWsjMASsSQFD3kMxghDVfkWh1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204a71c021260c33ee6ae8bb5966e6ab27f09b2c3f11d8123b85c532339b2f6711022042f89f016efa114dee8c5944f160129dc5b1329ecbc61222f3ea5d7a2cd0c547", + "id": "d231ed2b05da5b4771bca09d3262cd4b66d7efd38ee36378b93ee9e66f0bf6aa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 23788728527651, + "fee": 0, + "recipientId": "AT38VrTjd8gcmW1XWao5hHz7zA5Cxvzber", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402204f4d855fada8a76db9e26e0e79c9e67f51907cd2ea7ada9bb6d640c77d49c15a02206fd26c6dac762a78531b42e74ee643f541fab7b4467caa936a865b63302dab59", + "id": "4cfdd48b353e9f7324dc3991738c9dc8f4c93c51cc18c535390c5328a4ec5ed1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25133628072635, + "fee": 0, + "recipientId": "ATYKgxB6sMBiW8bkuiq18eZBNM7ceyJ1zK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a7c5a575fc88063777204691c7d8c83bbe794820058c87a47099b4fc523b7ae802201925f71003886a98bd9a14d2e8a4596acf325186449825bfad2d2b0947e75967", + "id": "5d86d1704c731ec1454b149b7105c223ada5a8244045c91a7386444cbcf9fbc2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25643974662172, + "fee": 0, + "recipientId": "AVQ1UscRdsXQZncshwXSZEEWqvPNMJXtpY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210092d6502d9d4945e59d22fb7a1141717e41918428362fac7b365c5e6d2ee9bfdc02201c86ca71aac24424cbf2ac44ca3b77088cc7c873f826b8b6542abddb68ba50dd", + "id": "b387e98cd84f82172c378834cc12bf9b21646891deeb37ed9c2647bf400009c8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25690453309429, + "fee": 0, + "recipientId": "AMpKaxERjupes1GjHCVV4Rctj49BJXQ5Qk", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207bb590646296b2ac4b2ae347eb2da399b9df6ba21273b463e7117dd14ab61e4702204861d99922766f5a05b12730ffd84bb9784a37c8e27b58aed3f0b896230ec082", + "id": "81b65afd6830ef600587c8f22cf425cb485df032f8fa236121d986a0937ba41c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25763719365860, + "fee": 0, + "recipientId": "ANymwDRzUoszXjbJXQMhPBmLrBv37AoysR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008815fa72ddf054b47d90c7a4b9f12c783eca5ce10a557caa79109ca2e51176a20220595c9d31fb930159603274e72362a8807999dc80b3bb49ec43958837ac60d270", + "id": "434775c5695597b6554e89281a194cd903f8808b655c65f1b8cbcfae3e39c89d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25896835969408, + "fee": 0, + "recipientId": "AdLxpRiPQdsdZqxQWEc2kYxD5YFzKsLB5T", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022079e5465fdccb479bd275f6157553ae46ce6b5c51dd51ff0fd4ec517b6deb7c3d02205213870fefea75258a08f564a7920f2740f4026a8655fbc8d1a6efc6dae611b6", + "id": "3c33d4479fbe31777d0263d7a4b6c035be675a2a8b9eb9964353af2c43737a61", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 25963064569714, + "fee": 0, + "recipientId": "AYqoBy73QzTfN9LHCgvT7eH4tfet2RU4Av", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e891a570ff244ae679fb0284f55f65595d7d86ce5ea42323708bcf5daab0b67022078488e4c73790511ed6c1f9bebe8bcb0dd8914fd9ce070d23ca5553923f9d775", + "id": "5016cce5130bdff278ac554c89615fce32aad318bb8917784b51fe40bbab4e63", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 26922263723607, + "fee": 0, + "recipientId": "APjnWFvHhfjudvjXHQ7t3d3JuW1efhiWkM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203bdb6af67dbcc1a8a1fc4a71d9e7fd6ca66ac92d0321eafeb3e7e44aae2d25dc022045598a45acc95423dad43741624c047bd2c08aa878cf3175a789f3ef75e6ec26", + "id": "981e2c0669340b781e6989d2e4ed088e71df124c2c3f90eb97f024fdbea70ef1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27197785575254, + "fee": 0, + "recipientId": "AZrX4pmxJEzr8WkANjFsofL4ww5UeqbVE5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201b3c396a9b310ecc51420e3ba5d9a711269202abe71353aa9a99c9388b3a282c0220221c52a87c44586507dc1ba810a7aec7eabf07d84ed7185616a3ec891f224fab", + "id": "421acc286ec7cc4bea14a8d3cbe33e77cfdda80ac748e27e4f0dbfc09584bbc0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 27237835405410, + "fee": 0, + "recipientId": "ANgMKTXMWHB3WnU7sBeLqYmUCN8khwc4o4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fc96ad466ecf9cde5f005a6124f12ea237e66013b782406db1d8974926be0946022063f106ce7ebeba7a8c56393748460eee5ac888385a2467cd495d0ac7e6dae9d7", + "id": "594abec1830a739b1cbf625eef2b6301b5595ca5f229f353545c920c9b74d2e3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 28691934514542, + "fee": 0, + "recipientId": "AM78rDegT1jy4U7CEsWErtPSuJCdF6fJVD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3043021f33602385c5f0c9d5a374ab0a835c0ebecf104951dd15dc812cc94738ed6ff702202c366784aa6cec2524f816bb41b0eb397cb0702ac7e069a7a8938694093b868b", + "id": "c05ad581c646d9d8975a65e54a7deaf2c255fa6622af349bfa16a9dd753e418c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 29252093379711, + "fee": 0, + "recipientId": "AUL2WbCnaYBkSRfz3Ak11KA3Vn6b6eyM7B", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402203e1c6a962f0d9dac66fe40b694ddf60d1ae12a0e8d7d4d12d71615dc08c885380220597d2eb1eb70e8afac03f9f02a0fed3532d15958d431d41e852adf147015f43b", + "id": "909a1c0f7371c0e7bd5b668e80ee67c980c7c06ac95c9a8561e2d245439eb29f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 29403011432706, + "fee": 0, + "recipientId": "AW3MgXojRTWgrx4PzBmk5pjn5nyhY1aKH5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402206afb959420376fd8e172ff9535ad4c73423eb6528b90d888ef4698437e5560e002206d00854f5d1964357562ed79f680e7d5cfe887ee413073645842cbb248f5c34e", + "id": "69fd70220c9ad3cef6201a5043bc55f944cabbdf705808c6331a947ffb7933e8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 29495120724982, + "fee": 0, + "recipientId": "ASaaHvXuvS52QJuvp3nkm3EC4pEAze47a3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c3838c356872fd692642ca595c4c53678396cec839de9841a74a4799cd37c41c0220168441e0f980c92516b41c8ea7e337e384ef95ad130f2a62d297bafc5ae6f710", + "id": "87e6c248637dc0774a78b108505ad19ece71c9248cf5151ef517561135c5467a", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 29617494546387, + "fee": 0, + "recipientId": "Aabe2A3WdrsrhBp8uTjqxUEQVyB7G1tica", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022011583f87dc647bc5887425c585d04c395f17c6bda80dd0fb2b310acde7294e1102205e64a69a81cd848f5743ec144bb932ebf18136f952b240f5f579e9524ea1c61d", + "id": "ca4e72b41eb5b6bf5210d47eedcef40cc9ce15168fbeea443dc8c179ffa6e711", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 30052189818242, + "fee": 0, + "recipientId": "ANaT8P38GwSHV9mcCk7cEZZAieB3Mp8jxE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100dfe6ce17738cd9e3565cc2ce92f2b1171104573deec8f7c35ebcf39b906afa5b02206310b1b28588bdee1c4c9cccad579606a0150a73fc2500a78b7f126c6a30a736", + "id": "1aee700c3737c07c9a6a8dbb6ad404ab7dc313af7e1e0c63e07cbbc1b3642845", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31009000000000, + "fee": 0, + "recipientId": "ATgAA1gP4awvYXk4T3FrHRgpNsVtaBU5UY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f44445e667716c93963815e8872bb798f050c69dd18cf11260cca530866aaf6e0220741085d44bcc255dbfc4a8f34ba652ef20798f50ea350929196dcdcdf79685ca", + "id": "2f5f4923cb78e7d9aaa2b5028c16f3ced1448fdb6749b82711b46d469865bb1d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31239229496679, + "fee": 0, + "recipientId": "AUcXquR6CPooj4FPVQomhCjfhJik6KTzYp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e20a35c940073e07cdc048f9cab3ce5417ee3886790f8d8211b72d0d2c68ae97022018201c4a586e505c12d7eda3a6a8976fa4009bc8f10e11debcbd6df8c92bc59a", + "id": "11d6023327daa0bdecfc7f0db4b7f476270cce07d1442af2d81282004841f302", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31413424826151, + "fee": 0, + "recipientId": "AdYVznebAKBjBGhNuKeKA4zzXLFJBxX6Z4", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100ca7de35722d5a049d61c01a8bcbd7df1a990534e589e5890d07cf7ae6181c0da02206ce232bf4f5d2e144e0aae9001e0f64f38b462720be5417ccd4e3192efcf54dd", + "id": "db369c7c18acfacd9d702e63c7a77e8bcb07d4e104ad8fa39ffa37489e2f1b29", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 31703556507222, + "fee": 0, + "recipientId": "AFumy9qrTPRdfjMq3Q71CyJGADVURsQUyS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220082801ddb5cee6641ae199fc46cdab4d8bf945a602b60099ab0a6014a07f1b4302205aae82168430ad6632563426a4b6efa855d7ba81773a50894427dc9f54832580", + "id": "1ec3d57963cfe1528f5032fc045c7516f2e811ae5b5362a5e5e98b887abbdd22", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 32988434694317, + "fee": 0, + "recipientId": "AQpbXQB2G9fyoTsc84PZKtWeTANaXg6USH", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220118c9096756390292e7fadd471894e27610985552c8a69abc091abbce4426527022073bfcdb62d459942d9fedfc00dd3b7f473a2b7a8f3b607caddebe79788732adf", + "id": "f5a02075ee137276ace1af818d4e40826cc57ca42a28e2f4966008114f1ee3e2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 33301358574530, + "fee": 0, + "recipientId": "AGv459sY198H3RyLRwSUNUNVY2sdHBakyF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100baa3c39d1f99a14adadaabda9abcc74a8eb0fabf4478ec84e44c4c981194ea9f022026b3d6ed87d29120f5338fa371813700f08d989f8473f4e565e10ae7d7f3a5a0", + "id": "f85ed28225d31a5e12d1390e288d870d3fda8c31004435e7b581fbef143777a6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34838200000000, + "fee": 0, + "recipientId": "APz3bVED2boFzmM6X8Ri6EptTuwYecymNG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bc5e1636cc1a3a20189320632b53d9ca4dd4d6ed236cd79bc39897cb32ce1b4e022012c647f2c41f7f7be18989d37b837a1c597b9e8d9db34830e8c96aedf793c36b", + "id": "f762656f41ede6c6b2248a567e9338fa5fbcffd6e3acfcd166b3d7091dabe45b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 34933049632938, + "fee": 0, + "recipientId": "AGnmhUFTHK9waUB2RvPKcGLBfxRow1Hz9e", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201feb35774ceabcbfc577b257045b93395c3eb0e81d91cf4e0799d0c74fa9db4502203fa395d409e2aad11c3cb7a0db6d2904cda3ec8c850b810b737202a9717d90b1", + "id": "9f9b749ef794c617b2136b392f09d358f27c1c7ae33d917812f777405d9ca61b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 36753764290883, + "fee": 0, + "recipientId": "AXicS7Jbvo9wCN2uVa7RfBLrBS4oPHXvmF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a9d7ffc30ff5da829ab6a0705fc673c208fb1d90b4f37865fb859fe32771425a02204dd95d698733a1cd9ea1ae28044186dbca88e643f740593c4528e2a225be7656", + "id": "1f308b5f48e5396ca4b83e14ad69a329a486d695ecbd6819be782db4e79ef161", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 36753764290883, + "fee": 0, + "recipientId": "AMKQ7YtVjAHt8mWniZsvj6rqmA5jzu1rnZ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a75993db6caf823dc6d38968f205c3fec9406d2e215791a62cbe9951bad8677202202d0ccd24e0c9757d557720cb40182f5bbbd4f17b756aba16289de6190a2e7205", + "id": "dff7c0fe0b3548e5b68bc7705dcf7a33660bd77cc5d66bc1a5445f1ee5eee757", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 36827565849579, + "fee": 0, + "recipientId": "AdUHt2sqD22q3Jhby5K4nSVhBjPaBDrcN7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008dfbca2daed7bead47d885fd3f763a75c4b076bfcac2ad209987a2c0a2ab913c022019fdb82528d253b78768f8b8e508da440fe8f2f7a34ad9aba2c37f91df76bd01", + "id": "22b6643f0273b7fba1e4c4095c245ffd0a59f8c75582bbaba951f0f626fb6b67", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 40000000000000, + "fee": 0, + "recipientId": "ALcLekYJLkWKLwEX4TNsQszu8nfQbWSkDt", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202a594df3905ee3f8c8db32a69857b686badcc6784b698f8ad1ffcbdc5cb5d2d0022072a8e72f42fde11f54bdaccd48de55ed2d3046b1f426ddbb560b97658a0bc83b", + "id": "08a515e48fde2ed1141f3b7c6726284d1ae7b5ed1dbef1742ec43bf52d229eee", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 41574092648018, + "fee": 0, + "recipientId": "AGctmMKFHy54o2pzgeh4ESHfqZ9B7zMnzA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009ed97e274d06d1458aa4d0860329931ba6e3a2886f45ca9f9cdbe46f089d6a85022068a1f3f8edecbd96d580148602ea10709e92655fde424d68fb8d049d8841c418", + "id": "afc119b8eb143c9fffd3bfe31a39d7e42497770bfbad4ae7190b5b7b2001c9aa", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 42101522494901, + "fee": 0, + "recipientId": "ATAu9Q9UEQQua2UvXfQ6aev6eZAqWmSNFp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022060955d98b915a99a40fab60a89c9813d050550f17ea432f309c62d1e4da4f80602205685972723f6a5223466af3fd20c15afb95938182e7b2a636a32f6ab88cb4628", + "id": "bbb16d3b87ba67e0ad32da5eb342cc9bf60b51825b11d2d435f94afcc4ec42e3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 43098358629104, + "fee": 0, + "recipientId": "AWbC9Wq2LyJki7J84AkTLJkzguF9mo89BF", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100fa22327b9cd6deb3153c39d8f5d76739754798733aa96fedf2283dfb9b1ba055022014e16da33b966e40e27c883b314341391b7f85ee3c1f272dae914533ba540c3a", + "id": "8a87ccc624c279197648fca4c03622ca87b4dbfac293377c260e536e6ca969f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44103929088831, + "fee": 0, + "recipientId": "AUJWBJDUgY52u89unPzk5oV9e9xge7P1Kp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207069312fd56f16680a836c2a45772777be38c18c6f86287f414e786b8f2095d202203f646bbd58f31c2618c31649f1c48bc1f4ee3ec791a1805d788497179564b72e", + "id": "ba49cc0e31cee0207d2f0211262dfcf9787a314f0ce63d3c292f8f20c596a821", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 44104664164117, + "fee": 0, + "recipientId": "AUTMnQD8ANkE1wtMnH3aoPCrYkVntiaRDc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402200bdcfb494ba5f808db740b6535ae3adf6719d3442d618e027314833aaa3fd7ef02200cd5039225a23be87af57866b6de9c0e005a643fdbb9d94e2bf0f1bc9eb4227e", + "id": "954643f48700f6776cecc0c8d28edd052b6ada807c4ed80d4020db027f8302a1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 46581295003558, + "fee": 0, + "recipientId": "AM6bDiAfJMXdivdUiGaDy984KA97ZEcKWq", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f8bdac69ac19d78a7db5398b5bc643399689262446788af4805d85766e705480022066cc9169d1a7f9f53351ee695e2016ca5384d8f7069feebd59e8fb4a9788559d", + "id": "545bd5d745ede704e8c2a8d69a2eb6925610f37e7fa79ed9483266d860c7f512", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47047758593474, + "fee": 0, + "recipientId": "AWvqcwdrJyDEfsbveh5F7Nh6F1KWD3KAHS", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d41cbcf3488a0246488bf2cf73bf1e4e797260d1a5250ed8f04957b31586b0fb022036ded1aad7477562792bb697ed03f86d158ecc8dc325f37ba3efbfc2fa7c1fa2", + "id": "45838af24957952037e376f81200350a6d795b8435613a4845adbec9dece4d66", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 47594373941014, + "fee": 0, + "recipientId": "AJUkMpMFt8Zny1v1rENN9ZPZYitfePBvyz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100c5d4aa1d91f3a0b7468a31097fc9613e9c3830e1b3c6dc3926217442fb605f6f02207efbbe01ac68387e6fe0719ee635b4574d7040595d086d7030f903070f7c3247", + "id": "bb58a2aa2a8048b937156f3618663c1015f1b9ae7527ba8a0bc351c6f0bda349", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 51199117149060, + "fee": 0, + "recipientId": "AZaAZmaQgY3JaUTK72WUJEp9ZEgQgW1r6z", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220060cdc83a40e3ec69b3359c304a0ac46d863617599e2de03fc9a022efdec329f022018cbb65aea9d099cf6cc372a6599525b1aaf8a9cba53edbc95235764d265b3c5", + "id": "97d3f0573caf2552f8f5a696bbdb7f95782b98e5057f26ff7eec8e152513a3d4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 52274142846490, + "fee": 0, + "recipientId": "AMRYJaup1hTjCsV79HCTn4ySvhAzb8xSo7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022070140fe92e3dcf59f0ae1a87bbb061cc4048a8489d47cc37a3f3ef489548dc2302206163dd3630b960269613c98ca886637f32efc495f6c4e593cdb8c91dbe696c44", + "id": "223bf57b085d5fb463d106ae666648acfa042952e7417e4d22f51a100e3bbdd1", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 56564297142251, + "fee": 0, + "recipientId": "ALy8rPTjEELHQbkkDfsQTWT1M4s1Q4NFDX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b5f571f366492003d3c17ddbcdafddee27389907a33cf348f7ef0b4d6f4cdffb02207300a2d680dd5ff2fe2fc70d71e76c875ea15572be63fbd583a4de383f99c6da", + "id": "5333df7856532e4d8a13adf69376b48c986a46f773a3e8677b5722baa45bbda3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 56945445022471, + "fee": 0, + "recipientId": "AVRGa6s7qAzChqfXrbJEhExzrTo1uscsf1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d41baf4236b93dac1d8604596cbdabedd8081b8666d239f2a193b69e56acfe490220046d22d267e480523194aeb5890d54b5e161718ad754321aebe61a5a9a6a4947", + "id": "f466357fc462681688413511091b23148fa0271b2045dbd8b28aa591db29f850", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 58952449862348, + "fee": 0, + "recipientId": "AWeSiyLTbvHyfqmUNNcRZABWZkcd36oz7x", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100d8f5f18629c666ef1aad569544d581c9c6fe2beaa751658389ef260559f946ff02202da47ce0d71725b3004c43de69c0fe0a5fc94dd7296d16b5f64c2fb9ce1a515a", + "id": "edacc4e6c26a064c04742503524ac91012a87e77f6bf7186b4f591f3221b7b58", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 60406501374583, + "fee": 0, + "recipientId": "ARviCJv7BdnDHCxPgFa2uqRLXNNtBqc8Jv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b49e507c020746c9f70dcd26f52d071e05bfe9dbd303d528812a700883f7a0c4022009f6cbfd51b20e464451a6ebb5184c0abcfa758ca304bc88d031c8075c7d6902", + "id": "daa89776995d4fa94c6c64cd06b1c2d1036cfc9082151f9d3a70adf948c93939", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 61218300000000, + "fee": 0, + "recipientId": "ANLeC5wGgtqiDQcX5pmHiYayqMyWy3AYCQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022049a07caf745eafae488a242e5066c8d2220b99ec3127818ff9ccb2faf682fee6022072aab5d565ac05759ebdf6f896a53c584961a6c0a362a321cd0efb2e78cfa9e8", + "id": "789e6d917e48fa9a4538ce5948a1c4013c189d080b94275eb975299d3d22177c", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 61966258534201, + "fee": 0, + "recipientId": "ASaaFCmPG3yPUwngEFoPA12KnQYqjgVKnv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220449a94f1a85146353328281b937a9ce91b5644235ece1e2046a28d1199fc7f7d02200dd68146162a869688f44fddddb990bd2edb1c4194aa8c1edccd72cb1dfb7e03", + "id": "e09409c50d3ab8e86cec1216a5a8f971a8830c4bf6f847fa59c7eeff606ee9f5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 63613272588492, + "fee": 0, + "recipientId": "AYpQxRNbaiEL6FT7YUNmjNjRvpUfvRh6xA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100953475d0a8a1f5b26eeea7d4697faf1eba8f4a4e465b387f6c3550407c2adbef022047a7f4af1bbad90c076617319eed901196e46a692131fb1c8861c3ba2df2a23c", + "id": "311931514d05d2af41ba127a24dbc950f7476f78d9184469947d4b3b59561c4f", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 66966172832127, + "fee": 0, + "recipientId": "ATVwQWf1mpyRa7L4BAkSUxz9osCfBsBopT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f571ced76143801d0760e47fdb4583bdd45d1dc46d03fe9195be5cbe0a9f44a402200f6a4cd36111e9b5dd06f966e3ebad05294ab03b5be9f6a231665ed4ae53a275", + "id": "28a7145ea1920ab8de9b6037f050627ffb2e37c9f9f1d16be8db7110f401fbd3", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 67622306078799, + "fee": 0, + "recipientId": "AKzAriWhTPPRXueXuSttRiBLkjP7i5wKpQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100969e9376ec702a41b6da148d02603e8e1b4c1bfbf52e060aff80a89cab4eb3c9022076972f9de9cc4e883c63ab263cc76df952b0bc55dec80b958fa8069b94046f12", + "id": "7a6649786a864355ed73e0f194caab15593a11f93400b13ce6d0b7030057d903", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 80563884771309, + "fee": 0, + "recipientId": "AMFQ8md4BymLJ2ecw436fhyUyjyRM7ygzz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220188dd6b6c7e1c09b420ea73ecb17db20d85848342cf45c65192c77f5390fded502205eb3a4304dd2ee62ec1fa9e848dcf2d695ed92162e13b52271e3430ebde7eeb3", + "id": "fdeba3a56077063853cfb940d036d3ba75e03b6fa68d45de52bf58d9165c4b96", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 81926947255471, + "fee": 0, + "recipientId": "AMMDbSvuBNCi1oAXvqgBguAeTehWRTjBrc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022043e6c8beefe6b5ab949adabed4830fd87d4639a6afd7fe8e1c3de3087f7f47b302202cce568aacb5bc2a416a20b6f940020b2e9c1f0404264230bf1b9c0086735ea7", + "id": "1630a5b12b69dc1317bce4ba97a2207c6bb4ff8c9e3b5af88d02f4b42829d51d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 97831169789473, + "fee": 0, + "recipientId": "Ab6JpVxrytQhj4QpBJ3LGsLvEhVmgCKk6Q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202b44c2bf60f8af0927f9a5c62fba3ceabb58e5944dedfbd37cef6368b219225502201c1074fb7303b8a88479009e3a36dda95d6c8d0bc60770bd7f12beafcf680c0d", + "id": "0c8a56b4a0aa03b2f49dce2fd0e7f367096065a51128950188b553b8a750d4c6", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 98443286091931, + "fee": 0, + "recipientId": "AMQEjpMh7o1sJT65Pdm1Wt829evGsTWCJ2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304502210082a8b45a0e351baaefcdddee3583b1b5ed7af81fdc9e2aa7243696388a5d6b4302205bd1469c96a3494a9b0bc8cc4513cb3ca57b8cf3b3250c028346d42666daa02e", + "id": "564f7c69a9cacf987aaee9ecb5bb726d1ae26a26111f0f8364611984d18b300d", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 100568151043750, + "fee": 0, + "recipientId": "AZdsyiib53nWP1wG5wara6XfvmmqqBWxWR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100da0594484575e66f1eb34211a7d3249c15d0eb422be3e1b46ee364c4a377befc022041cd9cd4237cc8a1a7e9359adfe182da7edb24a69368ae1deb7e2fad26aa0312", + "id": "16a95bcd5ca6388de776cc01666f616f863f9dced646badfe97b53a266985bbd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 110555322986976, + "fee": 0, + "recipientId": "APaRmUnSummHSAyHnYqd1BGvPqgYReEuWG", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100874c685c11233280806221994ce6ac3be8ca6f2dd88421991a1c9810614e9bb5022071ededd800067a5a3e6828ab6cec496b153c9331e5f08a417c09e583b05219d7", + "id": "a387aebbc8fefd8f0963f2ffbac837f5bed99137b19b19c356949bdf12c0d0cb", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 113717099151927, + "fee": 0, + "recipientId": "ALC5AHjaUUSaecu9R4wADbKdqzqf9U4e19", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100b5b9bf26d7020ca50abd010624a7443e7e4d0c5d17c50aaff12c2a357380a212022037b7e9e80dd9e5a092d99aaae3fe0722ff6e085b8ed42e4d47030e9497e6093a", + "id": "912d5b497042a6da16f4dc3fb0b7711f19aeadd1ebcb5aea32e2e49d6b2bcde0", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 115371683274712, + "fee": 0, + "recipientId": "AVNXBEcnwKmirrG3kcZSzHa6JvRkiCKqXc", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220401302e2773dfc0ef1a6b4b013d98fcbaca1838e9c688013989ad990a7eb51dc0220268eb491d53660b33ace47049d96e4c97dde689621044002f703425e17a3cc3e", + "id": "1dab66009d3db2e422824d6af3e727ff146c2b3bfb1daeef583742a920d51908", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 120262700000000, + "fee": 0, + "recipientId": "AHHpmXD7F8j1r5bftPMkp35ct9qGYiupwj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100edfc8dbb54abcc1096194776c222e08fae1e125851b580f510ba5b6c6ea39f38022032b48c581a123b298838dba4a22b71ddfc51c0514f260fb07dcb2374bbfc8e92", + "id": "5ffd7c36deaef4295257fec15f27e451864538aeeae9b21ded6d4e579f19d7c8", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 123626090911753, + "fee": 0, + "recipientId": "AczFP54nMoafRABK4c7idxHpAC4RwmQsDE", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402207a3f7c3ecb0295f08a85e12af23156e0765ad1540047f68cd89be139307b5bb3022072be91d55a080d6e5fae0a327225dac4c63505b292dda6958a1207f9adadc8c4", + "id": "03b08987233a71d17b296c057e03821022e9b504b47471670ddad11080e1f4b2", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 125719320015809, + "fee": 0, + "recipientId": "AUq3S7bXMHmz9zBbz8YLN6LrSrshvxXTU5", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022033d2d418dae0c1c1ef89b1866258410008eb39a409c98f6ec3a73918f449ead102200ca603311c12cb4a5f050e0872e05a0e8f2953827b168b85211b179600154585", + "id": "82b2c63e0139fa52979e008e6a3d3086efc4aaf5e1799c57a29bdaaae9a50189", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 126273901505716, + "fee": 0, + "recipientId": "AdCXBbcpNrnMDHMar7Ebjxu959uwN2jt2y", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022078cb591e525902157b5736e798461266a2c7e32d7aa4d06d07f80d64244e0a2602202981d622da3539fd16efc0aa0ed8503ce2470ce8cc3235cc325904b9246dfb1d", + "id": "367b31d00256d8608bbe3b62f350bbbac10d896a3a3cc084e203493abf6f74b4", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 126299500000000, + "fee": 0, + "recipientId": "AeYkC4MJ24EF7yVkb8NfSryBw9xrTvSJ5o", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100f3fc2ad6305967410752b664c2d8bca041d552c013fa576e10889a69a6c4fce5022067ecc8de665c05a1594067f873e067db5e1884486d4815bd1cb3959c282cf0a0", + "id": "af6c2725034e35ca7dcf0b20a9161af2ece9d9e30d1d8f934661c2e121844832", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 126837200000000, + "fee": 0, + "recipientId": "AXx966jfTHUz4nSjZsFEKkpsgNoByhuMJo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30440220025e2076ba6db9412d886925b4ccf810ff5aa45711ba9c9f049061f675560937022042ed7606e3f46bc5fb5b2ef2b36e894666c78804a1d95ba7916d6a41f92e36cf", + "id": "e274c6833dc8021404af6782020539329acda8f274ac2be31535eef95cd70668", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 127928194702051, + "fee": 0, + "recipientId": "ARwU8mRM9aM4KjwFLpNH8eHntj157LNAW8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022050f8011fa9011b5b4501bcc1d4154864b6c5125e9a00f468a194e59edd32831c02200c9f77d38533ceaa8bbda2bbf9ddc73a7d6654d3d3a338a3e04a80f67fe16a59", + "id": "2debd1afcd9cbc2e3c57dc27258cb49d6ef21b10d36648efacc0053acf7cba19", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 128450700000000, + "fee": 0, + "recipientId": "AGto2VECRE2tGFr8X9pfhqHNgF2bdW5mgR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100aecdaf102f6d5a4b18ddeb17c8d5c00c18cfe0ec6382df8c972937a17e9271e0022074d6bf200487ee5c03800c2cc28e571bea6129c95ebffc2a1a8435fe6bb351d0", + "id": "7477beb697266fa054ab120b1381e293d8533d1a56eec29731525b857d05e8dd", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 136497083929386, + "fee": 0, + "recipientId": "Ac15ysDrQ3fvMYL8S2u52VSwXZPKZQs8MY", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402201e2a494e971932480c8286c2c12748daf90afd3234969cd720ac9346da94fff802203fdeef1aa6daffc70709e5c86bf53fec3169651671060b8e495e1c0c40c725d2", + "id": "811f46c52bc4e94cebb2c8a4053c13c3ac3f7df24134cc027a37f7614e935a7b", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 140099329287361, + "fee": 0, + "recipientId": "AZB965rP7y3PUs8RXqpkRy5Uu9dbQgu8mM", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221008fdc7606464939463c4c48ef06663b63e447db9b560fd323efe695b59832e792022048631bfebc37ff98d5d5089f607869eb05b86119830da14817cd31125a4910e8", + "id": "8bb29e211493fd3f3282498d749b55d5824e9ed9d5813a834258b002204a2cb7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 150034082630911, + "fee": 0, + "recipientId": "AQK3Zu6VW2QMh6a3ckNzNsYduTV1MmDkyJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "304402202d42c892d4cd76cb582573977a7b2a294a945754dbd3de4411fe3a3ed814f0d402207c49588af322b49914a933cda46e2ade762f5bb9b9383cb690bded2f0a734d90", + "id": "7e1e33f30bf45a2b2b7be555693894171481a7cbdc48397575e6a99d98382138", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 151578069939920, + "fee": 0, + "recipientId": "ATUe1KJzRsHUW6rA8UnNNNo4yVEF6TqyEv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100e97f8bccc9e3f5e3c3a5b32987c64caf4b60faf4a282784ab3b1ade1579c3b0c02202041fb2375eb061fc3ddb64cf94edc614a690d6fa9e22e9964aab80a66eb5b34", + "id": "be993fc90ee11f5b9bcbeec1a272e94c1894b65804011f76d0d3227c71ba19e5", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 191573850243351, + "fee": 0, + "recipientId": "Abee76TtVambNDBmTTyoCYosWQ13MC9XEn", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bca5294ba5fd28e35afb067b328aa2469656158f4c8580d98bfea76ef925761102202ca7d152501f2999169ee65cac56b25f41d9b140d11708d236f9c4be972b0dcd", + "id": "2bec691439fdb6fd9a324f26d44e2ad64bd9e2ffd566d220e01c4aa12028fa91", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 196019568105246, + "fee": 0, + "recipientId": "AX1ZCuzNnVHfPvKikwsnsNRUxXfKq5g9dv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3044022011856a7a6cf61478d183e18a4a7404241dc561629d7db7ac70d1ff385e85d724022003404898de3709cac4bd5a6aa718c7ada3c9d08219c60f034a45da8135ef4a51", + "id": "57b80107cf40b39135241aa92e7e254da815763735f0ac5a9919dd8485865d80", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 225493764614631, + "fee": 0, + "recipientId": "AbYwZ1DAUDLgJwusR9bCZoTfuUTwNq1C3c", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100bad48978061d554290cd1afd07851e3101aee65f3b7f9f677e85a28d6590df5f02204d7a721a90dcec6644bff0aecd2f1d5b506c334bad8810b1b2d123b6978d57aa", + "id": "5ffc47d5a0826f031d51f0502e45ddb9d7b8c4f4bf9b2d4bd7a60c4ab027b541", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1034744897737528, + "fee": 0, + "recipientId": "AaFgAghGAmNWndDnaxkbcYFE7fxbxZn6dJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100a4489ad919a05f73c3be4de717a3b23115812c146caa1f33301485e1d9521dbd0220487441651432d5b34bbe7f383ee3a1bdf84dd22b82982d9566020a6033783685", + "id": "99a3fa3f91f257fd3e3ed284a9bbcc5eb007ca7e004372a5912bc3850d5ec9f7", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 1256766795179826, + "fee": 0, + "recipientId": "AUhVLSiqTnnzqhxp5RKzheuRyvh58TSCUR", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "30450221009e21a29b8428afdd7f140379d839d5ee75191c3e5add0582f58b9a3b2b3d69e30220749a0e7a3a8c63e877f1bdb0ec12987db8b138bb1746efe0b18d9ee817276cfc", + "id": "ac7ce9c3dcbee526a9f15af016fc15b6dabed5f8a61997c4de15fcc987926234", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 0, + "amount": 3141438227793004, + "fee": 0, + "recipientId": "AN7BURQn5oqBRBADeWhmmUMJGQTy5Seey3", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "0235d486fea0193cbe77e955ab175b8f6eb9eaf784de689beffbd649989f5d6be3", + "signature": "3045022100df12738ec2164ce2d2ba980ab8499dae066149a1019f37ae5b17f28e9cc2aafb02202a0db97f9e012f2a8ed819d2cb02e5d9e63fda01754d6f42308c3cab4bf92e1a", + "id": "5222124556517be676fa67fccb07fd581ec2928140a72afdd65259dc3e5db921", + "senderId": "AewxfHQobSc49a4radHp74JZCGP8LRe4xA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "033c9e5a710bff3131b406a8023a60e6b76a2ccf937cd85b56add7c4a33ae3090f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "033c9e5a710bff3131b406a8023a60e6b76a2ccf937cd85b56add7c4a33ae3090f" + } + }, + "signature": "304402205532bc3fe7be805b4c8a0df2995158e634c7146c2467d05d75f754782b87bbbf02207164df69b13c9c3c46bb3c7d027e7ba81eeaf726f25e08d268c1507da4581b73", + "id": "8cc98e02422afa8a246faa66c1672b62996be356735b00614d8739ea3a5f73b6", + "senderId": "AQaKx7UU8857b4tJij1jb7aURUzd3GDyKt" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "031de61bd525abc902396bc66daf513eb20715180beb50be3a1c56a36dc73476a8", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "031de61bd525abc902396bc66daf513eb20715180beb50be3a1c56a36dc73476a8" + } + }, + "signature": "30440220125d7a0f64c0328590d67dd27eb5d8f209bdcce03d730c7469ab25a8943c8b920220027305ec4ac8d46b0c2e711274159ab2fffec7f5a5994aea775cd48260db93b4", + "id": "bf2641e6ffce5848282027221c7f43eedbd0b72a7ee2a56cee1472c5f69f194c", + "senderId": "AU9BgcsCBDCkzPyY9EZXqiwukYq4Kor4oX" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02614722c83a05de882d887ad51bc5c687e747d6d19b58f5731d38223c58bb6ca0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "02614722c83a05de882d887ad51bc5c687e747d6d19b58f5731d38223c58bb6ca0" + } + }, + "signature": "3044022078d5f10573deff4cf16ee4b253c6b985b8a16e5f7f4840493e2834809e26ce6f022020d6502389f7512ea3b8f9fa9ac296c153aabce8f9066cd42d854b94069f6f2d", + "id": "3625b2591f15394ea0dc6b26a113284b1b56de0cf3a21c3a1dd92f373f796982", + "senderId": "AJqbav8MPPAe3zdzo1f471gaciQbx1SRe3" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "029191a8ae0fe0561e3ddce562554e8ba5ba36cf9ecb389290a1a74acdc53ca89e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "029191a8ae0fe0561e3ddce562554e8ba5ba36cf9ecb389290a1a74acdc53ca89e" + } + }, + "signature": "3045022100abd4ff196ac1fb56fdcc0f0b71a49ceca5da07b3631570e22626fb43a415a9e2022042d32e0fc05c6e2467b9749ad1f6bf5e1fb9cc8eb5c21a40baa1381cf58fa327", + "id": "05c1fd17a20d62c2740782097261b960f9ad353f68b94d4a13dcab05d08f9dd1", + "senderId": "AXArbuCVSiRuYBkzCAajboxCfNiw8AyQX1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0204f8aca74a87f69432298b13555cf50aa0709e1a942aa3efb447620eb78bbcbe", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "0204f8aca74a87f69432298b13555cf50aa0709e1a942aa3efb447620eb78bbcbe" + } + }, + "signature": "3045022100d15eb20b58fb9a6e7659706d20c3deb26dc176cdaab02fff557dc5a8d65982a3022064686cae9101510295993e1d3663dffc88741bf1e2781021e28a7b2aa71ba80b", + "id": "6debf628e330726bee0f64d9fe7c56fe4024941b4394ad5c7cb08fa9a3abc0f5", + "senderId": "AbrgipEvLp1ZHNJ1GcWWAsCLNgSgDG6Lxh" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c12428bb56ceb96e9c2a558e045df6b7b2c551fdb6a132ac6c3956d10f479f52", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "02c12428bb56ceb96e9c2a558e045df6b7b2c551fdb6a132ac6c3956d10f479f52" + } + }, + "signature": "30450221008c43c6f251517d972386adb42d69510ca59d882a89d7fffa55484ad7989a3bbd02200925d009f1183934e4be66ad48ce7149c990d0d87afe629c14551ed34e54ee1d", + "id": "557d5fb9b8b948ba2a6fd3d4dceb476e97e61faefde8eb11f4372028346c5aca", + "senderId": "AbyacFxcWS1JsokdRCx8bFsWP8f48XftmS" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03f11ddeca3845ea59c82120a7eca68558c99e4e7d373142ea0554408c58ca82be", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "03f11ddeca3845ea59c82120a7eca68558c99e4e7d373142ea0554408c58ca82be" + } + }, + "signature": "3045022100f04fdee2cc09b029d1bedea130174f21e24e673438655e71bd61998732debb040220439b36ecd46cf6ccc9c8a8fc44085733bb82427a760ab87bfee1166c7623813a", + "id": "6d8c04b24c24c9680b4e496b686c2a37cc05fcd9e6f2090cc945712199b2b8ae", + "senderId": "AYLTbdiYWHvPvJo5Lh42MEVS4Fnepg5vgc" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c134247a5d54cd30a5e3080daa99c74f54e21ad0cffa60e7efdecece862898c9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "02c134247a5d54cd30a5e3080daa99c74f54e21ad0cffa60e7efdecece862898c9" + } + }, + "signature": "304402201820565556e3039944c8c7240c567c5d8f96a8a34a05dc1d68577883a8025e94022011f55c1511627de25b4cd7410efea22ef2df50b3c1f4ed55a8ce8d4f3da91862", + "id": "e606ac6acbfd96fc4054131061a69e3d3f58e044636a6646227775c4e646a126", + "senderId": "AVi8PFHr5jFBFGWSissPFDFXUPABuBgxSa" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03afac7e796d781e17600945010be34cb6760fa919be67baec2cfb691cf4ce5f33", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "03afac7e796d781e17600945010be34cb6760fa919be67baec2cfb691cf4ce5f33" + } + }, + "signature": "3045022100bd7e5550aecbd212d9fc13842c7ef586b146ce6f289199107af979eac6b61fe402204607eb502c9c8f2363e468e22afccd754e042e605199c92a93a0c29a19ec628a", + "id": "2cd3411cf5a52c717afca1f42cf97627fe81c0869f39ab494df0ce6e99f7e485", + "senderId": "APXkAdLbLiLJDC9Ls68Y9a5ws3PcgmUDAo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032879c56cfb96539f6a8c03a91b4a987e35973a79e62f9da438831b353066a84b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "032879c56cfb96539f6a8c03a91b4a987e35973a79e62f9da438831b353066a84b" + } + }, + "signature": "3044022060dc174d40828324a18074db079ea300d382ed5c2f27a7ff782686e7e78002fb022072e809275cc872b3c4b430e3094268e0260aaf67ced17387ee17a207a9e2032e", + "id": "4b16c34911fac46716cf8ed383401f70657201763b54ecaa5904bc09fddc774d", + "senderId": "Ad8UiXMZPecuNGwCXZTmF8Mysn1TchVVTf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03bc7be5c8b1221beb63f6ed23d4b5e2748b64983087986c4953579fa7d9022a71", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "03bc7be5c8b1221beb63f6ed23d4b5e2748b64983087986c4953579fa7d9022a71" + } + }, + "signature": "3045022100c8901dbad46f6fd0ae9db39c7be5b002bc871906a80181ae45de6fa67eb4068a022068af82d46fd008c8efe5c3fe265313e01aca9262b1160a5c095c6b31d97f1579", + "id": "2d808f28b223999c1d652c01b2f22ceeca15d1abf2acb42c04570358ed176913", + "senderId": "ANUfH6C3Jjp46pDUxWgeV6WUbWCagaVxM1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c5282b639d0e8f94cfac6c0ed242d1634d8a2c93cbd76c6ed2856a9f19cf6a13", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "03c5282b639d0e8f94cfac6c0ed242d1634d8a2c93cbd76c6ed2856a9f19cf6a13" + } + }, + "signature": "3044022051a8aa7906358589161956d1c182ef9ee5c77a144c2d9965eeff043ddc3b7a6b0220797a2475b5dcd8f0ef7a24a0fbe3d0185839e33f313b93b566b82be168d4ca33", + "id": "3f21a6f499292b9bf0c2970cac7d78953a977f7004d29446698c7a29da656fda", + "senderId": "Actv8ebcwNsbfx8MM5k2AeJidJuLL7PotT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "036c474d6b22e6d7c2cf054721ca73d0e2a904135b49bbf9dcc7ed7ce9718984ed", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "036c474d6b22e6d7c2cf054721ca73d0e2a904135b49bbf9dcc7ed7ce9718984ed" + } + }, + "signature": "3045022100d3ab1ee59bd1a45bf936867661f9fa9b22c161506bcd61ad17ac871ba4a17e130220114d37a98120cd1f2465c9700089101db7748ab0562561d4e01ec98c0e96750f", + "id": "337968b8f3e7ddb26ff3e6740df0b12c7be6bc427d9d570bd77a9312baafc80c", + "senderId": "AR5z28tRUnvaBWC14KSBpvNJDgsVCNtagq" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "028bc0f094738f7699ab4432d13e3c18e09a462dca06960a1fd0eee82482f50be6", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "028bc0f094738f7699ab4432d13e3c18e09a462dca06960a1fd0eee82482f50be6" + } + }, + "signature": "304402200cd7121995da2f8fb24ef2aab15b8da97b050ff9aa600363305f2c0a0c8ca72602200099ab565497c84319dd91a34001b16a92211248652ee109fd28eb90112bf022", + "id": "061e51e451667ae7ea59e0d1cc66fe47700d6bed1dfa671efc028a2b580b71a3", + "senderId": "AbkDPAUDsfQJgvHn5g8AisDm9XqLAtFFai" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "038467e1dd4d138f007f70cb09afdd8439ea744b282cbba2d76fee8463dfd17e2b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "038467e1dd4d138f007f70cb09afdd8439ea744b282cbba2d76fee8463dfd17e2b" + } + }, + "signature": "304402206772c6c4e2f58c1401570fb76ab87bf63865fcf32c5f9a5a3f88f4360f44e346022075710eb73a4303ecf5eb899019ee59c9c5ad206b6da902adf90f3df02f108512", + "id": "ee5347ef87ceaf28aefd8374f686725b8ffbbe55042dba0c59c625e4c822fbf5", + "senderId": "AbAeJ4YJU5rxuNtXpDn3E4W5E8UNHyc26a" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "023f425c88cfa289c28e5fefe120033c9beff72487dbc4722e900d6da59943b8d0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "023f425c88cfa289c28e5fefe120033c9beff72487dbc4722e900d6da59943b8d0" + } + }, + "signature": "3045022100c185d867d976dff795ccba4654199f1b075bfd3441e045a2f6ac64c4fd0bb6d702202323d84585da2268276d6cce663e842ea5f8617e5e12cf4a20ed7436a1ff7f04", + "id": "592ee85d2922ef565fb02d649cdb2104caf06fb8747ba2425057f79518132b1c", + "senderId": "AP8agRcU4WmsYhC72pBqyfLwaDU6NYKUL6" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02d2a0fda621ac213ca51dfa8efe1772eab8659d8b8c7023a35dce48f2c88316ed", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "02d2a0fda621ac213ca51dfa8efe1772eab8659d8b8c7023a35dce48f2c88316ed" + } + }, + "signature": "3045022100da6c5c7945eb7dae41e7ed133a58e06e660fb8d012e9ba93df38b8b86a3aa7ca022007514624a32a5fd6f294bfd8d6fce6e8c75b20b7fcb8d3f8b82725c83fe43db6", + "id": "686baa6cda61403ce346c293a8f27e0dc3899eb4629d4b3bbbb9f02b41ea36bf", + "senderId": "AHmBqWJgxZfaC2azkyASdrbCxF6csE7Qxx" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "026ffd3c35c7ef08e3dcdde741994bfdf9cf6af75cdf9e6f4ef8c251b5eb321fd1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "026ffd3c35c7ef08e3dcdde741994bfdf9cf6af75cdf9e6f4ef8c251b5eb321fd1" + } + }, + "signature": "30440220192c5a3433384ea1ec3f81473f18962a179fcdd067359accac33e8ee4e571a780220549fb42c1a5d40daca2fc97aa8e0379e041c28fa524505c0e43760f1022a1369", + "id": "bf1644830edb59dea6ea1f3fa107ce84546d14904715b84bf2d5e9648ad426d1", + "senderId": "AeLV4NUMsPJW1nvHquBWPFVKCWirs1pshf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0295f1d78f3adae92f0c2d061367e0d06a3fcc9a838a725ccfe660b9c2024253fb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "0295f1d78f3adae92f0c2d061367e0d06a3fcc9a838a725ccfe660b9c2024253fb" + } + }, + "signature": "304402207720c103e2c3000d867ce3073f9c5375d9f3cbc69dc6affd08d72cdf0322dd44022040ef700ba6c235af8b2e0b71b3b8a825a18f276a192ffdabc01118e2786eb567", + "id": "aa03f6eff50359838142b70634991e93d48480369f087d73cecd0b93847233d9", + "senderId": "Ad6y8ae35QWkrtiLBpiXRR5Cj2KK2u3EGX" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ac0a9348306d3dea50bde6b38791b2716f698044c207bf005788e13f35b46693", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "02ac0a9348306d3dea50bde6b38791b2716f698044c207bf005788e13f35b46693" + } + }, + "signature": "3044022070bf433e253fa69a33abf85559d804d1d0e055199c89abef5dba34e5cccf1952022023fd278d32b75f0465c777c39b0a616a4dd0a957b08e9723c1702dbf4d6a1ff7", + "id": "c64e9e47d8c7573dd97d2bb634eb7bcb141c156ab701044319e19ac0a7f55f3c", + "senderId": "AGb36aHfdxvqbMqoDCnm6wVkKKtqkE3zAh" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c075494ad044ab8c0b2dc7ccd19f649db844a4e558e539d3ac2610c4b90a5139", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "03c075494ad044ab8c0b2dc7ccd19f649db844a4e558e539d3ac2610c4b90a5139" + } + }, + "signature": "30440220470149adad4cf95460a3e02ce0fd79d933a1612ee9d8711d104efb5c708022c5022077eb9aee6dc62bafcb4217484cb2b892d8ddac8ba2a170390438cf47521a8d3a", + "id": "1a7f292a5f3df9f9a3ec97d7abace3d398adb943ff59da7c0b0eed8f19928ec7", + "senderId": "AeLpRK8rFVtBeyBVqBtdQpWDfLzaiNujKr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02afe652f7d14a877e73f00cca6c836efda1a05e79c6fbd7f92b600a4cd519f4f3", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "02afe652f7d14a877e73f00cca6c836efda1a05e79c6fbd7f92b600a4cd519f4f3" + } + }, + "signature": "3045022100a7aa2d57b6598379d04e9143542bf9abc4fb4899aff37ea7a7be0dd07c7317cd02201284a2101aa06dd7fa6e34006b666e4916bd76071032002302dde92610e04dc7", + "id": "e8afeca5ff2aa3ccd8c29f7249d93d465049bc31b3cd3fd6cdc6e09ca92ab921", + "senderId": "AW91q3n1QYTn3caBm7KR35zexcJU7PgMtZ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "024dd37755804e6dda0ddaff6691aa038c57d8db36d59fd1695a2519835a828072", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "024dd37755804e6dda0ddaff6691aa038c57d8db36d59fd1695a2519835a828072" + } + }, + "signature": "3045022100f5a4c4d258caffc712c9aa515f3e235b352a8d2250695e7570a14337be64cc410220446bbd588e8aab1f69c900e8472c8bbb61cb6b14aecd0e0d693f74714e8511be", + "id": "e77aac7c7d14794e86175a0b0e985a09dcc485c4e1117aba251dc3e45addf4fd", + "senderId": "AW58iWw1ATvzGHu338WqMYvgUhmvMMsRjF" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02763591456b7d3ed76125e4b9d5b34c3926f5aa166200dd270e45ecb6a6b15d9a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "02763591456b7d3ed76125e4b9d5b34c3926f5aa166200dd270e45ecb6a6b15d9a" + } + }, + "signature": "3045022100f6ee4060cfdac8c8a9dbc50b49b7cb59d4a587a5d14135fc7345594ac2b7e10102205594aaafae4f78415f01e0228b7233ec36356db4d73da92f6720502874bc1672", + "id": "4164cada2725a8e52f8713f8600b3d945bb3045fc956d79066974e3a012021ec", + "senderId": "AYaYHEKaJvLLWX2NgM8VXk15zSDxV8vCgn" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "025e6a4c19af3228659e5aeb3638e78c50203d76a806d2e1ea938b79247a25a932", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "025e6a4c19af3228659e5aeb3638e78c50203d76a806d2e1ea938b79247a25a932" + } + }, + "signature": "3044022075186579fba3bb355622ca8e4e0470bc54e82ae15a16b5e1b16784b29087f70602203fabc6d8547b6eb07c59193e0b42f3bd6b5cc234ca5d415c791c9c5f4a4b7f90", + "id": "6aa2b74f026ff9cfb1e5e00a3b4a75f752f912337fdcb61ac9257d58fb406cce", + "senderId": "ARDkQVS4DbJnErrptyWSWdJD8gtaPEyRH8" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03916b256a6ff5c51bc0b7866678cdc40a2d06477fd022b61f79383b6cdf487bcf", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "03916b256a6ff5c51bc0b7866678cdc40a2d06477fd022b61f79383b6cdf487bcf" + } + }, + "signature": "304402207143545435e31741050f802e434d9559c486dcc19e136e6eb68c7881959f5f970220387f941b78ecbe29b209993bae101828daf5492cd2febf256a87d157b97af8cf", + "id": "d6805135ece218f780e604673b9946b789d4406553a5dcedee1c06575cc7c63b", + "senderId": "AJjkVwkhsvsX6dVKhVxpmhRvz4CyXLEvQ3" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02b38e56ef8fb018fe914c484ea4b0aa18f1a938f46af1c394dfca40adf2771bde", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "02b38e56ef8fb018fe914c484ea4b0aa18f1a938f46af1c394dfca40adf2771bde" + } + }, + "signature": "304402203222d23708aaba6f4904fa04ff5cd88c9c9aeb91771754763be9206d79f2d85402206ce18e2b3bccb6fbca2e024b1bb38ecd859c27986a0bc123bcc29f99b0dcc6b1", + "id": "86367f56d8b4a1bc34cf1790b3a1f81cf794b9e9683b347b7a5a5110e63b9b34", + "senderId": "AJrXd5u3y6FH5HktH2jgkLQHjgD9ZztMtk" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034bef7fed4f718a450ec4672533d74ef95039cda85a1a0ff74e6b2dbb9e220d68", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "034bef7fed4f718a450ec4672533d74ef95039cda85a1a0ff74e6b2dbb9e220d68" + } + }, + "signature": "304402202b33813e4cdc7f97dd41747d53542803cc7c5ae7aae56cfef3e4b9d85e74302102203854d993b16efbf7b21454bcf44873969f35d033ee93866fd2293841f866441a", + "id": "65303140bae99aef3d68b36379acd8b9f4263f1d90887efc1fe7f0735c26b84f", + "senderId": "AZengw5WND4WLC8JKz6xUDFwLz3yCKPpTC" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d48d4160457b1fe8d76e2da68f33860ad2520836b5ddec8af925757690d93c21", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "03d48d4160457b1fe8d76e2da68f33860ad2520836b5ddec8af925757690d93c21" + } + }, + "signature": "304402202c43ebd655af5fb0e7336fa41f03977bc5b4c05a5a6f335e544a58acb5f5ff1d022074d415ad20703c67fed90e1d0bc970ebe1d55f31a1a3dede173362c3736ba6e5", + "id": "c529cb54924f221a3aba98b204f82c033b67375a15d52e52671bb526957baa3c", + "senderId": "AaFxHxsjYyYCsZtpQwwYGvYESogJ2SHxe5" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0384e505b25eaac812f8acbaa1ecd09e2ca4b5d973210189a39dc48d2a8f908780", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "0384e505b25eaac812f8acbaa1ecd09e2ca4b5d973210189a39dc48d2a8f908780" + } + }, + "signature": "304502210099161fc12c6ea658f382dfcbd495c84961d1f8f029edfb6c2746ddca918418400220556ffdde284f10b5ba62bdf6a67139bc56f7d0e44e87c651cad6e8c2f646aefc", + "id": "bb014f5d3ba5ecdf3dba9949673e1cea6eeb3771f41bd88881aa4f055307e4d4", + "senderId": "AY5GwZtG9sFvDzMKAQfAD1Q8EHDh4bW718" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c3709dda58fcf7e26f94e865458b08d95d398446f67f465387be56b03ec36f4b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "02c3709dda58fcf7e26f94e865458b08d95d398446f67f465387be56b03ec36f4b" + } + }, + "signature": "30440220562a015669686e64c274be9823add0ce9da9bb8ea31e376c194b7e194c7306670220186b3482683572164588f2d89c8483eb6a789efaaf1945ba86b5333f23235254", + "id": "ee1933bf760161dfcce831d57e784e13f1e428bdf6ff59a0f06c778873172032", + "senderId": "AZvjdJSG5V4WnNjPaRbDNfLD72j3rYWqbs" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021c2280e462313d08fbe92bac59f43203cef4e21611d42747535ec2987a80e30b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "021c2280e462313d08fbe92bac59f43203cef4e21611d42747535ec2987a80e30b" + } + }, + "signature": "304402207e28352abc1eab6c5405cf78c2c1fe406912d3b08c6e02bf44877dd94d117eea0220144013da4977b0517f8fd8b7dc6838185e25d0f6f272af17125f50feced46242", + "id": "658f84d6568a7f48295e9f25281c6ca71c404245ece501cbbebb0c60efd08f31", + "senderId": "AKeDRRgG4TdDo4Z2iCzaBZS2UcvjWKMSrC" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0212fc419fd5f4c88f73b8475ea676f3800f4bc96433a074462d827fc35f4fd883", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "0212fc419fd5f4c88f73b8475ea676f3800f4bc96433a074462d827fc35f4fd883" + } + }, + "signature": "3044022036cc85f65b9cd55c1a336cc02c06cbecbc90a70314ed2848dbb7bb9cb7f7fad302200b217996c20eca5c5eaafcdc4d2a27a39d8945f55cd39322a0070b180e7a923d", + "id": "b8a1791640f64e4c977bc2c6f8afae412f6d125db9791db33d979fd3f607976d", + "senderId": "ANeLRbMxkSguPMq9CJeMgr8xZxwZ9mbqbx" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02015ebd34b20a1c7bcbcff1d53e7d2bbcb2b4eacee494830409e1f84117b802a1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "02015ebd34b20a1c7bcbcff1d53e7d2bbcb2b4eacee494830409e1f84117b802a1" + } + }, + "signature": "3044022023390c4ff70c6f02923b4f55ecd6ef41e93871b4e4eb21be80b5023f72f1095f02205c921d9051395cf7a4d335a00a89a9e0f0d7afaae699b371503d35eda7cd13aa", + "id": "fa9bb4c78bbcbc164ef4a630997199aa01b53bc93d49b9f3237efea13717f04b", + "senderId": "AdCa1oGLMRQqygEBGGq1AEtSV44CJ5Kbdo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030cbba2415349f8093539632fe26692411219c11e740e9a3e02b6c64415f056b4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "030cbba2415349f8093539632fe26692411219c11e740e9a3e02b6c64415f056b4" + } + }, + "signature": "3045022100c0e0115ba685768aa3aa11b9d0e1149a63824f72a486efe2dfa16df93d8f8123022023b207efa969e876be02f87ec8085da800750c127edeee1fc481fe978029802a", + "id": "4bfcd5ae4f01935aedb2fed2d509698902a054456f7dcf5fce02da02f1c7d752", + "senderId": "AXuVpX3tAewNp5YVWvXWv3etxjrMDgaZdK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03367a6969c0d62e9b0fb3439ff2574dbacd5d616cc57b08f7c5417d4ac6e94faf", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "03367a6969c0d62e9b0fb3439ff2574dbacd5d616cc57b08f7c5417d4ac6e94faf" + } + }, + "signature": "3045022100f9a6be773cea615ec0d0cf0e330f3ca1faaa86f7475d620f6ae1299ca58671d5022065ae6e660f1c80e935db4e66387fb44225ee73f671e54003c0780e435029acc3", + "id": "54f2abed7bbdedacff1decc7be229f6385561cc8f800676ea61deae358bb252e", + "senderId": "APQgLh8MaztT1XuWVKb6d4FKM9HMmdmDVB" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0315ccdbcb0ec7bf484726f95bcb2b331cd976bd7d72f0e3f5c38fe4167d7c69fe", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "0315ccdbcb0ec7bf484726f95bcb2b331cd976bd7d72f0e3f5c38fe4167d7c69fe" + } + }, + "signature": "304402205a4959cab68877017ad4d4ac61a5b7c9a7cc674ec66b21e7463a8673b8d51ab002200fbdab9ab0e4601e1fccce869be1a1b3348d8127e4febeaab68228953a81cf04", + "id": "7348d455b5dae1aebd48b3668b571f5e44c0e8f612c1377a19de8f3e27c84644", + "senderId": "AGZhXHXFUdvd7W4aWs1fYJe6XFUYeSWrd4" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03477d8e77a43b443d401aeec8b32aec5429f8f453b93d5c4061cd17101e09b077", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "03477d8e77a43b443d401aeec8b32aec5429f8f453b93d5c4061cd17101e09b077" + } + }, + "signature": "304402201a4433b86c57e2701fdbf7640b13b21634b7cbd3319aa0c914e16b59fae45bfd022044dc7bec58897ddb36fadae3c52662aa228ae88b0ee6c467a52d1e476784a30b", + "id": "6f6fa4b601053f6fcf50e734d6caf590dc4d23980c32d9f1b62e10cda3241018", + "senderId": "AVoTzMjHaaWKDuA26ysXGU681N6Q2PQre6" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "037d5887d9a9bb48e45404fc5c3149bbd53d82bb4572bf468f0da9b4e9f6c73ae7", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "037d5887d9a9bb48e45404fc5c3149bbd53d82bb4572bf468f0da9b4e9f6c73ae7" + } + }, + "signature": "3045022100c2f43505df18e7682ac36c16f6429764ad86339527e554a78ef44d0941cfc937022007118cfd9b51ea359ce4dcbbb5dcf06147fd219a38bf0eee9a220c507c5944de", + "id": "dca332856f06a5431c648f73f859b8f8286bac60d7f452eac42d7febef0fd7f2", + "senderId": "AGdHR2UZ3MJuaXWejKGoAycztyN8BFQnNG" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "026f1910d432c8ca8f04248e74c4b565a236d9851caeed4422550c3803b313bf39", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "026f1910d432c8ca8f04248e74c4b565a236d9851caeed4422550c3803b313bf39" + } + }, + "signature": "30450221009a73cbe01aba47c293914b2bd382e5715a611dc14714e225fe9287cf293fc34f02201b05843284b43452c1b779b6121be37b4459e96d6e1328f6ca4c076522f4351f", + "id": "49620d01436262018949d76325f7f7c974fe5419a32626c3ba1e7c3b2f0752e1", + "senderId": "AYTEu82arYgRyvTgi7dbYwjodV7ignYucz" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "024eda8b8e70b93f87c1e18929a2ad789aad3f6b3fe4aa12b4f74513bc45916726", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "024eda8b8e70b93f87c1e18929a2ad789aad3f6b3fe4aa12b4f74513bc45916726" + } + }, + "signature": "30440220637254d742d5038797dd38323259ba4263c3e98eefae4a8c6717faec0e866a2f022075765f0a4036afbd297bb3bd10c7ee7f62731b45dfe708a04096947af7caf106", + "id": "63acffbbad5440dec99e17dd3eb4d501f44330f0d8c4db4cf161cc0992eae5c1", + "senderId": "AWLoVgSScNNB2kecswuhbY9tcrtv9kf2iC" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02886a2ad45ba50edeffbb7447cc1baf1cdd16e3b91e5ed6ba8b16c687f03e01db", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "02886a2ad45ba50edeffbb7447cc1baf1cdd16e3b91e5ed6ba8b16c687f03e01db" + } + }, + "signature": "3045022100e78d6752036f323c61e78cefc4355a1dd7372fd782377052b5c80d30f81597c3022004b86acb40c52d91a1eea79d4de929c59a5e24fed6f243157088c1eed03f039e", + "id": "531ecd8620d51f46dc1140c8d8f0ec8344b4ebef61f17e8c304e5b3bd5e02586", + "senderId": "Aa5rKoVusA5xiyh8Git1tJUWZE48ScbCR8" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "037bd8595ad6b5787671c99921208284ac6f791a8ca55c6e49ca94a94731b6cba9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "037bd8595ad6b5787671c99921208284ac6f791a8ca55c6e49ca94a94731b6cba9" + } + }, + "signature": "3044022066203d448724540f14a916591051ed79f57e3995af8cd9bab5ef003064c08dd002202a60f251bda6088ce8d43d6289ea2ded6442e95344654f02a17488f26c10a893", + "id": "eb253b77c4560b226d8edbfbb860717289258f4cd1ef15cee6da88f80029a33b", + "senderId": "AbfnTeGFiRM3m8eHcMsrqNvrpUCoCnuSzH" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03e8eeb0e5063caf214986fa2e085dc67897908cc2501ccdeefeef33722afde50a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "03e8eeb0e5063caf214986fa2e085dc67897908cc2501ccdeefeef33722afde50a" + } + }, + "signature": "3045022100c6bcd9f75dbc9bafeb2e134028b2c4f8be77214611012f4d9b92bad5610d42c1022039923f93586e1976ae614fc90208352dac120505a08be0a2b9b82840622473a4", + "id": "5378c9044477765f44580a5f42ace617df524457315fe63ef9806e41de5e65fc", + "senderId": "ANstQM4LaxfsuKtFMd8vqdGte3CL9s2vMA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0307784cfc3d9002be47ffa32e8d99146869342254247df059452c5f92f7ae521a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "0307784cfc3d9002be47ffa32e8d99146869342254247df059452c5f92f7ae521a" + } + }, + "signature": "30450221008ad9a84421282b28c6d4cabc42a7855bb3853a02edb0524fecc21b821216a6d702203b0558b44c659d52fa163f8941a61ae0b8cec81cdd3fdbc8e91577ede6815fcc", + "id": "20d635b856a468eefc0801853afd1d387d25fdbe69c677e56b850fc45a7c3029", + "senderId": "AVFkEkCmEg7cuCXoVrfvtH6mKz6XC9XnvV" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "028dc3fd930165f910571a159f574ab15ac740f48e68429a7fbfb42ba202c64a0d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "028dc3fd930165f910571a159f574ab15ac740f48e68429a7fbfb42ba202c64a0d" + } + }, + "signature": "3045022100fdb6d70801c889e1173ae9de78a07e55bed999ef39107f2dac1e65d6bacbe89b022004557733f6fe0d96f67cf420691375a8ab23ab212365a96acb9b36aa54f8f415", + "id": "c7e1b81e3cb7f63eb947647fa572b5309b9f022d893e745baffa282f51e65dba", + "senderId": "AMG1Y3LP4kZJMAtoPhsnVkpsMTJ8nnCDr3" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03de80311e0ca23a780507fae2691b7c995cf36fb2b2d079b7c518e03302d56eff", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "03de80311e0ca23a780507fae2691b7c995cf36fb2b2d079b7c518e03302d56eff" + } + }, + "signature": "304402200bd5d223d887130a7a8b65f10e8f0a7eacda456257dc5eae4cf0f3f7068ed6e402207d6d5dd9cf0f69f592fa81bb117a83cd0e55a21b16abe048c1d4b603f295fcdc", + "id": "71debbd9fb57e0cdfcf42d9d012768a7d9ad85b4b6f32d520b424ef0e0893bc8", + "senderId": "AFxqVbsVuDfnfyd9ciRAuGq6waR5GqiC5R" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021780f82dfb6331cbcf35b9fc233cf3494e569a329b2966249c5632b0ecf53cb2", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "021780f82dfb6331cbcf35b9fc233cf3494e569a329b2966249c5632b0ecf53cb2" + } + }, + "signature": "30440220043a0359c3d68ff69877cbf2f116410bea908036f57c3aa1d51684ef9e4d1fa102202a55ae852b5e547c6733d8ea0fdf137f6525b4effda003e09289936be0f333e4", + "id": "3eee8478d002ba59f2ab94bd539c26f805005b24d8d0cdfda6f79ba576db55ce", + "senderId": "ATjjXXcGPTum6wogPVGb9pmimpSo4EDDEv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02d113acc492f613cfed6ec60fe31d0d0c1aa9787122070fb8dd76baf27f7a4766", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "02d113acc492f613cfed6ec60fe31d0d0c1aa9787122070fb8dd76baf27f7a4766" + } + }, + "signature": "304502210097b10f85338d39ca4dfda4baf9b86f97253cb06ff2c80d3d9a3e5023b2294f060220192986b55a91bf3c5d5ad1e8f7aebf69df89f022e1d8f8674ecb675faace1623", + "id": "69483bf341ab8e5ef989340da103a162b25e09ef205b53a7d2f6f8547bc7334c", + "senderId": "ARagsXvdeTHYghaQgJkwbdSkPLZ73qdMkR" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03aa98d2a27ef50e34f6882a089d0915edc0d21c2c7eedc9bf3323f8ca8c260531", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "03aa98d2a27ef50e34f6882a089d0915edc0d21c2c7eedc9bf3323f8ca8c260531" + } + }, + "signature": "304402207f3b69c5fe22ec832246ff2e0318b361849cb8fb7250d9eee96639e17d112ecd02203a4d4010360992f4162f7ffd5361884d787a07580a579da11fee4a8b8b7e78d0", + "id": "d6424b437baef1b93e967a48b29313347e6a79cd93818528c9a7515ad167b917", + "senderId": "AT9xWcPQ8hGYuXZ8aWE57VJFohyX1TTLkH" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02e83dae2b59ad7923d931f8d0ff96588b6f2b2183288c667bacf2888d5f9d80c9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "02e83dae2b59ad7923d931f8d0ff96588b6f2b2183288c667bacf2888d5f9d80c9" + } + }, + "signature": "3045022100e6d497a3905baff592921a269d78882d787a03a4c67e7818a5c71aa57c00186e02205b54519080782485a6ca918a50bdac68c0cc1723259cededb1ae9f011a88b919", + "id": "fd90a8bf3d38e0fc81020705be347205da7006d4db889f8a5c28653069d060b8", + "senderId": "AGNMmJ5upuU38ucaG3TUsG1ESaQDExSMo4" + } + ], + "height": 1, + "id": "4366553906931540162", + "blockSignature": "3045022100c442ef265f2a7fa102d61e9a180e335fd17e8e3224307dadf8ac856e569c5c5102201a34cb1302cf4e0887b45784bfbdaf5cfbc44f6d6dad638d56bafa82ec96fd45" +} diff --git a/packages/crypto/src/networks/mainnet/index.ts b/packages/crypto/src/networks/mainnet/index.ts new file mode 100644 index 0000000000..1090be92b0 --- /dev/null +++ b/packages/crypto/src/networks/mainnet/index.ts @@ -0,0 +1,6 @@ +import exceptions from "./exceptions.json"; +import genesisBlock from "./genesisBlock.json"; +import milestones from "./milestones.json"; +import network from "./network.json"; + +export const mainnet = { exceptions, genesisBlock, milestones, network }; diff --git a/packages/crypto/src/networks/mainnet/milestones.json b/packages/crypto/src/networks/mainnet/milestones.json new file mode 100644 index 0000000000..6154db5dee --- /dev/null +++ b/packages/crypto/src/networks/mainnet/milestones.json @@ -0,0 +1,38 @@ +[ + { + "height": 1, + "reward": 0, + "activeDelegates": 51, + "blocktime": 8, + "block": { + "version": 0, + "maxTransactions": 50, + "maxPayload": 2097152 + }, + "epoch": "2017-03-21T13:00:00.000Z", + "fees": { + "staticFees": { + "transfer": 10000000, + "secondSignature": 500000000, + "delegateRegistration": 2500000000, + "vote": 100000000, + "multiSignature": 500000000, + "ipfs": 0, + "timelockTransfer": 0, + "multiPayment": 0, + "delegateResignation": 0 + } + } + }, + { + "height": 75600, + "reward": 200000000 + }, + { + "height": 6600000, + "block": { + "maxTransactions": 150, + "maxPayload": 6300000 + } + } +] diff --git a/packages/crypto/src/networks/mainnet/network.json b/packages/crypto/src/networks/mainnet/network.json new file mode 100644 index 0000000000..6eb5620e77 --- /dev/null +++ b/packages/crypto/src/networks/mainnet/network.json @@ -0,0 +1,17 @@ +{ + "name": "mainnet", + "messagePrefix": "ARK message:\n", + "bip32": { + "public": 46090600, + "private": 46089520 + }, + "pubKeyHash": 23, + "nethash": "6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988", + "wif": 170, + "aip20": 0, + "client": { + "token": "ARK", + "symbol": "Ѧ", + "explorer": "https://explorer.ark.io" + } +} diff --git a/packages/crypto/src/networks/testnet/exceptions.json b/packages/crypto/src/networks/testnet/exceptions.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/packages/crypto/src/networks/testnet/exceptions.json @@ -0,0 +1 @@ +{} diff --git a/packages/crypto/src/networks/testnet/genesisBlock.json b/packages/crypto/src/networks/testnet/genesisBlock.json new file mode 100644 index 0000000000..a09a1fa3a0 --- /dev/null +++ b/packages/crypto/src/networks/testnet/genesisBlock.json @@ -0,0 +1,2210 @@ +{ + "version": 0, + "totalAmount": 12500000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + "timestamp": 0, + "numberOfTransactions": 153, + "payloadLength": 35960, + "previousBlock": null, + "generatorPublicKey": "03b47f6b6719c76bad46a302d9cff7be9b1c2b2a20602a0d880f139b5b8901f068", + "transactions": [ + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205fcb0677e06bde7aac3dc776665615f4b93ef8c3ed0fddecef9900e74fcb00f302206958a0c9868ea1b1f3d151bdfa92da1ce24de0b1fcd91933e64fb7971e92f48d", + "id": "db1aa687737858cc9199bfa336f9b1c035915c30aaee60b1e0f8afadfdb946bd", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100ffff4e9ba62e5e3beb37deee052824da83c4030925bce09f190151652d0669b8022056a432e56a2e1b026d4b54f6c34ce88a0c9cebdccc730659c03449fe878c66f8", + "id": "0762007f825f02979a883396839d6f7425d5ab18f4b8c266bebe60212c793c6d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022001a6326e5d1eb06d0ba1fa39446bd6d56ea45f0c269ebbce5dfc6a649277cfcc02203b252d3a6ef2b22349d9d0a9110ce28a199c39dc8b911edfa82c297a02009d07", + "id": "3c39aca95ad807ce19c0325e3059d7b1cf967751c6929035214a4ef320fb8154", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210084d855eddfe616cf1dc238b19226c7959c2fc4027ae2e8aea6fd8e9eb8928e6b0220440f980e40c1c56348782fd69d49a96944df7ee5b68d18028600e0e7501d4000", + "id": "9fdf6ae86f7c005b3b7dc1b9fb6411219407ecaa93adff85fdb61710f5121638", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205438b8b9058bbde5d30794e7681e400e52b5fbd22324c5b6b521f97bc8b8aabc022000fe04d7afbd2e668b1d4576988ed596dc92251e33efebc081e2cba14ad5a898", + "id": "1d7c68087c875d7ce555b2c3e71e1d91a1ad62d0c2497efe3cab91415e634041", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b2e634a95b011a68489870f003e4bac4a4f0578bfdc6b9f645c934016c2c0463022022cd4ebf276dd627d98be4b697bae2df10b86d94e984da2eb7e011b08d6dffd2", + "id": "0c993e115ba26981b0be9d22e7c4a13b0f106e0cb472f9d34eabfc8e414dd528", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100f965e5c280acb22d1cde405223fe9a6fcb765844adbc5321b17a268924e1f597022043d31b1edc5fe0cf60a960d84e3528472cdf34560c9463979043a409f37e7f29", + "id": "c279f2eb1f9e6e7d4b0ba7a98233a0f1a2536231976c99f56f64b248eb06a0c1", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30440220715463c316a75959dbfb6a59a013fbf914bef1ff739ac8000d49dabbf5118df9022019345ae1c34173dc214bae82f3cfbf438092f0fd2d277acafe3e9deb644b1a3b", + "id": "7e2fc9ecf23e909a3d0fbecd615445a0eed8c2cef18e01b1492d63f616f5d87d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100fdd8aff26dceeb5abb6e5e8a8f468c8ac1997a587225298e3d8135d57dadf4dc022072ab80a81b301a162ed5cfa67d213d5a3980185088632f5f592351aff8aa0e9c", + "id": "511c0e1076104743f98932f8e7720bdb3f1539134edadd331914fd9ece1ebede", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30440220635e04ce278870f17fcd1883aa26c568e63dfbdd302add39aa30fd3637c79c2c02206fdd9e7b1f4d238a97d26ef1758927e2d39f121687490f2bd79831e36afdd43b", + "id": "0768d5016c53d884e3d68a09d1bab0d730b7067c71ef4ca1c4d61b3815f5ff66", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200b1dac57ca6565ac31afb99686f2e0f0e8dc219b9860b295ca5444a1663cecfb02205787393561fe407449af4aaf2f621db9e4d3f11c7438666cd694d495c0a0c41f", + "id": "1aeb50080ea118165e5041f7a897974c2ed1ebde08add85dc78cc7cf73566a91", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210098dea25eccf31ce6f874a9528578805aaf07be8b41f1571865793f9e3e6e3c97022033ae9c73dad44c01fe6362665fccf63bb1a0ae8e26f77a1cf60b67dc96b05343", + "id": "254f0f4fa277cc651a746d6ac371eb27afc3ea155ba060552dd26b8e83d17b72", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207f4bf346aac501e766156818089fb16905a9bdca69ff6d5a55ba918a08afc7ab02200ec2c25cc4bb30e2c176d55630d8e2679b899c14ab4ba43c3d62955dd940425b", + "id": "e5ebb02e8e8a6708e22ee5ef99fe1dd8b6eea1095be6b772aa21bf63cf7ade5a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100a0bbc15bdad648bb9b439f1d34b12b853442d1cfd4ce7f569905082801fa58e8022036b4e73edf7ab7226f8007233f77b1d497cb6b4736f02721bf1b399312ebe114", + "id": "8a686b21477b64dfd85f08f8598a0f121ca1c7d65ccaca9e42326c75fb5f3abb", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205d77dfcde527dcc6669bcb01c27b92c1a6399e35ebac9e69415645f596ab1d2802204179497bfd952f44d5f9e295b2a3219a290a4a82841c084a18553b7712e26415", + "id": "21175347e2acfabc09a7593aae0682e39fe7152199a90561c11125f525211243", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100cf77c16df9185727ff717b71a94f8b29ceeae1e5bb3a28da8cef9df5bc63b7c202207bca394ce9ebd344a548e5a5697f672dedbef640dc1f9105f7c063287bcd1840", + "id": "ce1d9b7377551f36568127f5b635b5443f5a58abba6566b50a8d4d7b53c8a874", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100eb8daebb5484f3b0a738c9344fb28298c596f9486963f8fe36e2501ee6876f2a0220559df66986dc9a9a8e76982ef85f907c62745757990c69f0b17b6ae5a7ca4719", + "id": "b56702f5eddad0d8dbbb33b6b1ca3e07e4740def9c5dd2aaed9a70b90a4e31b7", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100d088e9bcd78978f2d67e7c7bccecfb73ddd0d1a2dad5b039390812320355722d02207affe83d815f04f6b11abf98eebe0488bfb87f8cd6513d44b829008ed1c15ceb", + "id": "a73c053c42e83a83498cf58e5b077b31443e265ddf8228081cb17a36bba366ae", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100db16a8e9682f07efb607bc7c75b654646ff449761ed146ab9358e69d29fadd7f0220436554ad78db0e04ae5b573258e2c8067848e89b55a6e8e1e25011a43882a643", + "id": "2dccb8b44ad2e598673628fd9d74e336b467a0c941d5e257dceb85c8e0a0000c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b03738eccce8ad0b8ac0a656119c2cdd202089c5650d8e1486bd13eb9c3158980220059079900c7fdc16e799c50dccc074726fbf0068044462faabdf1e73f9f9bc38", + "id": "b2cce30021d139f97925807da796722bf4d5459442523823388c259ca5ad73db", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100becb49fe5edd6806d5ba6eddbbb34ca8eaf3a12dba123d1610b2b120ca8bd017022072972992ee0ca0f319ae754a2a5a10d715a08b23f8239f9d6d59774f790543ea", + "id": "9e4841f43ab355be7a4f93b09f3d82c17065fbe25387dd6c5eb4e2692ea05b0b", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207f1a3fe8c5aa7a77a58ed35c34f128b5df6fba89aa918af35eff432be7d1f8e00220460d4f2a457e1a477974157e33bf2974de6588d56e59729ae980720e9794827a", + "id": "2c7ca823be21724a4876de632dded3b9afca45df357819ed028488128d85d29e", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022067266dfe9d8f2550b590e1eae2f73d28c6b80fecb24c3eb1b4539bc864b3b4f4022031e5122145c35874c0c48673d088e76fb3e11c308ffe9d5dee6431d3441d627e", + "id": "a91119f04e2201184761f7fdcb26e4aa81c7e1076cb11a58a422d351241d4e4a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b970ec89927de0cb7805e614a742d42c2967db5a9c68d0892956dc89d68ca7d1022067fa30265dd2e1a2985980be2bf876748a7a8c7f3cde0382265b601fa658dc17", + "id": "94955e6bac6269fbd19e92d2292ac947225fc6f68c6216001b528596a961040c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402203671b82ddf8a824b8e5aac8bc28be4aef1c00aca1097d14ec1a55003d7a3f28d02203aacb6e7517e916478432b81399828ba7425183ce0fc43feb361bcf345fb0519", + "id": "df563ee9822bd3d7aada600d4800952743ec64fafdc7697428d7a19a60745885", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b77653317c93eb20ee19c71e64a7f9ecb985351bfb1fe351ac65a5738cb37ae202203d540395e1d55f87caaaa867afbfbaf98c553be0b4c7d1748418a76b0c258c89", + "id": "d21b6341e2b4be5ffdc3dd8fbcdf2c576ba02e2ef4ab5eab0e4bfc9da4e9e442", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022046239e39062a58925099b005888355b8cd6700af66972bf509a10123f9abdec60220202321ea74e56177606fc079d19c29851d832e6d00c93985ffbec3dba6f0d675", + "id": "df6bc7a17ad34f8e9faaa2646e8e5dd8bca35affba352537184f690e200e17b6", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402204eeab87f7ecc2097b85606b986177964f3ae777535f6fc0cf08a55fec587d87602203779d59903b8de63511e4ed0a7967bd85e9cb1fc9d84bbc5091e3caa87d8bd52", + "id": "5f0d5f0dff464d0ad587da5bc93e600a8e2657d359d0a1224bdd4ccc3b6f376a", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200a2b9d0f61066fa00a2a2882379aa8ee60e949bdc2a85103bbbb69ce3eafccd9022057364f349faceb3047fa95ada210c64fc4a81978d66925b37d3dbc21ede885af", + "id": "1b39e3702576e6ad7775e34d53e43210549d52a56b3f246031e6ba4121a66bf0", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304502210099e568d3d0c1b48410e0b85c74d04234dacfb2fdf2b1d4b51fca1cfb3445347a02207a2509645aae54560762a37422b66ba4b3ee1c42de35d58c36d2f9d8fdea11b4", + "id": "0f21e53dbb1edb1cfb4c31bb675aa4672b452a03ec363a2b3300a9dda49e3be3", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022026cc5f2b588a86241badca73cd9c1686916d516b8c6c397c66a9d5bb6b5d4cd402204ab5a8c8589ee954bda4a116999d2a0e4ab0e3e96f0c7fe131d7c57b9a1ede43", + "id": "410826c255a23a78ac5c3aa10dd48132693bc955845af16c20d9c6f69b05dfe9", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205fedd8d3b5c8d69cdd7db5ca8e9e7c5004f6ba751e45eb1b85b26d9e89800a2402202be56bb2cd824bccf325b6b11432bf6d0ddb5ec97fcc121839ac2ebf884c7173", + "id": "ddb57d8270b2b6c876191c1e1c5974388b9fb3ae0980cb2245d8a7c426237f47", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022053cd42ad147eea33801b2b57388b33f633b4bfe2ad902190e12480522250d07802203066dc0d0c2ffacc4c74cca1e0187fbea1cef7e78a78666d2ec7e4e87ef546eb", + "id": "29e1aedf98935c369946c8dadb2d6784f9ab5ce8d73b9b4de2466c7757e2557b", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100c10448b87e7176735c8ddfc8fb3c4d5d55c2d71d18b7ce3ab321209ec299fd41022013517a09e4b366ab386698286ec7bb20410bdfb7f6674fab25a739259083b297", + "id": "4cf04852529b5525f22cc540790e36e61ed36045ad1b5b788f61ebe42637391e", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402204cc1588b204ebc0c20f44a31ce53d15ab5e4d1f9c103c02dd4e4eaa1c33630b40220194b6e427b6def0783461cd8d765f97b105d048942be468be2ee9b0a2785d2ac", + "id": "35c6bc3f0799d9c79efc6515f232c58be0d03a3a797d066cba879eef4afaae2c", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100be44f7ea12e2ee89245fb474643ec6c2c75afa00276826a4ecd6fca4cad5ff30022071a2c083b353a821345e4bbf74d98db0760b8721856572572cc3436ebdb8f08c", + "id": "45f75a349f3b4d73434c0f2ac9c291d5d07278b79e6eaa0d38d6e005f66c4783", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402202090f506e8f18fde70b87a3fd6c470a23e9e262f20ec6268dd59b6362e51a29202202b838c598b33c6317c998dc179fad2b660b8a72bfaf8223d7cc82414ab4c6af4", + "id": "a8d9034d1091a4dbe595647ad5f64ca8b243e7842301aee48f7eaf8b8ae98119", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100be59b689a48e198267305f1ae7e116f69f7c360857ea0b1fa81db122278cad69022033436d24ec0103674522f0c559e2357f8696bd498deccad2e0f66b2cf7469538", + "id": "061cb438ba1216cfd5a0f268ce18e6f280557bc944d9aed3655e2bc5f08bdf51", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402203b5d2aa7c4554d6d2dd6723043350df0199e6e7bbd9f21a1a20dbba8c63918cc022014a78064c5f9c5e2f43d3be36de2b5e2f17e9af557bb6c75e8d82d9f725d0188", + "id": "239f0640ddc3170a737ef349c07cb82b2493d207421b6f71b6b3dab856f16088", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022005eb29ad4cf79fd4f6898de19459e15cc816acb0975e53530a202e69c29d0d4a0220686cf6e0c14779d6d68dcb9d16358c0e859094d2eec8083598b7bb5869478bf2", + "id": "25d8eef755cfee7cab0d7f9fbbea0fad6d5f906c432d997ae8ef1c49d23735f5", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100b93096a287d59545fa3a08593dfc740d9d47f3cfa3c4bd3c8ff8ef53d3a2e957022027eda62e47220774cf799f46916195e5a8b30015c56ceff4f4a1c10a918e3675", + "id": "aac25996e3be809ee88996b6b4063e2097d6306e77a067de8ebc8d7076a28d43", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022017282aa4fac7b18e834abc3ca37b2f60cf989c26b12e2f2398a66cb907015a760220428218d39db812a22cc138acc7d5d4d2d5713f0546751c02d2c3fabecca0e724", + "id": "b040f86b75750b49c83ca7eb8f2a458f16b44789796ff306c5f942ca5f19164d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205970d53cb0921a62bbef540dc33189b2313f3574e44f046097067e6991d63b1102200a356c87642cc781df661a1fee21cce354a144463d37053280e000e1b75da7a5", + "id": "25ce96f951d7b7d886ef487331125b3413f655f9c5ee7fb4691a728c3cbce18f", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100aab0201c9d9a9641c11605d32353685cbaa051ecc276da1e6a3b309be9f20cf7022067aecbc7329bdf1770974e317a1243815511efa8c7af7801217a83c96d86eb0e", + "id": "285143b8b19cbde7c680b0f62ef51293e8f315c823ffbd97608c38c02045d831", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100dc7752f6f8acaa3a1ee2ed1bed306ee04556b3866db92a1e770c4b970c7a932e02202d137b312342f9d0708704833b26b6611d0464c87df97049ad8b616483e9d1f8", + "id": "87b06fccbb63809e976b3405cccec2eeaa3694d5510203f04c0e60bb6c2c0020", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402205ccad5c77ea339f5e3f2b7900b4b1c409d3c8204273e89b6401314fb61f0d224022026a63fef86356de64fe571ff8488a951dcacab56e980fc044ef9f43b9d37439c", + "id": "5597ed52e4123756bea9307c09c916ff9d0f9fbce8d2e9a3a2ff719a87ad0966", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402207c91153f820f34228bec62772e0d78876bd3277912eacd866fe35b5c86a316c80220104529c6f786cb387ec1e3d5826271c837f0d0a6d0fa5731b9a5c6663cce7108", + "id": "d46fde78608fcc668246cc35336210b3c167ba55c82e91b0fd99df7e36872130", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3045022100acc0cf119c18861d3683bb3b0f6e209f2d62acfdd958f86dfbd35137ada814320220448f6f8adcd46204629b45a4a06f5dc7ccb4dbc2a1d702e107d91053847adf2f", + "id": "aa92faf5d80459b4e058dc8a8212608b589925052e22148384835ab687a4e875", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "3044022055b6bbde5fa886db3cf1224a59f1fb43e850e2d9237db593368e1043698fe2c30220067dd20195e794af4152f1ff9e3ae4261698a86c54803ba1890bf176d97844d4", + "id": "432e67db0d5fc8c66376aa96c7324e5a1e6d00a415a9c8898b5e3bf25d8b083d", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245098000000000, + "fee": 0, + "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "30450221009d6f38067264df8497d6888e4a8c316ec58ceba8a54c39ccb0ce261d114fbbab02200fae3f2f950f5c5e3387679f8ca341ec70cd90d0e32a30112f03cfb12cd9fc23", + "id": "9321e1b08faa544f592ad8dc7b60ff1cf845efcd28fedf8b445be3bda60434cb", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 0, + "amount": 245100000000000, + "fee": 0, + "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", + "timestamp": 0, + "asset": {}, + "senderPublicKey": "035b63b4668ee261c16ca91443f3371e2fe349e131cb7bf5f8a3e93a3ddfdfc788", + "signature": "304402200aed5a4102bdafda00fda575294f149b393a798c510af8ba877b8c2d7ec8051e022004f7487c4f728c633aee5baa62ab0017f4b91cf2f494eb1c4cc9addc3e9155da", + "id": "0bbc9340798a18a81109bdfdbee9c9003f20a586dd9f80a39507c84588c1b4b1", + "senderId": "APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647" + } + }, + "signature": "30440220072124721ba7c997f7c29ad3d4819515fae7a67be2bc395cb73f114eb8d4abe60220523ac295e114de30ce8a4300f4670db91ad2abe1268460e6ad3463fbe9834b84", + "id": "d2e70f9d2de57240571905aa81db0b6883e27a83be2422530722d76b56e63ecd", + "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a" + } + }, + "signature": "304402204b93b06e08e71e3317f9426a1d3d450d6293fdbf5a6b3043fce27b3ce65431e20220683609720ea1d7d921238ca8b5098d3d9c0caab7b1e26efe42a6aebbc095471a", + "id": "8695bcb906f5fd81d858794f7d90447aadaa38418d312e33115a81e856b34d12", + "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd" + } + }, + "signature": "30450221009711559a43005c808113a1e9a01b1665495ff4bf30d635f7d98c752ead4cc3fc02207879e2a939914effe2b5c80cd515c4b3ff77a071b707c85c4444481878803db9", + "id": "55853d2d2a98def00c5ab842866a44d1db91678a07b6dd63d062508db28a00a5", + "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565" + } + }, + "signature": "3044022025ba51a588253524557547ec492d71bd485fe5b291e60eef681c39eaf8ee781702202bf24c3d295c7a2c9aed97a79fb835506797dcfe7e7a2853e2578e7773c7e134", + "id": "553298aadf692c9c5d0334c307dd4ac0e277a49ed165c97ce1362f8ec639ee3f", + "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb" + } + }, + "signature": "3044022041291ba10ad30fb9ebcb0e13902e92d85e2c3e98493b6d369d7d1e70e8474e31022009083444460c415eab6b4beed9e0206eb0733bad5d2a476af4db4f5b5e74b835", + "id": "90af927db7b258538c8e21116b5a31418c88ecc163628b2b65fac92a5a949b14", + "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d" + } + }, + "signature": "304402205d4111c87874e696b8f4b8897d0dfe68fabe4ad5c5769026c6ecdd04f09a1e2f02207b9c8a2a16b50164215eb1efea6d5d9f4e693cbb7eec8535e526cf8ba68bb796", + "id": "8a920ebf5255a102d0c9c5fd720e0d36a6a3539991a2267442facf1fea2d0b86", + "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd" + } + }, + "signature": "3045022100f15ff048872020d9efc561b8c837f542d54d43b9b071f7a6cc09643c6d4180f002207d0e82153a30b66f43fc4cb4b9b3093bb3d5dfd70f96928c8780c838b1448c19", + "id": "30738f376aa40fb3c8d8849a5dc698786aeb1409fa801c18729f8da624631391", + "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751" + } + }, + "signature": "3045022100babb7410d09215def98078bbab6b5e5690c2ebf54960d94527226ed3925877320220342576d1d8fd2d2fe3b6974cab48a2e16b4813f022b341b32f88e13f572bf060", + "id": "ccbe1c27eadc1b3b33f3f87f645be4f756021ee3d4c96f4f094e1f82d5728a3a", + "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374" + } + }, + "signature": "3044022032f2c350cc1319f5838d6880e91b49ae0438fb3a626ed9ab5e27ce8788e3347c02202cca18567c8491e0feea8a5f078e28605029346c509fac0c0a192e934f8c5326", + "id": "f99af0fbb4d65c2c3f2c1c558f0c0c0eac2724942802fcde02fa6da1d3a9000c", + "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17" + } + }, + "signature": "3045022100f0cb5d885ddf3bd4a58837f9b86486da4171652a5eb39228dfd0ff9d34d9c7c602202dc6e3d268d745a7e8633311a337ec097382342049672880c7c2215cf58e5da2", + "id": "2dca03aed08533585d8bc609da5deb9f17ac9be5a8352769d7ae63d0db16ff59", + "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a" + } + }, + "signature": "3045022100999f19fbdc9a12eebbb8c748a4cfc6c91b2233f333a09cddfd49dfeab6aaf38602203d8dc9d1551d400572a88ee812f51f897f8b35508713b789b2c1bf6dd0e88945", + "id": "5d7e51d57b5914ec201ab65a019ecdf651c4f267cbffe403fd2170bb95145f9d", + "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f" + } + }, + "signature": "3045022100e86e648add940a1e637e32ea9187497c281b843da09597e62d0c927d7f43235102200479f64ae63abb55e338f9ce1073a5c46907f7a2a82ea6f9bd9bc29811683515", + "id": "eaeed4133da26612c53550b6572722d8c3380d0a2344da1bd270eed1ea91fdf3", + "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904" + } + }, + "signature": "3045022100bc3b2ebc58a92bf38672206e8311e7ef0e54912abce7338155b11e7d191b0b5d0220765a568c1fa4665c0ace6b4bd3b7ba0f8329e2f25af7a3cc0d78b2ea398084c3", + "id": "bb91e78e43c59a19ac06c015d8a7ef09d7c5b274c9f98505e5a978027354b71c", + "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a" + } + }, + "signature": "3045022100aae4868ab75a33e4e77f9bf6c53b920c5e7c523a7cfe271d1afc472655f3d6a60220499f1bcb79bc0fa830dfa939898db5c9fa8571a2788c8de0da7e550bfc818bcc", + "id": "a6e687647dde9c1db68690090afc4fcf11833dd35fff3186b6b709a1e7d24260", + "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c" + } + }, + "signature": "3045022100c0cf1fc54705c13f70fde39c55a1703a4c612b8a919379cd5b1ada464c7cc8de022074ee62490a184010ad2418d3177ff2ab03d02d2589000176312b90422b1bd64b", + "id": "70262b0eec3ab5a60a736eb8a628cb600eae7522464a49791c0bf26e82318ec6", + "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc" + } + }, + "signature": "3044022045db446b109215c6d3dfb0ee5869154a8a7624376c3760eec4fadc75a29033cf022003e524d64f3ccd0c6de4ca80a7327e2c47ffd16b3ad042bd25a02f5f64500ab7", + "id": "56048c449694964bee3d367609a7bc46c8da20f66878c09c01dcc53c3abd932e", + "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a" + } + }, + "signature": "3045022100f8f69f2957781ed02d64983744c8e51fae613ebe5bbb330d4f509bdcf4fc6b6602205568ad1fd840e01ec26a24ac9a0ff093e978172da55d494138d018a45eb67893", + "id": "e15dfc4e18106480083b3c6211349fd9c803e334e9ba5eb62cca19ae3f57d8e7", + "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689" + } + }, + "signature": "3044022021eeb9e1db8915a9adb99db72972cd17fc7b5b377fc532ac2c9deffcb2707edf022068b9e08f45bbebad89295f520ad40d7786fe64059d45df95551576e3acb736d1", + "id": "2bd0f888ccdeeca24a0134e3c1bf729582d284f32ee000d97f1417f1349a6594", + "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12" + } + }, + "signature": "3044022040a9d0975f747df19792211546410d7c735aff2d26f367d1bf9233ffd1d993d702206890c66d4d0eb5de37df088c082d8fbd8da043817b48a76bd5d70f1e3f6b6529", + "id": "f75ac5ccd243e09fc9da2b3842a0654ca860d2dba5bb73866693a8a918937994", + "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95" + } + }, + "signature": "30440220550c0ab565ab2de649ca7a2aaf2975453a1e4ab8b0d392d69663c0c9b6b80b7b022039047d4d1bf4e9b167a95adcde0a5a8631aeca060dfd426da28a10d968fb3a64", + "id": "aa2ed932faf4832848356beaf87e5381ee56a1a84fb485ba975acb28f8fcf5df", + "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1" + } + }, + "signature": "3044022038df37ef25928d1a04516e982c99f49cbdc193603f814b48ab3802153bdd352002204c918915a3cbfa305c5f898ae4bcdd75394b57460f85c80daa0999751d466c08", + "id": "d30a726e1bb8d199d8f44700bc999c9a0a1a8be86e4be6a15764ecd424f9db1b", + "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d" + } + }, + "signature": "3044022028dd44b9609b0b599c15a257757fd068f9014e33947c77776a6fcbe71879271b02200b46fd8eb0827da6de13f5efd63b17f29e8ba4600e4a690ec31eb08bf2d9af33", + "id": "1410b8b5f15c05528013378251bf5da30e04c8a6b7ac0f729b527664cfbdfbc4", + "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964" + } + }, + "signature": "3044022038edfe34f7b89b4e69ea8b94e3335063b60deaee28246932147f53b2525924a402205b89f5e3d956aa49f24f81e2ba3447c19bd5c026568b3bef73a7a7d5160ad661", + "id": "58d14b74b71586e18f0499a50004ec2e0cc2e5b56aa53f4cf57084030ff90fa3", + "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5" + } + }, + "signature": "3045022100bc1e477994bf4cbcdb5cbe2bd92c7d955a03adfe562f8e3bf04d2f62965e9f78022045512772d8453314361161b2bd2a39aa0a7fbb897a5a83f4c7ab54ced615b42c", + "id": "3ee53b3f1455ef0ddb52afe08854c9d87f42c7313babd3e05bb3ca4f94c495ef", + "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f" + } + }, + "signature": "3044022052fe00e8e9f05b1d890f6910beab0627c823eb2d5875b4b9813a33aed11edfb6022034a723b827ce0e73bfdc0f535b244ffc983f8d549ee72b4d432de90d658db72e", + "id": "4a3d204c2916c93360d7bb11390e355bc1a930e3cf503965a45253d65bfe928b", + "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37" + } + }, + "signature": "3044022013b2798a4ab4d741850abac10d962360cd4ab6a47dfac7c1c806d6f9c3d810cc02202742414ad8a04ce679b445fcd040fb877bbfed3d2692b873dec8cb46c01c8c4c", + "id": "7d0c5a44a7517f6ad7a1253db45d58e85aa1c735a282a32f45d28efdb7869d7e", + "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b" + } + }, + "signature": "304402202c372b7b9679a8fe66f952a1d47d4327968d6e98770b215ada2fed6a8d87ed5502205a797fb511cfba557255dd37e028fb40981b7b65ad2ce8fe0e559a46eb274bf8", + "id": "70bfe97ae7452dc752ab4de0e2a0e81bd18bef07392c56e7a101257683d4d932", + "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0" + } + }, + "signature": "3044022058851712200f7386d6b3c188444f9c8f05788667649ec17c71b9e514206eb105022061e6a4bc4cd11599792e03298f95509893d56af54d51e9f639981045e754b974", + "id": "f6f90ff09dee5be7d8f3d58d217772df7a95865bf8609d7d5b0b673e9a5bc953", + "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d" + } + }, + "signature": "304402204878d69a166e60e0a779c31fbc48c67b70d2e4aed1d63c60beb9f070963e2894022078c46b6687f23493a4c2ed39709a183a0f7352568cc9cc2c1f0d7bf0d809a4a4", + "id": "f68809e407d20a50029fe460d411c866b79c7e09c076dada768a38d81f184aa3", + "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294" + } + }, + "signature": "3045022100d5576393a1dea704cf79a5d0bc2757a3a5e66e1055103b52157fca05fc5693ec0220522832ce0e31b779decef83ac8ce764930de927df9ae1d6f6f99a3312d99c90c", + "id": "2ec6c6f33f00431ef063fbb8a79fb90eadb13a79bf46e6e1df36dd9434314df0", + "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24" + } + }, + "signature": "3044022008a7d0bfe9c4c150566ddf701d08e84b4a5f84b07e3b1c91dde1cefa16d2a3c202200b787e898c0b2c68f4343e74f18ae7363f62b5f4ef2962386932aee09a9fa0d4", + "id": "e37b3efbf034bea4c852be7d7013978f8999eacc39549ceea775de197e14e8da", + "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28" + } + }, + "signature": "3044022023b6fbfa5f4482a4dcc34411846696052b1592786ca87243b7d3344fc9fe9954022035402fbca22691de2497552c743f0f68c7591edd1bd7954ab7639548fcd558a3", + "id": "08268f5e6c15cf146523ca928f24aca65b162f363593d927c66144ee5df297cc", + "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b" + } + }, + "signature": "3045022100b3cad169f29a3a95995b87e1b50b35583c1bff91d69cfa236f58ce452491c579022026775f4ef50b50ecf6d78b530b4633711394983456e6a45ec227b652c86e3014", + "id": "ad94ee2ae94813a638b93909930c7cc631c364b6c8528b2dcd6fa8f69260cc2d", + "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93" + } + }, + "signature": "3044022007ac9ff2f272f3fda4947393b8688586cc8b2958ff5dc7931ac8f82c697bb76802202a66c28852bbff86ef17ac7f51e7eee52e611e825d91a9846f531ab3c3115c81", + "id": "76fb1984da9ef90fd7d588756163c97e00d3e4d6e9dfe78d9e3d3cb6d71ddd38", + "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252" + } + }, + "signature": "304402204416e428688ad29928303fb2b00a26996cf79753fe70fb91c1f4635c644ba859022068ac5eab7d05f87c40ba36bd9dc149607c196778120c061698d7ab64aaade7ac", + "id": "0f442a91857061e87dd193b0b9f17a71719ca7e3da62841a63568713fc12b5e7", + "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4" + } + }, + "signature": "304402206a248caa5949024202f297c38cee18845e344c5f140be74349787097d3b0a33c02207ac84336e02592bb5e00dcd0c490d30eb856b34177ab9ac03410d82a355a7b0d", + "id": "eed30a45c350fdffc5877458f7fe29f28dc4bf81aa1a197d003c9433148b71aa", + "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca" + } + }, + "signature": "3045022100c99336ce666cb4a6db3727a61c04c14d8746365f72280d9984441b7d2b568b5402201759e4f417f683743e1d4a14f8a7a215009321cdfa29834b2dbdbe54ee22c1d9", + "id": "ecfba14a58f9d79782c4f905646df28bf566e3e7d1f17b39df6fe6b52c11de59", + "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883" + } + }, + "signature": "3044022070de7b4d4ce64bd605c9d008142544c2b113cc84df07ed1982e0adf3cf69f4520220211b01710a6533a270dc2814c7f968adf27eb6dbf437e7a72960b013b9651a0c", + "id": "36ce5323859a92f302f77f27bd08ee3485d720f55842ccba353a47ea96a964c2", + "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c" + } + }, + "signature": "3045022100a7c271633ecbf3c6641c7db36913b5fa0ea521f400a4848edf024648f3d7128002206a271f8a88644062b64d856407af9567c0b2937d4a3d89a3b3d07edbd3a0f177", + "id": "e120452e7c56a9327b2be7dfd3dcecae193f2e2e772903008b03cdf00146ebd1", + "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564" + } + }, + "signature": "304402200394b6545015bcf2d0f291de57a4197cb6ef57b2ad5fa37f05e8a220913ba83502204d0d2f2206edba54ada5b8e5afd194ba83dd1bf15f744258409595251dbe3ff0", + "id": "7d15eee8e4e3be3d2c44acd51b87a816bdb593565d4ac358dab24ae9c8a5bae2", + "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2" + } + }, + "signature": "3045022100989eb331951a13152aa03583efc765499e836c6fbafcafec4302b243ada8de5002203876fc4cf7fdeee4a095667e55a2fef84e5a7053e807b4d8e029883f0d578019", + "id": "baa686d521f95d265e7099cfd9ef14e0a9a92254dd94c16ce50c460bd013c588", + "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e" + } + }, + "signature": "304402202be177dddfad323302565a866d38a3e7939e0234b16e7dc02075cf258502eba302200928a139ec1a82b4609fcc1bd6d1d027ad050e93fcd2eff94181936d2d43e39c", + "id": "9fcf7ec6fe98ed94710e212226d8b90df7e7467d66dd4c5c9d48474388be3099", + "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9" + } + }, + "signature": "304402207b4f8c09a728acedf3b6ba0632e12d01670c683215053e49dde8598954d85a9a02202a7d7930baa17c2134b314e47dd6c334c828f78e573a2bf92fcbc1146d630541", + "id": "c35e4b1e7a2435664fc0939251c2052633ebf4b51fb22d15e71bfcab85b26de9", + "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055" + } + }, + "signature": "30440220127d27312345e015c681adb799c1a87d16fb0caaabd5020b39257d567816b91c022018b2388f6d2d9afb3714d84ed102b3ea61159772786033c855947613c7ce7b5b", + "id": "0d682a3a9c252a674043bee5240e456dae2685d76fbd3bdeda6ff50f0c442fff", + "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a" + } + }, + "signature": "304402203d0ee691830e4d001553bf4e49b6d9669b3c959376f391410551c8adc679dac902203ba6e275bf6d543efd19d20428649f802d9396bb0967114a1f09c24827be1da7", + "id": "ec2373b0d609ae72fb400ffdfbffc59670ebbf1c15f59c0ac22a4030dae700e3", + "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983" + } + }, + "signature": "3045022100f2cf77b0510f589b5aaaf2b0027ffbce6ce8d4873cdc67dc8900865d156de3be02203c22e30945618683182f3d3873e6b3657e0900b062f866bab2705cd593669e79", + "id": "3cb2f0f7d05a515d4c5c873cbe96e33b1dfba1b7718e4548de7f9da54933b652", + "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe" + } + }, + "signature": "304402201e328159172d543d2225c247c6b728800c52eb724f67c0e919f6b7215e6bd7f2022075fc02fe0b14a1499c5602d87ca2c99d6e789beaceed2b9702060dece872d14a", + "id": "2fd77e744399c9632cc8f106c39237f201dafda976f1040235359f99eea3b832", + "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e" + } + }, + "signature": "3044022063903d82e8bd15a6741a298b9a6007d0dc3626acfe2f072c3b624ccbf91ce3360220486ba4cc5591d8aa31b77dfde025b61691dbaad0feabe13e840d26e40010c5df", + "id": "5baf9e318c9e4cb0513a21eaea27e51c849f95fddc963207fb07aa2fd2b9f9d4", + "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357" + } + }, + "signature": "3045022100efc1bc16e0b646da48f84822543b62ef5253bfa98bed6613f2d6d4634076e61802200ef243f9dbac7633a8819ce45e2a85d0eacfdc9a33a92bd3a03e90cbd312b823", + "id": "b4a959ad75f81b7fdbb957c90a3a63a6c5589e7819e2c455733a3a2b4b034634", + "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80" + } + }, + "signature": "3044022012e52a479648990bfc1ed12bf901cad865708ff45962c3724ea67967be4f9d0102201901525ed8dd090af6a2637c123afb304e9fd178794addcb88d916227e66887d", + "id": "6439f2308efe31ac52ad06ef1caa45b9abf6c589118b7997da6a287325ca36e7", + "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" + }, + { + "type": 2, + "amount": 0, + "fee": 0, + "recipientId": null, + "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + "timestamp": 0, + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e" + } + }, + "signature": "3045022100a0874d1582ce210081f7ab30e7f951dfb9ce8f512d237f8a8cbd5d85569ef3b902200f0053c05de3d6e5ada4e4cf1403a836779d653573c2f374055645cc954c4c4a", + "id": "b0733072e98d3d6afe977e32f3dd118c15e79212232417743ffb551dc2a2ba55", + "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW", + "senderPublicKey": "03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357", + "timestamp": 0, + "asset": { + "votes": ["+03d7dfe44e771039334f4712fb95ad355254f674c8f5d286503199157b7bf7c357"] + }, + "signature": "30440220158ed59156e0eef2d2b94a296451dffe079be701b3d74f0443ef43bc266b334202205a2c39f57abfcd279d568608b90884b3ebe107316aa7552eca35c743b318a47c", + "id": "ea294b610e51efb3ceb4229f27bf773e87f41d21b6bb1f3bf68629ffd652c2d3", + "senderId": "AJPicaX6vmokoK3x8abBMDpi8GMPc7rLiW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7", + "senderPublicKey": "030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80", + "timestamp": 0, + "asset": { + "votes": ["+030fc33db3d3ab20d73bc6d52633a4f3cc26081ce307c89ab9fe493def7dba4b80"] + }, + "signature": "3045022100898da9f693a458a6875344c6c4cb73069c4075904c75595ffbc665967d84b07002200f168aaf3ab1b52dfa74599394387dc4cf627a447fbc5a91000e9d251cdb20c0", + "id": "3639b5dc6d19d46d8254d941bf7ace0f3da8a7cf8a56361921b260820c7239cd", + "senderId": "AHXuTrYMxsdSvYJvRoBkM3kH8pS4QSq9i7" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1", + "senderPublicKey": "032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e", + "timestamp": 0, + "asset": { + "votes": ["+032b59ba992a9b8987b48606779d92101e4b332f6fcb47a4e61e9b49f2dbb45b2e"] + }, + "signature": "3044022055ed9a8b55ccb3bd0945a710269b6f243f1dbfaa28467d3218a17565eb0c962d02207d31561478f16d93a20f5454ad565dea24e8dda4ddc464cb011f4b6b360c4e81", + "id": "fe24509580cde0c2e2f49defedd3a0f7572d2f78f90b51a253b0d8cebd74c20d", + "senderId": "AZFEPTWnn2Sn8wDZgCRF8ohwKkrmk2AZi1" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof", + "senderPublicKey": "0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055", + "timestamp": 0, + "asset": { + "votes": ["+0305322487b6dfe8abe67f680bed2df70d92379a48840dd636b32a2c142baa1055"] + }, + "signature": "30440220092f367f833d677e8d0609ad1df65f389c2c35d1501c71c245c2982e6a832268022018e67445f525613d6cb6ac0c9683bd0f55bd40d9c929165649414f083c9041f9", + "id": "6a76553db794ebf4d5f60a7d7d71cfe29f4dbcaad9610106fbc578cdc7167cd4", + "senderId": "AeenH7EKK4Fo8Ebotorr9NrVfudukkXhof" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy", + "senderPublicKey": "03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2", + "timestamp": 0, + "asset": { + "votes": ["+03807f9abe33fb390546bb5dcab075dd1136d0b98c54420c8c463c4ed3545161b2"] + }, + "signature": "304402203dc028b5013c36b03f97b111a8d7c05d0cd8e505b0b0d18747c0656c9b5cfe8102205e9ce8a78d1183b3e9880c69635d04218d94d17808bcc3f92e7af53195c23daf", + "id": "0f9d7e7708918b77afbdfffb63eef8fe87ba36e0131c88b44c1a7f81750cc025", + "senderId": "ALiMCXj25VkGEAbj5PNbNez5NagZZJxLsy" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9", + "senderPublicKey": "0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e", + "timestamp": 0, + "asset": { + "votes": ["+0282d4297584488b9c843face25f1816f95ccdd6660b1a2788fef259ed26a86e8e"] + }, + "signature": "3045022100a80ddd7c3adaf0e97ab938773fc78a716f3054d7e03afc1ddfcb5005badbd2810220231c0dabe2262149f994c939f9dc90d46b9bd7ca96b19aad6788cd3571e4f71a", + "id": "0ac77b2637fb25be42b3b60d1651bbbd788aeaba933a08ec4a417c7b4c54e087", + "senderId": "ARJWp8VJEieDA8h8YDiHq5LqU9vWcpWGG9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT", + "senderPublicKey": "02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883", + "timestamp": 0, + "asset": { + "votes": ["+02532c68cd0842fb86b2202c1027eafc741bdd581517047d9d19319e6741c54883"] + }, + "signature": "30440220772c9cd8b96f74fcddc429d57d466eca6fc40fc211845f59eeb78cb027e116c5022004cda291587eb118d622de21333d2a5783969794b5b0101ad8b1044c7d8058af", + "id": "4b0dda465564d53981c0e36d73caec888e3523633eaa80dfb99a9c81b2604c7d", + "senderId": "AMv3iLrvyvpi6d4wEfLqX8kzMxaRvxAcHT" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU", + "senderPublicKey": "0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252", + "timestamp": 0, + "asset": { + "votes": ["+0308c0d019cd9c0c59618e3b86afc584078b54a85a025c9f30a8bdc82cdc8e1252"] + }, + "signature": "30440220406d54714b6425ae4553ea8bec75f31fe52e9b1a9b6f6897151253ab7f637d3b022040a2df4b69840f4d9b0b67658c75efdae8d8269780d4cc50d055fa63922dbb9a", + "id": "c7db9d36d97ff0168d0d670ec695e1dc786dfb93f4081586870c8793b50e5f17", + "senderId": "Aa4M1zL3a74L51f1AvEsLmBTsKLKrkRScU" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK", + "senderPublicKey": "030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4", + "timestamp": 0, + "asset": { + "votes": ["+030d13c0a7f1433091a5730cfd7956175261bb9442d8c0c0beffeb3b5de32e5aa4"] + }, + "signature": "3044022018b7e51118ec83c985fa4eb3d7f0cf0655753bcbde7e82bac521665fb1c0ffaf02204e2ace460b2542db8c77e41d05d5e02fa5514b746a0a1e947256925846ed19f1", + "id": "c41f4cffcdd523f1718154d5bd5f4f0bec0376076b5f8dd340337e9edb4821ae", + "senderId": "AX1M38eZC6TB1mzz33PxZBYBGrmE2zPdFK" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW", + "senderPublicKey": "03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28", + "timestamp": 0, + "asset": { + "votes": ["+03747096ce60f19e52e99f5d80ae1ddedf6fa88be4ff0669b33f75f3fd991cff28"] + }, + "signature": "304502210088dbe249503da43c157485bfd4f2c95babfe4d0b8bbefe44afa52529b824a79e022045239b6a374fd9aca52c27171ee66b4863c956ae4085c9760d863b1902596c1a", + "id": "b1736ec6a1ea4c6d4eb278430a8ee214c88daefe296ba98530e692f8b7a7434c", + "senderId": "AJv5ZFmu6fuugsdTZNi6ukPgptbCmdW4AW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej", + "senderPublicKey": "02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d", + "timestamp": 0, + "asset": { + "votes": ["+02589ffa5363e245f8068d823af8b721b6bf9742c17cdd7925bc9a1fefe66a243d"] + }, + "signature": "3045022100fcdf750a775e728a31691a1b38908a7f990b579da510959cc2c63442f5ffde760220316ebb051d9fecb2486771dd39921fb12675b6d46b2441dd1db3c42fad0a59b0", + "id": "069271456015c2ff842771775993b8afc3404bc070572eeeb0f2fd72d58e18dc", + "senderId": "ALJVm7EYiMtc1JJDG6BupFw3ttRR6Yewej" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U", + "senderPublicKey": "0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294", + "timestamp": 0, + "asset": { + "votes": ["+0232d790f7a6ac16f2581283a47d0dcfbb51ee100f92e46cea46a63a8a043cb294"] + }, + "signature": "3044022034ce8f77ea9d0f5cf3a9135d7b72d0ba3b96ac6d7eaa3670e9956aef2c9a83cb0220626d1f269128f673a23f9993ce00ba78a08103e697298be29a4c8ee94f204e3a", + "id": "9a99bba8340e7ad4e05d8424a0977ebbde428d31ee066c9828bd06b42bb42a72", + "senderId": "ALs933qA9Cm3caRDei4ZXxnzXexpXNem8U" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2", + "senderPublicKey": "02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983", + "timestamp": 0, + "asset": { + "votes": ["+02292be0bf30b21cf57d55b20f9092a70dc1d1b71f51a91d2ccb2d2f8d8abe6983"] + }, + "signature": "3044022039ae1155f8b87a61c38b25cbbf30da6ecf6cfcc12b25c2e7fe576373754a41eb0220061a66a893129fbad5d48cdd19cf48b1a0d133dd2f3ecdc60ee7b87277e1f81d", + "id": "6c2c8926420ac269b50fa30127e0e791afb2131aff5821ca7aa80d38a0182048", + "senderId": "AJRf2oWusRmm8QEiZuwvMg3qLbMxpd7BJ2" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf", + "senderPublicKey": "02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964", + "timestamp": 0, + "asset": { + "votes": ["+02be75b3862c454887da01b866972b4fb312e0b72fa7d5dda5c0e828c1f4d7f964"] + }, + "signature": "3045022100d0dac2b7691aa059b1048d7925a0c5d5099f6e9b0f2e321e6d4f128ab1b3272b02207e8c4f643f8f9d1c3f81f0cce6a698df2da2ab71d5b01042766bbe0f46f4a775", + "id": "9259193c5de72276ed7a99f9d507dd6ea9856411fda521074fb41a556294fdf7", + "senderId": "AJQuCRxeJpzkoGSBMXtmuRMYg9mtCb4qHf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9", + "senderPublicKey": "03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5", + "timestamp": 0, + "asset": { + "votes": ["+03827628ae32074e284bcd660aec4f0504ba5d401586cb9566c887dd4da522c1d5"] + }, + "signature": "3045022100d5496fec447367ab6b53956a8c40cd8566e050ebb3b92d2c0b2a9d09bef36c7402205e32367605372375801f7b9db39aaafb46ee763b1494f0aca144fb91f3415752", + "id": "2a41e5946ab0773ca2334bba9d3510184bdd258f1c651ff8ec95b7b64a01dc2e", + "senderId": "AWdRZPxQQvG1TP6hdxvQCn2t3skerq9Ky9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW", + "senderPublicKey": "039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95", + "timestamp": 0, + "asset": { + "votes": ["+039f71d74e13cd8c4b7e134ad46e2c28f1bc8e6eacaa9839b5bf59eef5cea06f95"] + }, + "signature": "304502210099249695dc38826e04c8fcffd2570b98c43dec4788cc6a19737ed0872f17ec3302205301f645d803ad5df4ab1a700446e28c7cd76153607f6a2d68ae9168d46f3fe9", + "id": "e5c09b0fb2c24c57a4dcef0078953093800329ab4dc8e16a9d9f68215b5acd3d", + "senderId": "AcFWyJRk5sRKagThYhk5e1jdkx3wzhL5cW" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU", + "senderPublicKey": "034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a", + "timestamp": 0, + "asset": { + "votes": ["+034ff439276ab784098e66dca4075111008448a3b3519c10701bd2d1600ec1203a"] + }, + "signature": "3045022100f983b03e319aaa6c6ab6381e3ef8c0c035d6e3cc2139cedf70fd4e385393e38a0220286f73577765eb3e89e362785ad8a6de572bebf41bbc1f515b0ea93e41801eb3", + "id": "00b2c0455ef6f508d65f11bb49e3cfe1e6062d5fd153cafdfdfd2ccbf9c646e5", + "senderId": "AZeX3qaqdU8iCebAKYoLMR2QkiuG5CffgU" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr", + "senderPublicKey": "022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689", + "timestamp": 0, + "asset": { + "votes": ["+022b80e0d314928d93e48d1fe02190378384215237a5d42a86bc91580ba8c88689"] + }, + "signature": "30440220103862ec51621ca27a0ec6b2817848e8824d2d09dbf7e6aac2f45aeea5d2dc9102205e8cce78b5cd7148aa4d406dc7b491dd7758047200e10cfe1e5fde5c56107ac5", + "id": "e25439ad11cb8db3d49ccb3b8b608c1bcb24cb29b2e5ea15101cce3e475224eb", + "senderId": "AdT9FrWUksf99Lhkr9JGb8f2HLSg14kTqr" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ", + "senderPublicKey": "03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a", + "timestamp": 0, + "asset": { + "votes": ["+03a46f2547d20b47003c1c376788db5a54d67264df2ae914f70bf453b6a1fa1b3a"] + }, + "signature": "304502210099241ced4a0fd1eb02f5cdcc880ae5f48eb3c7e490d4520c20124ecbf403893602204729dc6cacf3e87c97ca57c1be54d1e80791bf31ef022135e68fc06c950f6994", + "id": "1474f50815c6c7df41ab652414806d61abe15bee0d41f32d772f4e2793badce4", + "senderId": "AGqSC7M137ctKtkAjd3J1haCEWNfayXnuJ" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX", + "senderPublicKey": "0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a", + "timestamp": 0, + "asset": { + "votes": ["+0395ff46d07f197dd4d4cb5dbb46e164c1e7ca9896c33827f9d6f8003ea167917a"] + }, + "signature": "3045022100eccf81d44992c49a5ee37c6fc2ccc4b6bee9aa44888513b3e18e79452ede3156022056b0ddf079d2918d72e8781d3af009c87e6058563591dfd6ee0117b7df5534b2", + "id": "b394e2a8b5c2d20a72ed288408b8f0d48aed922edbee6e16c1c5b0e67517214c", + "senderId": "Aa3mWTMFTXeTJukUgpeihQLBYDHBzdpWZX" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf", + "senderPublicKey": "03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f", + "timestamp": 0, + "asset": { + "votes": ["+03343930630f8235c2b3ae9ba013dbecd4d8bfc999d34bda33e18c8caf43772f1f"] + }, + "signature": "3045022100bdb87894846eccc5a5473edaee1e6dca5f3469963e22f06123b6bde195aede0e02203d0c6833e87c5e60f4597ce624d4c2502a0562b4e54d943f82a4889e3cd69532", + "id": "6a399099bac6c74fa5e956512ef8b3a39f6f946d5d6996f192c2f1dd5ba172dc", + "senderId": "AcATpmcMU1tmLDX7TzR3wXop4tfLFR21Lf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv", + "senderPublicKey": "02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751", + "timestamp": 0, + "asset": { + "votes": ["+02c1151ab35e371a333e73f72e9971cfc16782e421186cfff9325d3c3b9cf91751"] + }, + "signature": "304402200785771ccf1a6a40b51183a190d4cb4ce76b9ffd4c2c736d7724e6c667113d020220649ecfe73017d8dda96a7914793470ee7e582693e4866df123b1032194c163b1", + "id": "f20a831a6bae0a85470e308fb66517e70db479657459f6bb39f2cd1783c565e6", + "senderId": "ALHDQyTm7wALtwjmKwEejZjq7f6u6w5xCv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv", + "senderPublicKey": "0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb", + "timestamp": 0, + "asset": { + "votes": ["+0287a12b336fc781f2621aeb703ae47feca4d3ba6f30625f09ba03d225be6ee2bb"] + }, + "signature": "3044022020b79e1f07bcb17cae9485b9f44e9f583ca235da4ddd363b905fafb884347f71022015a20481b43720ddb3b1e3ca64b1f47e59b5cc2016a62f43327ca14533384dd4", + "id": "7a1285be87dca9718bece5b84266c1bf6801a39cc111d534e660aef9e6d26929", + "senderId": "ARMEiPQE55CfHfR8WmosiFykTAPGYUyYJv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg", + "senderPublicKey": "0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d", + "timestamp": 0, + "asset": { + "votes": ["+0311077c86a98b67850e7ed2c81775d094cf81c6991082ddc33fc7be5347dc765d"] + }, + "signature": "3045022100b1615d16763c46d42ca2aae967f04c1c07c119b5af7a378c262ba85515a8d35002202cf7df91676cd137943720e93f06c11907412a6bdc5ef2157cf536a203cf83a3", + "id": "76fb5a1de90f245b1eeb79cb11c7bea7c8b738add0fb8cd95191186a944b0229", + "senderId": "AcmXmomxpP8NahbbFivq32QmLuKFkTkqRg" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + "senderPublicKey": "02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a", + "timestamp": 0, + "asset": { + "votes": ["+02ad799c6bd670746892bd4331e1aebada26a2cc3ccaf0fde1e94942b20066b05a"] + }, + "signature": "3045022100e3c7b5d6a72acde4d22e8c1c6cd864c549deba89683f4b84320407d6c380827c02202da57df0ab7cd381b776bdf85802aed371e7cea7269a84f911b1d8e9956badee", + "id": "8da75c8100e6248ab37cc92f72ed9facec3067f4f82f03db8bb8063791463fb3", + "senderId": "AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t", + "senderPublicKey": "03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe", + "timestamp": 0, + "asset": { + "votes": ["+03ba0fa7dd4760a15e46bc762ac39fc8cfb7022bdfef31d1fd73428404796c23fe"] + }, + "signature": "304402205779b5d8acbfedfc105fedb6fcbd4636713ed27605faa9bd988598072640a958022042d8a8b3d7910c7c385f3707a317c5d445d56da250f8d127c71df2d9d4c5d86e", + "id": "fd26e265be88289828d0ce7ffc5faeb9849e1f4cb37a8f1dd5d6fcc436d910b7", + "senderId": "AU8hpb5QKJXBx6QhAzy3CJJR69pPfdvp5t" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf", + "senderPublicKey": "034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e", + "timestamp": 0, + "asset": { + "votes": ["+034985f6f2167cc8c9df1204aaf6744bc97c0d7f3c07c43ee6c0978bc91b6c680e"] + }, + "signature": "3045022100e18a89fe1fe0a8acaca2b6461314e784ffebbe7374f6aafdb06934e83985ccbf022027314b21a4a25b477bd7cc070b4e00ef8f3d69f3f1af028b96571dc245924c00", + "id": "41d92e128e6b8367cbf8fd111e5263d52e1abad553653f975dd60d7f7c5b637b", + "senderId": "AQBo4exLwyapRiDoDteh1fF2ctWWdxofSf" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9", + "senderPublicKey": "02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9", + "timestamp": 0, + "asset": { + "votes": ["+02dcb87d64ee2fdc6c2bcecdd841ad8e3b3163599214a818924fd433a8ffe7daf9"] + }, + "signature": "304402201c614c84dbae26f87973c9e2b38df883fe0c8c469080e31fe32a4c4946d50b67022075b8fb498fb1384aa6be785845da02813185ccf095597b5782618033828af4d5", + "id": "1e4a1f8aab6fbf8682c2b35e0d04e9e007ae717ce3f4a82894747e5807e3c759", + "senderId": "AWHGbGaz5FgvyChuAfWFmKY2LsbcwqPYL9" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV", + "senderPublicKey": "02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca", + "timestamp": 0, + "asset": { + "votes": ["+02c39e352d0f3c4ea19842a5bca3114b4247cd56da72157963a5873ecfcd824aca"] + }, + "signature": "3045022100b1ee6becc59d594776a40e5b3caec82390d273b703ecb0d7caece44953141449022016543cc29a28882845118afab6e51296cd216bc662260c28e5efd9597b6025b1", + "id": "2ce068bfccb3f967f4004e9a1e81614a738e55e45c80114c0af30a085f71a2e9", + "senderId": "AeooqGMJPE5UWRPkKW6kgLGZu3898vcLPV" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ", + "senderPublicKey": "022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c", + "timestamp": 0, + "asset": { + "votes": ["+022ebb110e9630377073d4f0e32897a5928a2c71f2941fb6d4b71251dbd62da98c"] + }, + "signature": "3044022036698a329d7f5f751f91ce02bc188a7527a377d01583b70427cfce64def945ec022079afafea10aa32394a1e42a80577de3869856656221d5f259e05fb44f01668b8", + "id": "3478d1ad3655e10fcc864f191972322c866616866bb1dbf66d7b66b31cd95de6", + "senderId": "AWtD9W5LCv2TH3VcdzbGQBGaJBwvbzZNDQ" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA", + "senderPublicKey": "03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24", + "timestamp": 0, + "asset": { + "votes": ["+03d27324bd4829f57d549bdb273bfd666d88f43d8429ba9a42a4fa1c9bd1032a24"] + }, + "signature": "3044022035fa7be80cf881eefefc12b11de04ffb2e2e92815cf05074afef54a3c5b2eccb022041f3347f59db0b3caadefcbfbc5ae275d3fe3e2a52fe1504b23628d4b79a43bf", + "id": "8adfd8e73e96188ed9fdec459d88db1fb041a2b25b3f64830476aec661ae5010", + "senderId": "AH39inbLsUBhC3k29NcvQP3zKZWnsQksvA" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm", + "senderPublicKey": "021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f", + "timestamp": 0, + "asset": { + "votes": ["+021770413ad01c60b94e1d3ed44c00e0145fe7897e40f5f6265e220f4e65cf427f"] + }, + "signature": "30440220630da8a73979bd3988b7f84fe9e83a429cf3239f54c140c3dbcc407140513fc002203664ad54ed9f199f2683479b988bd97ad8fffb2c2d5dfdbdb10858aca4abfaca", + "id": "e306328ffefcd9e3809e7390a358199a62cf8ef037d57af1f5c7b54d728d427e", + "senderId": "ANRNMPjQjJGVsVbyeqwShcxKTidYJ2S1Hm" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1", + "senderPublicKey": "02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b", + "timestamp": 0, + "asset": { + "votes": ["+02af50fcb5183b3f2c468fb4e75e573a6bf0a048a6fab095df6d70f9f91fd6651b"] + }, + "signature": "304402206f1df93f299ffedacc25aa201807df47d32c43369315cf9db280963c357be56302206a66acd553710f49bbb7b803a2bcb71128c8e617ffce66b37b7c968817349247", + "id": "dc69bc8f78502ba34655ed062987788939189709a4112760cd8807245d7461f5", + "senderId": "ASqzCDRS5cTBwCmC5moQ34W4QZhtrj4pT1" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8", + "senderPublicKey": "03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12", + "timestamp": 0, + "asset": { + "votes": ["+03c74d53dcfef0d79f249a812e95c1a58040b769867df036639f0c107d3b577b12"] + }, + "signature": "30440220629e696a10e04d4fbc10a5ac443bf9bd40dd5d89d4b214224abe47d7ab5600340220643f361a24d9916e2c5aaec7bd7d8a6a0d3ffc5fc0b62c3ac4906eb799a862fa", + "id": "c3f49fb80c40f7779b32ba23616f5573a6ba58fc60c4629c2252933038dd89f0", + "senderId": "AHysG9CfbXvHtxev9eziTK8WUbnFKKLFR8" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2", + "senderPublicKey": "0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904", + "timestamp": 0, + "asset": { + "votes": ["+0345ef2a1e4f64707044ba600efdc72aaad281c5a73195f930527c54d7cc891904"] + }, + "signature": "30440220660f9604896dad2a97820b0d7524f0bce5a8b5766f150517d5061fd02bddf768022055e87c25891d4480e66e5d1a71e42cd5a4bef3ab2b2651cd72d44f30a4b32309", + "id": "8e8ac1b1a586e86867abbf25d63387bb6dfb793c691f0b06333c1581a9a568b3", + "senderId": "AZuvQC5WuVpPE9jwMCJcA28X5e7Ni32WY2" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT", + "senderPublicKey": "034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c", + "timestamp": 0, + "asset": { + "votes": ["+034776bd6080a504b0f84f8d66b16af292dc253aa5f4be8b807746a82aa383bd3c"] + }, + "signature": "304402202e2ad64129f61ef1156c4c7e80ab862d4823d62dac502685f53028536ddfb41a02201a3ec777fdfe8fae9f7cd5251fac322c1b6a2a4d41b3ec456daed474986d4872", + "id": "ff73565c373f2cefebf86c72dda3a6a6205750eb03b69178cb83378620715e1d", + "senderId": "AMfyf9iRjXiKNcLQVTUE9oCESUPzmQ6iUT" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + "senderPublicKey": "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", + "timestamp": 0, + "asset": { + "votes": ["+02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd"] + }, + "signature": "304402202e5c78cf21a088db10e1e1f64d98d84c8d3294fde7bc322d4af06bfe99d4c2e302207e7912a16a37b641a9f8c7c722f2b0d699917ca73e4d0f21584b717fb7f02f13", + "id": "3822273b496f2e253081cedf382e4f9937713fabb83449e1f892377cf536e68a", + "senderId": "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "senderPublicKey": "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", + "timestamp": 0, + "asset": { + "votes": ["+0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647"] + }, + "signature": "3045022100a65ce45164c9bc3e018e26703370c9deb2933ee3b4e814619043cc37c4a39c4802205ae4931ac9e8dffd714c3b601fe248a49c0185c8367887205f497d951c52eb54", + "id": "430d6db0b87c25dce4ce14ac907c13bcc6efa5d95135f05aa4ba7596ea9d400c", + "senderId": "AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN", + "senderPublicKey": "036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd", + "timestamp": 0, + "asset": { + "votes": ["+036f612457adc81041662e664ca4ae64f844b412065f2b7d2f9f7d305e59c908cd"] + }, + "signature": "3045022100f3cdd7f688ad2d7b6a5b9cc7e793cb8a6e6e07d3327bc67add64691a53fd2911022026ae1adc8f4fcfc01bcca3efc83019026755b443a504265ad1f46f69d1f5951c", + "id": "dda86ecc0332e6c4eed1c0a5af7424374089b85dd274a300fed51b86e2655587", + "senderId": "AW8n3yvSAqUJkyfcG5u3bgRxsNKzXYPamN" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj", + "senderPublicKey": "03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564", + "timestamp": 0, + "asset": { + "votes": ["+03691178f8610d0a295e650201b62345056c788d7f9ac7e8570b69c6c90091b564"] + }, + "signature": "3045022100d419072a752acd55792257c96099fb14c56c29112a00535d39bca96fbd7951c902201abdf4db247dc956d79f4543c389823fbd1a9337f95d30df39603a3b52486bfb", + "id": "0998e9a055c53bf6697ee76af94c7a830c1364016d78fce889a21bc38ed70cd5", + "senderId": "AdWRsk7Lbo97jxGBKzLAFwevVHbqVbW1Cj" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN", + "senderPublicKey": "022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0", + "timestamp": 0, + "asset": { + "votes": ["+022f2978d57f95c021b9d4bf082b482738ce392bcf6bc213710e7a21504cfeb5a0"] + }, + "signature": "3045022100ba1e0ab761326d2a53cbda2a4a5135033c94d8166864d2ad3ceb963b4a0c046402207d755ecf4ada9fa2a598fd75e73a59d30cb83e01f510020b48b6bf162dc60b27", + "id": "be13743deb8486a575d1fb564d2b07d797ac77148d35793c9aca43c0d47aad61", + "senderId": "AV6GP5qhhsZG6MHb4gShy22doUnVjEKHcN" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA", + "senderPublicKey": "03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b", + "timestamp": 0, + "asset": { + "votes": ["+03e59140fde881ac437ec3dc3e372bf25f7c19f0b471a5b35cc30f783e8a7b811b"] + }, + "signature": "3044022038a491e2e13ac32025209d00aec1af31b73a8b6ee77ad9b8bb80a34f5df59dfc02200ce82c89fe9f88bd5af236ceeaa80f9954e3fb4af7bc884c447505751d49c134", + "id": "f1d3d44cc289837de9623cba8891a1ed1cde8918473a91e2daead29975afad22", + "senderId": "AaUgne8txmQB1iBboiFVLVHwLaYChZiFVA" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx", + "senderPublicKey": "0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc", + "timestamp": 0, + "asset": { + "votes": ["+0363550e2a3fe2153526effd4302045fa2c3027d0d9b30b19821a4870c8cb134bc"] + }, + "signature": "304402202ae599ce389cd030b8ab48ef53113458b9ba8bf9c9ed09c662eba2849bf540f802202ed63f8af492dd0b67d1b451170a989418a42466a3a7ffe89c4c5a18337e8fb9", + "id": "65ab302a44ea7550891eabc3b4a8d5ecbcb80784c4666195d5d0b7e33394300d", + "senderId": "Ac9dCo9dFgAkkBdEBsoRAN4Mm6xMsgYdZx" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK", + "senderPublicKey": "026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565", + "timestamp": 0, + "asset": { + "votes": ["+026c598170201caf0357f202ff14f365a3b09322071e347873869f58d776bfc565"] + }, + "signature": "304502210088a3a4e82d307c238e01ce154b57631d4429e0b591e828ec36839a783736e842022042c6e1d719781e2edca3dbfe84ad13b9e490821a47ccadfcff379decb9c873c0", + "id": "d26a7ea56f398634a81086bb15c2f0c863c71b8bd728304d324d8245a8fb6c73", + "senderId": "AdXbS4GKvV6TZVHrNzcYSQKfpenQnFGTxK" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz", + "senderPublicKey": "032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374", + "timestamp": 0, + "asset": { + "votes": ["+032c51f895ccdafae44e68baf283c50605d3f7dbba1c48011c6577383791f4a374"] + }, + "signature": "3045022100ae5805541f085a50076835422b2581d3b7a128a05b4f068ad7e3c14cd02799b802205f4bb40e06f90e02282ae74c0aba97923e601fd78234b9585468c4fb73f47893", + "id": "02504eae7ff4963c081219523bc48d7a07de4c29fdc1622224547f9a7c133abf", + "senderId": "AReCSCQRssLGF4XyhTjxhQm6mBFAWTaDTz" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp", + "senderPublicKey": "03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93", + "timestamp": 0, + "asset": { + "votes": ["+03832487ab0aa9450a4c223999bf4311b7b65c50c06baa90d19d4f65c27bfeeb93"] + }, + "signature": "3044022078d38cabd8f427ef381d0aa6a0b98c6a590cb18f47acc1d80b429a1c1959b0ab022022a70d4d93d650ca3121dde6065e80cd90d1e2e91cb90f0d0b2eadde609e0d75", + "id": "addb8c1baa833baa52a5b51d8a86f8524bde826b5c9f0a99e57070e6323e1dfc", + "senderId": "ASEJEDLfTxy6upQDWTuYucoVwMUcmhSGhp" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv", + "senderPublicKey": "038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17", + "timestamp": 0, + "asset": { + "votes": ["+038082dad560a22ea003022015e3136b21ef1ffd9f2fd50049026cbe8e2258ca17"] + }, + "signature": "3044022076dd065e3fba825b77884a179d0231d7fc9e7d3a02e34bc6565fab81a84e559e02200a880c028e690a9d6f2c4c6576b1bf3e913817c834da8ec6afdbadfae78d341d", + "id": "72f31f9a829b93045ef2e860b24c33b9be6a2621c26914acd42121215c1d517e", + "senderId": "ARAibxGqLQJTo1bWMJfu5fCc88rdWWjqgv" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P", + "senderPublicKey": "030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1", + "timestamp": 0, + "asset": { + "votes": ["+030a9321ff83e384aef559e6030008c23a137e3b3c5d45028e46cccbaafce772b1"] + }, + "signature": "304402205261d9d8ded6364fda8b10bd477982be84990cb010f9214d52c492676814e1f40220489f441ffe2478d361a12ab96caa59da495fe62d61d0e2255aa5ec4ed789afb8", + "id": "1f17b4ba072d205761ed3f786491eaf684ed3601b69082e487e568aa74a319e8", + "senderId": "APRiwbs17FdbaF8DYU9js2jChRehQc2e6P" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd", + "senderPublicKey": "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d", + "timestamp": 0, + "asset": { + "votes": ["+02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d"] + }, + "signature": "3044022040219da41054a3eebd3122df7f09a62a4e8b4fdc287ae77221f2217b42f291ad02202b9a70c54bb546a604eafadcc086ef6b6570f57542374d87de02ad7f61fe51a4", + "id": "5fa837023159d6a3d6cf7c5b2ed6fe05ff7df19300226b2f0be5a48a06993780", + "senderId": "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo", + "senderPublicKey": "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37", + "timestamp": 0, + "asset": { + "votes": ["+03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37"] + }, + "signature": "3045022100ded426768f114f459485ba6ae293c9649b340cf2dcb15e8e887fbb5fed6f7e0b0220752297022de6e93ff64bb9e07b4efef8e946cd2872f84d9e1cb3165ff5c342cb", + "id": "0a16dc31514629a36d7237968ada6a95d6cbec027b7d26e1e0f0d7d4febe9494", + "senderId": "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo" + }, + { + "type": 3, + "amount": 0, + "fee": 0, + "recipientId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD", + "senderPublicKey": "02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a", + "timestamp": 0, + "asset": { + "votes": ["+02275d8577a0ec2b75fc8683282d53c5db76ebc54514a80c2854e419b793ea259a"] + }, + "signature": "304402203aa292e7aedcd62bb5a79c2521b666b8e1886b57923d98f51911b0461cfdb5db0220539657d5c1dcb78c2c86376da87cc0db428e03c53da3f4f64ebe7115998f00b6", + "id": "8816f8d8c257ea0c951deba911266394b0f2614df023f8b4ffd9da43d36efd9d", + "senderId": "AJjv7WztjJNYHrLAeveG5NgHWp6699ZJwD" + } + ], + "height": 1, + "id": "17184958558311101492", + "blockSignature": "304402202fe5de5697fa25d3d3c0cb24617ac02ddfb1c915ee9194a89f8392f948c6076402200d07c5244642fe36afa53fb2d048735f1adfa623e8fa4760487e5f72e17d253b" +} diff --git a/packages/crypto/src/networks/testnet/index.ts b/packages/crypto/src/networks/testnet/index.ts new file mode 100644 index 0000000000..29ff36300e --- /dev/null +++ b/packages/crypto/src/networks/testnet/index.ts @@ -0,0 +1,6 @@ +import exceptions from "./exceptions.json"; +import genesisBlock from "./genesisBlock.json"; +import milestones from "./milestones.json"; +import network from "./network.json"; + +export const testnet = { exceptions, genesisBlock, milestones, network }; diff --git a/packages/crypto/src/networks/testnet/milestones.json b/packages/crypto/src/networks/testnet/milestones.json new file mode 100644 index 0000000000..51c32313cf --- /dev/null +++ b/packages/crypto/src/networks/testnet/milestones.json @@ -0,0 +1,31 @@ +[ + { + "height": 1, + "reward": 0, + "activeDelegates": 51, + "blocktime": 8, + "block": { + "version": 0, + "maxTransactions": 150, + "maxPayload": 2097152 + }, + "epoch": "2017-03-21T13:00:00.000Z", + "fees": { + "staticFees": { + "transfer": 10000000, + "secondSignature": 500000000, + "delegateRegistration": 2500000000, + "vote": 100000000, + "multiSignature": 500000000, + "ipfs": 0, + "timelockTransfer": 0, + "multiPayment": 0, + "delegateResignation": 0 + } + } + }, + { + "height": 75600, + "reward": 200000000 + } +] diff --git a/packages/crypto/src/networks/testnet/network.json b/packages/crypto/src/networks/testnet/network.json new file mode 100644 index 0000000000..0f373e462e --- /dev/null +++ b/packages/crypto/src/networks/testnet/network.json @@ -0,0 +1,17 @@ +{ + "name": "testnet", + "messagePrefix": "TEST message:\n", + "bip32": { + "public": 70617039, + "private": 70615956 + }, + "pubKeyHash": 23, + "nethash": "d9acd04bde4234a81addb8482333b4ac906bed7be5a9970ce8ada428bd083192", + "wif": 186, + "aip20": 0, + "client": { + "token": "TARK", + "symbol": "TѦ", + "explorer": "http://texplorer.ark.io" + } +} diff --git a/packages/crypto/src/networks/unitnet/exceptions.json b/packages/crypto/src/networks/unitnet/exceptions.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/packages/crypto/src/networks/unitnet/exceptions.json @@ -0,0 +1 @@ +{} diff --git a/packages/crypto/src/networks/unitnet/genesisBlock.json b/packages/crypto/src/networks/unitnet/genesisBlock.json new file mode 100644 index 0000000000..07ad32e36f --- /dev/null +++ b/packages/crypto/src/networks/unitnet/genesisBlock.json @@ -0,0 +1,3944 @@ +{ + "version": 0, + "totalAmount": 153000000000000, + "totalFee": 0, + "reward": 0, + "payloadHash": "a63b5a3858afbca23edefac885be74d59f1a26985548a4082f4f479e74fcc348", + "timestamp": 0, + "numberOfTransactions": 255, + "payloadLength": 55608, + "previousBlock": null, + "generatorPublicKey": "03d04acca0ad922998d258438cc453ce50222b0e761ae9a499ead6a11f3a44b70b", + "transactions": [ + { + "id": "0b127468138499138c9498d356975c2aac194f5a6963a59d025d1e46fc29241a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANZ4tvGqebEWKtXiHgzFVCsv6KEiEGNupr", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204b965ee6b4d07758f4e792c4a51ebf922b7b3a88068d8c47233111f5273bb8a602204e23b4dfa9dbfee74f490a33eaaef08187ca5f59e182f6d7271db9fe47761a99", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "dabb071285c1f29d0f1e04a60ae3ef13a4189f46f4330bf85cf5a0f7cfaa0f09", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbxSmnyBVzx3FGMjaFkE5tdXMZZvvuH8wd", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206c48cdf1a1b580e277c938c23106329e3638d1dfe0e6e8d68afa371bc892b74c02202d55a54e47a49c2afc167d926b3724ac22349cc79925152ea932361446af2506", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "21f63109fd69efa99583249e1d8fe0fd794997186a0f8db3904459c2fc49a03c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQoAJwxR9AzoaJfJWiuJ1S6mvLHfKtY5BJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022042387fd0c622e648451ec03f7ce461cbe3b5b29d8e151492be6c6cb25b71e39c02207dc9bb5a45ff4d65923e603e7e13020f4e0a738c4892c1a2d36c45432efcc100", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "13ba85ffe79a702c667bcdbb0e01da654ea5c572180e2ef5a22b9abdf27bef7f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWoysqF1xm1LXYLQvmRDpfVNKzzaLVwPVM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100fb4459a93cc115d9ad481c51d91c338496d583321fcbbcde86a4024ae0591f5f0220631339e3dbc001686db05dd7fb6bd7f2d043f6c83a0901d68699e0cd320a1643", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5aed9f79fe9addf50ee78b3602963177d251e8db0c815f167e31d3f970610045", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbsErLb34KfWNLV7ChC3QheZpqgxdp4Fxv", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022068925d76e16bd56c154bec93ff537bdfe50ba80dcbb3dd061873263d43080aef022069dfb2b36fa88fedc146c24f596f343c84dd7c0a10e3aa8a8722d6f32f95d2da", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "be26cbdc41a2dfdbec7224b3b1ab5756532582fb4091552d9d1a7f351adf5512", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALvY3DscFkU4H5EXSRe59wpE2CHuHRSaqo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100baa4d9be7e79b972fe59ea61336275503620ea59c619ee04022a8606280887ec02204a960f40fbb570b58c3777fc1794d19c8099a990284d3d842ce4bae5749a2e62", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8f693d75d3a9a4d637e260feb73a0f3642a9a61dc983f52972e9965693f2efb4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALCLA26axFSNRsWHnECjAggxbkSEnU2ncB", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202b19f2266ed6c2319cb839bdc7d6eadaee9b3edebcdc8ce20afa16f556123fff02202bec70495af64a3f5064407b0fced6b5b800008af4cdad88bf6d8608c8c875c7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1238a1a698374e5eb30a94fc2c4c5255b6a3376650590ca79d6e6fb218e5ae07", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdvTKR9AT1MSALnaiAmvRBukvomZDj86gx", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202ba92ed764c96f7cf64e921b040b7be00782eccd18eeb352bdbc251e57b1616202201ef137e8529403f5a1459c86895cfccedbbb0faba50458319f7151751539a607", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f1e5dc5b8939ba185b44ca246e1919983718b276822a8779728ebbdf76437d04", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZz7W8kf7FZPaHcTT4CHgUiiTUnKK8hxEg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210096198013bdfc0de6adba0ba6034690e20c6ff9bb11b60c2b00cfa6bfdda7d909022072cb6ac164af87160cf81bb756d19a3d162261a2e3063b47bd26a407247e3ccd", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c27f4ee03afeb34d2fdb6d66848ce36744339515c8251c03d43d0e89a9615b87", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AV5DuSRJ7o37refVkVcFNkpCbYbsQQFfV1", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220634d0d5baf7cd36b4934172a0e944c53481df8fc11caf31932c1f8003b3d5b2002207aaaf8a0e260de26b1b786e654458e9665f4085935767600853a81e12e51d920", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fd4b0ae6b226aa21d03e8af8ca238377a85668868fe4f6dd4ec89680323250a0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQVGV9A42N9rBhgvMd7FDzRW4A7nzwzqMr", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201227ac283b17d6a7fa27bd3eef91f403c14565a29975c7c9626cc6580db4003202205f09749d61c4c2ec7bb517fc17aee2950d81564795357a432ca31144ef2b1aa7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "56747a89a8ef40e51a956ee432f2b4a2ec75a5fcd1df6f235e9001ddc59faf78", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANTzfMjau4R5cDdYZrbdbgkeXgYaAny7DP", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220546000ca4960ee70a3914a4425ce6cfbd22f7234354fe418bd1e32dee034d9bb02205eeb945c1a9e159a4001bb3543a835e4749e0c937da852c4775d46bef93c0150", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5884828e08e904645dae532544c347fabcdc01465ce59a2a926fa6f7ba960092", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKA3HJAnRgJf6pRtuR8zyUXXkAygiY1gYE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a34ce1541baa69ffc48d4d60dad3080b9cf80711597453c31468f96795c72e2f022061cf7fcf64d86735d7ab9967be6e447f82678ed720e00157ec6f0b1bde95c2ec", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "6ed0ee7e2cc203ec731e0f53e8d6b4b806dfcf451041cf648274ecf51b3163e0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Af2hTgerw9M8GHuWLC7PjJQetsNKy96XG9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e0f5be9cd1a2a1df0755d12358668ca303d4485a51b27a445622aae8b1c135bc0220144a1c224c575e908a68cae09706083b7ce079627d6fad464d053c34630ad58e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "92884f8bcb1564bd547ce84e5ff278c4dce197b8640078f754151bd8bb84b97d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdHgri2tVkUto6CWb8pgsxyW9ouSteJULN", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022043134d6373c8c6d052aa76c9d85b587e40f66dfa7db2bd0b5d3aaf41410f45c6022025ec2034b8da1c70a208460213efa59bbcc138b58a124860d93f1128ac1d3e95", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "69a7444676192bef05ee1e54f47cbb240690454effa660c6d1572242998d7bd4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFnRoALAnBAd2mZaZYGFT9bBr2Y7re8gCX", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022015ab48a2f57569412cfca817a20a22c98ba5581e5f39c85d06b77ef01322785e02203d95cbd6a11d1ac7978d13ba0bc61c75ee4d6f455cd57d11ec80d9f96ad88862", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a352d1b1aea8ca0f85d68c200f5c89af56657f9c4efae45d1e36275ef8b28b6a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALx5FYHiVKrUEogvVAaBHBWtKdrpY5u6Ur", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a6fd4821f57c09770f2fac5ea28d9301de531ae3d7f544335c8e603fc710ab33022007749a31e896285ab4f601800b7fe0e8e67ee3ef91b3bab0504fdafe9c8f3af5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a56af285362aee68b103b6378f7a69fbf755a90ac3a82b0a0338e25b5fd97809", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGYtkWXBUD3ohy7a8Yow2sqEuXZpmUWMA2", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220096ebcd8c8f202a79e8cd5d290449bf99f4bf2e79288d7c5981fef4bde5900df02200caf2765c08e80f7b328a0938118e95addeef950f32ac6ae66d03f351ba8a972", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5f6763e747c5ad03fe7688ba5b993f4588d811893a0a8b231317c67b5efc1325", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANpBNG2abwurGKggb8MhbiLYaoSqujaUfB", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220704878223a2d7e5dd8bf06ee62ad39368c3f61b4eb39cf065ee97cdc92b7185f02207bb4a2bae5c95964586fa6bc3141c44826f28b4c553e62172ee586ca32563161", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e7e958a1767ea6d2ec0fdd1ab11d38085027020e44de2322f6e0a831d7e44b2f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbN7uVxpy8SPWQeCtF8dJWbHghUDU85NVb", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b340f8880716565eaace21015e259349b0a4207b62d5eb0f60853f5bdc7f2cf6022028d7e45d4b06c390c014a8a154a2b63c4c16a8b79a99f9aa10fc0747712b3e59", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ce21f02fe0870d429a23e1bfc44a45d288ee3c74e405af5df4709028848b7716", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGuv1r9zRCLhe2Tk2K5r8miaEq4gKvUhwp", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220787b6da60cb2ccdde0ac18f8fbdaea8cf41160f02a2597d71113ba2bfedbea9f02207b074a2147bd9e5032c32654360dc70927fbe9563349c3caeb86728885d0aa7e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "0a12fc0b242e630f3587ca32063d85885051b66c1afb6a2a9f4af8c084e7908a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASt5oBHKDW8AeJe2Ybc1RucMLS7mRCiuRe", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022022c6980430bdbbdcb264da73b1c5115b192c7506f2909d17c3b2b1faf545dc1d02200ac1a5d496279e0d2cb64a2ae6879446e8788bc0d752de6dfe5ecfea3e9cc252", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "46214f9f017a321e0abb75331245698a74ff9f237523c9ce215ac65933cc1cfe", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APazxbmv7XuY3HGPv78dKBrmVEcwM7WvQT", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022077b832c799ce580767d5cbecdb11931f275eea20dbebd52da9887b1d14f819fa02200d627285c77f4aef89a4af226a4ac6cea5e47555948693600aaa0fca6cce06bd", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "6ed37d18b737bb47ebe3573f111d4e065cee44b0b464651afd7c6fd1a3ac64a0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Abr5cUc7zCXKAyTjz5irn4JcxEmihPG84U", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203a5610a2f5004252ac2cecd7a96122e94df048da7e6827542710e52b0ef8e3d80220037cfb81f3ae8c30a590cf443cae92f7d8b8fb14e61cec9d9f9dcfda8e4e07f4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2395164855b021ab8793d8fa437799f44c9c1dee5e8f1fafb194256fd455aedf", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APCQx6efoAU2i9aALsmp7VtKcSgteuSwQR", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201f66a789fd8c0f3546e1e4c3138d0dfd08cc4f4b02ead17da6d271f691e3f8350220156c2eb3b114af58a4f479f21a7d5f3b9d42b27f68727bcd4e8d55e3eefcf556", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "dabbefbd9c7abcf87ac1d02a63a93bf705851e2c18729c3eccc733e5f8a13fe0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKcewanzTJGJ6akaUBV1zU8EVgFcZ6L6iJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200c59e7f29cedbc614272cc54db38900dfd2ed3e950c6b09b1e05351454ab18d7022058ba7ebfc4608b0c1dba7f738bfb9079ad312a2bcf582f7bbcfea2c9d9154512", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "64fe150acc22603fe1ba7e2b36cb9079fc4416e98459bce373a50e12a470a2df", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASQqwPSZhBpeaMRt6FinqfDRLwQNUwdLou", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ff5efda54a16a449738762ed1bfc3b827fc445b45d08d5f24bb7273b17679a0202205081d641f5439659141865d6326063dae072bad99653833dccd3140ea16361ba", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fd8c6ea2eda8883bde71e51afe1e6d41f38650cdc1086b4a2d22a8a754b9b0e9", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdyH7jMm8yK3QpucKYbrxWJBFfekmSkNtj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200aac2082a2ff8f691a6e5fb78df2f5653aabaff8bc820b54df055cf08d1718d80220434d25749c41b440195a3a7a740783d77ffe651ab7dcb0bff87cb31517cf7b90", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d32ea8d330f50c1a9e9d2cea2848470567bf95cee87c7e149285af5da1004a75", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASChm8xtb6tHD75zfszZKsscQnjXk5KVo9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220246bda16b8733b126c70ff18355dd415d67108e4e7f70ca655eed3acc5c5b7170220248fb5659db3536d85d5d81b8edf9e79bb3da9b8bdb27c30d1d7832d4b144114", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a1daec9a51eafcbfae35c716d9a2d923746e076d0ebec2807afbb2602fab34d4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdAd7MpFF8HYVL5PkZ173wsYutFiWs3svS", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022028936c369540f3798b93534ef30af8b3598bddbf679a76b6ec6dc4dc23485852022043e69874473c54d8312981e08dddaa233d5c2bd9b1367da43605f7dfbdf16382", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bf0fb0bb99384b54e489e5d014ee7716e49827ecd186dae3f27e724b91eec56f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AP5j7BWDUrncx8QRedbFKrxTmGxa3n4uSg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100c199667870fa4cc632608940f31bb7b2b0cd203223859cb540fb5f0fd58277aa0220767087cb7e0c2a90516804ff7227886333c4948310f63e2bff57f139ac2abcb5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d83bdd38633aeb434e88726c64efc21d974e1889bfa10bdf538987baa5247670", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKFbuM4jtwfXCTqCJMXin6Mp3tTubSxPya", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204ba60a594dda3c1a445908234096823af00ef0f2f7eb1da0aa94fd5a23989f170220342c45a45c0778242f6f1ef80f44446f8eaae6f2a3a4a085dc5976ee2bac9352", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e875a9deb0e058bae1685451373703479fc887abc0c26ddda6b132d12b10b33d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJXSVBkY4A65aDLudX8DohM6f7gyzYqLqF", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008e9e915e490e7aa1d969752f4932b595e9a8d5883a17d87883235d44753d6b0002203ec87c347b447c8c998e62535897df5f93bc2a5428616db0b165c097358af4a4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "30a5c19cf5e938ec498f5225ce47aef762cb0b8c9f23663e6646eed890914d33", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGPnbhUdoCoqdQWi6trWnCjJ3jxb1NuZYo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200f4c408046d46d8b04deedfc2f21b1dbd7988c8c5d5354b4aa371ea77c5babc90220571bceba36616dc7691e29e6b12eb567e74e20182d3b8f9093737a9d69c7a8c2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f84e2c788c3c1d5af4cb03d4743a1dd2fbd960f50ad3b35c4a20570388038c72", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMefC8bZdYZPgYjcnsEBgVqR4ZXGt3HEmJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100add36cbd0bb4f52721565c2d8df625965fce1b5f6a97943fa414a7918d51c429022072260aa30e9ceb9f59c5474db25885b8d888853b7a9af2a54d059907a3196583", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "b8a9bb6ba45428ce26df1870e2c7815efcffb161a72e658ef5ef93c931516fbf", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXysAKqGRaUaw3XrwXpj91b8CDPY8buvQA", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202f0d8cb3c4fac91a4c0da11f30a594433906638ecc37dd2877b14d5188d7e7de02205bd9aec0cbef58f02861c864be3b859acbc48863682d60e0714bc874bc5d65f7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "9e0b4329be5764791663357a363eaaaa453a51e0e6dadad385c891ebd9be4b0c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ATF3vBJKFQPKjNyUxieoArZ8TQgso9GGMK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100fe8b71f3cf816b0aec223486ad7a983449081d98c4a28cfb60524d1350eb28a2022030dd6b2b1b368e1709294cf05e1e6e2626e8eafa0e65cd4fdbb1a769c8b54d6e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f6ad0edbbc1a13418950e41c5bd88732a5ba8f84e3ce79c5a2abf286d4ed8e80", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH5S3Pc7it3gEzUUJN3bZePDzrNN6pEjcR", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b1da007c36b376f58f4a9b2908a4ec443c83954ac2d24d513d5a848312486207022043251f92b524b5fdaf62a3b8b796df0d61151743dddc3d42e03f5b2844892567", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "703926c801192eb4003dfb341269fc43cb71b7983cc789513d58e005cb37d528", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUP95N5fHeqCuvXVXQZWZpxm8bj77FmASL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022036cffb519c2d58623a5ce2793c49cfd2e064d6afe8b63f9bece927dd9be3337602206e445e277699f2c84e3a4b38ea9b3d63a542dcc88ffb313dae7d3b998b43296a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fb5019c3134408882602ac82be0e33cb44de2adaa6d228818c55002e4de79308", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQko9usk8N7wGV6VF8QsxXtsqbsm5YdCDL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100c5ccb793dab297ca6b9bf7aaf816492209cb6a4e76aa7ec669a8f4953436cb0f02205bb2764c5ce25e2fcd78bea82045a4d938ca97ad73b1cda2a0e217330677fe85", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "9cc49b610292df5549f617559c717d9729d322e221cacc3edc133b7a9a445c89", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdYJP7AmU5DJfsmg1Lycc9ytGAbMz2wuf6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e9d0b9103e3b7039bd2af8c683d18b9fc80b3ee7a37fda7732eab83f134b9e6002204345e884a6b57e7a60dae33ca84037786982ff156ebca0b7242e46f7564dc087", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d6d58bc704c4af5aeb8099857f6924eee24ffe0a006245679bd9b3f2245150f2", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARyFEoh7kXmPjDjZtbbGQEm3o8rz9bxmGa", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100f2eacfd6def9d0821cebe20d0a9ebc39300ef04123d6e400d43a1acc9fc395df02207eaecc1544bcdf2a7efe94e8a34658c8bf85bf5f5773cbe596d8420bdd08aaa3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3127ba17809631c57c9eb3222f6d7d4f660e4d182485cd43bbcabec11add28b4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJ9MXucUFg8wqPCNmd32iUZmYa5roXhYiW", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a3b5cd0f63a58b4f8ae324e2b36c2227d0473e4f9605850a8164670906432c3d022005ebcb4a9dca2084612261da00ff98cbabf26cdd34d0b2002ecb025958cb6439", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "277ee179972217a706d3acef74d06d07e906ef596e916034855e4e7540dbf2e4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdXg3Gchp8XBiPUdKCH9oqCgYSxbezXNdn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100df31bdae55e88f356e6aa8cb25cbb4e15ff7f4193666fcfb7385d8a22963c860022063a7675978379b15e215c0ce6a0411070b750cfcc3b7c706f98a826317d0de3f", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "b8b318753433e67f9a7947125cc32a14ae2cad94bd1896bd40677f4d4f4922ba", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AdFL6sbWYiJG2AFsAevb3juTiJseEH2rrr", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022004dd2f18f68f424c2c69e01156e39f222e26274b642c6cc5ee6b66062304796302201fa14951b0950a7234510da425d88a1199e9501f033ea4fa32c4c1e7bb1d3d78", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5df90252e3727693ed41b9be836ba413dfc471b1412283cccedce8555479cfd0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFzhSbX7MMxZEeoz9mNFCvUeNX2iBxNK5o", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a0a9092f9b3514bc48eff7c6bfb43c0697eb48015fe7897ce48e8f386e051dbc02207108b1ef48e65ef7756ce002392a6f8cf59430c18ff7b3ce96ab8f23d755fe7a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8012e6f2926c4ea1962cb329f19a1939fdf2d3896b66358605cb667cbf7f43ee", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXYAfJVjupjT29R7g7V8ZQXGtfBAYnMFZn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022032ab7465a76eda3028b3798a4b6c16885171e413d6bf3b072d4954cc09fd620a0220530fa60b84febc2c2a185aa911258730b35108b924b71b7754240af795a6f975", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "75c5ac9d784bcb1ab2af4325de0844840ef0b08a56717ca85beb3aef7fa04c69", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJZeVY3wgx3VwbPs9MrVXWz1mK8quD6omE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022023035af672aaca03ba8ae2a15e18a30565df3e4746e02acaf298291db0678188022048a7832778504c5764670543db3b3f5e587857623db2be66a6181b1cd8b3ddb3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4c6995634cbfd7ec71c6017f4cd36b0f38d35d9dd2eb96069dc7cde68047e994", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AcnNYB6HhHzfwzfyquZTXmn9FCLPFsXugf", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220750b33fb08446db47bdc77cc7321ba9f108b077a7fe4f6c03db58e876ae07c050220054da7efa11bfbfc06fcbc04a0d704c6c8c3cf24fe113f4c5d25aacdd074dcd7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c95be8065bfa8eb0bf7d475d4bde55a1f1a0b4f740c773ff05545317859ff6d7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJ1bRNQ4onQPvs9AMWoKtheVStwcf528zh", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201487f7a73feb70885ce25cc5d5bc0f56da5dd6be347a752b64d77784d835312a022005733ce71d2fee65c95f80a62473a41dcfe4e5345dd5c141a6a72eff5da6baab", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "301e915143b8edbbeeb7bb7333a38ffb1888318e57f3feefde3c2927ada0f635", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APM5mRCaLXyifdsfmd3x2SKdnq324aW5xT", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202ed51c6076c602e665372e30e5c02591c3a901da816772d34be845e3928e467102201229bed0e277adbaa75722991c86ef2c5ca6c057ee8d22bf5de5ce2b9412a835", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "86172795f61762d31fe2886372e97025199002f176479e9720102a7684ade9ca", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUqfHo5psb2xs2vzfikJVkJgfUodjreDuk", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206f5eea1e2aab48a48269a3bfd45c976b8fb5ec2438a82607a23d058bd15277ce02203c234bc81ceb7e439418dc72fdea94802f4cde42ec0a74230910df6f9dbef130", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d1ce4a5803bfe9b5a193996a0baed2eb69fb25902e1af1972396634315afb8be", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJJqpHNtEs26scVRiwiLgnR4PSFKxjBmB9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b0d237f5e4d7fbc844fdf22b888732c7fdb34fef2b22eaea5f5891716ec69af202200a62c1183f994792796aead0299ea11374a1d06aa3fdc68f53793ab27f558fc8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a12c680b5156c3d93ca836555385632d6d34198d80ea8e417db2f27b2d2298c0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASjr37NksJJunDrwkCPKyZRANPLPxFNGSJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220746df34e38555133a36968218c7cefb46e58e471139e11bdc4909cb18c4fad4d02206941a01b1a02c5bbf201fd82ba42c68bdabd34169292c3c372e36808d2194b15", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a3fd14746d1b31b010cfe35c904f80f70b538e3c2842866861b20667d2bc72a3", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGvYueD6VRnE2D83cU8FLhUzKHmZXbS5Vo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202af2052045ae608334dddfefa8d94fc1c8b994ddb6dd9876dde9f076c0f7227502200a5aba26a3170a5ce8ddb530e87527fc7a9f81ebe78019049ab37accd75a30e4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "01cef74d15bdf0f0889999bb2fde52bb9465ac60680ffad1c898a31129fc90b6", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFv8tBd4biYfZrAYpgDmcvAqgdBi9y5k5c", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201c84d773fba9cbb0653ee64c07f0ee041761a3520c81988fd0afa8c58b1b57140220114fe2f3fae89e52838ee5556138706ba90c409ceac1ee091c3e3eebe9c138f9", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "513cee5310e9357620b371d1a17aba3b5c360c44521c08602ad90222c43ab2b7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbuQtgGCksfDowEg62W3R6fN8iUSKEuQ7p", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206780dc95400f62205fb1ff95b28fefdfa3e4f3bb1c2dd8b989ae0a9598710b110220788b984f9b6aa33772bdf10783e03d4ec8ac6a6330a7c29a61603f8d80f1a100", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "671be425d8b568ef7e73102c7ff94306647fe83871a46d06929abb18d58fa2df", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGkZtkcyh8i8wDoiGm2zHvfLzZrhWc5V3w", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100dd428faf70964c81d43252a59576fb09e9ae0597257c023563fd07a26b2fc17b022074e6f434aa1c58c4e025f1dbecc6c66d52dc423c2713a8ffd4631c5e538750f9", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d1976ad729fcd16e577c3ace4fc2f70dae590471b4668fcce6383a67d2ece5cc", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYXnBEVyMzQ5ujJjyxrGnfexubnAdc3Xi5", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100da0dc3f1d42076c3e6b61294c1dc5484c9107ebb39d77a2f7929690f498a253d02202280b49313bd61e55c92e9b623fc74657e353bc7a62269888ebef38fe43a9539", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "66ba43e050831bc5149b69908f494f4ac2945d42001d845d0d8c30f4ce9d58ec", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUNdcLwCjM1n3oaqxMKmkZPWtdYPNS3FgM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022024d56750e148d5ae545466dd42dd0eb18bfbbd0c07a2c6c4ae244b7b2286253302207ca7283b8f8b45269d99cd597b3dca72cddb897819330872f2567980da6dfe40", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8cd52e0c8077c04d8d8e8e8a50bb11aceb0b150b342cfdd32295746cceb2a56d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYUTobeLMQdRv9mgoK3JSfBriNi9iGS1Ff", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200fa3d8377e738cdab0c4ef2d00395347261dec3af3ea06dea480dd747d3b4d3d02202c27c151b09f9088789bcb7afa5d98913c39940c3c82bf63c7ee5d859ebf6b56", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "b5eff427afd510d0da18794e7e548091cccea4d1268135468ae7eead8349efd1", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbW8nwbHoDhtVsdVHVVQLy88vRHfySnEkU", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203bc385dc273e65a5eff2284ba02ae88be7adc90555cbac01053fbb91a0d4ba71022039e5d47361eb2b6fd8d9f99c5ad8231d78b1f5c84ee9e3a023b5607a45ded74d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2e8ef1ba0928cc8ed9a63f7ddc43df60ee9a556d4c151ac05446a3f50071d44c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Aa11BRTWqZbeb4jXAJkGqaQ78SHmXikVrj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bef96c0c308b9dcfd73b3e8022438a3623e15b5ae3864956cd75ec80ae68ad03022019034b10ffa4aca438be4f5fe56639ddbb59a50994df187e5ed0bbe0856fe00a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ee7dec36da049130d5d9310a391e24f75620ac5baa46fba1b1486fb3d242cbc5", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWGWuovUFcxKNzNy5An8AFA2JfxfSnEGDK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bd6e15fc5e58445b9c5b0c67981a31206e697cc5a846fdd00cabb9cce556db5f022009b69719470f1ffe82da63a9f4f0a6219006cc769513d4709951cbbed801f5db", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bf902b9dfc89e78332e12a81b8ec7a017b6040bbe2e2d979969ce449937c5143", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZaos8cfC2u9TLw7mE3qMPJuEtuDLyxtVC", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206578ec80b033f4ac9a2ebce658fc7e096e49e4c60ae91aa191369ad3570b287302201b359dc9f191963ebad6dbf819ca0c03ac341373767153861c44c4cf68933e7d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d7d4d20213df9e6410db4b22937a8d2926130ca4dad5c0a466edfc06a04f4519", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AaP3FYebrQXfLT64T2r52HsVEYXDU41QEk", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100aa0af940a123257c5d1e3f06d7dbd0f537e03374ec07a2e8cd00f1651618c1cc02203531bfc2578d9df5082f87f7ac86df4441157cecc6b451db69d2a09985e86f22", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "7946eebeaa7b312f726d0ff3e4184f3087694c5567b9f4a218562ff0251256a7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMnKgkf7DT6vcyjTBs3gPqPhTNJ2z4oY95", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009cdb480847a9ba3559f5a1659ecb2d421e533759d04f5e85f7118e5749ca52e902200685560c698960d088a57b2dbaae13ba32ed8ee0c441e0ea23b45ac64e523ec8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1d06a25bcf303c4717c1de9906e0266c27d1de70bc226e32e85dd6fd69cfd208", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Act3vdrPTWgJFoRaYdRtHCZAwfQ5cJpbF9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204f8ad4f936551545bbcbdc0e52de66dd7cc207f3740f4683b6b3d72a101865c30220546840e876e8332ad07272cddcad2bb1e1eabed705c7c3ffd6e4e7918770c536", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e876c22b4d6fe9b4df652e14b1446f5385e457ea8ac23993b0ad32aa8d352704", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH9MHh829oyDk4f4sJhtEWvQTENY1P215p", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100df982190a2122f1fa4d64e450e717636d9135223e57983bb0c41624f7408f40402202704731d2e9d480475dcfe935adc9ac88c1e3eba28532b53de9d99640b0d8814", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d59f887b226f6e72876bfe8d2965eaa8938bbe64e90890500247ac81d9f2562c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APJYmiKAtvmPQmuibHB7qab8EhDoTHYeuj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220728d51f31d5852429dee752acaccb34df20f47cbd65bf42820cb4d4e572b3230022071d16f6845ac42021e5f4a2e32ca6cf6d276525e177327cd6551f9b615f22f28", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "91fcfb3f5f449cb13007e1f12e3939d08d41b220f0af1e32d7045f3454e82c97", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXn57wj5CCcAts34twf1V9oTLVoNuRH1YN", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100afdcfc39831803e5e2d01d368e4d582657e1bfdf38ea2f87204fe30699dbe1d1022070c91f4b9072b56a3b8890a6fbc8dbbce5b291aa828da5c17fcc1f6c5d387c22", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8833bad42ff07fb0a8cb4faed234c9a3e1978ee22456afc3b30fbf9e688bd345", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH8LYTdW45WTpKL4vE9TycXfoAYQvBstkn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a247cbd500187e31317879baac09db3e9f5144b1267a20021357fd73ea7284bf022057e3df5c18fdc64fbc99d86650625a514f67955ac47ac482218dd42c4dc81305", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "642b2728aad41b7a86fe457ffee0a709be9eda7caba04c2dc497b2daf6289f93", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMZ8udbpMyJPAj3qwWKGQmtTyfoorV79Yq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100aaaf8083f7a3d13bd2f0222762b0a884fe25293d64f5ce7bf08967be13ff3c3402200acc5bd9620a8e033e0a4d11023b1ddfe6b76b0399575d80ebd7ad1d7df467db", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ef9abfaa8d8d816226eba4ac8bb90f89267b9f2bee5bf217171c478b1a7bdddd", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AeS8atkPDW2y7vXo5oPtK7ifTEYKytVi9i", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bbef8d139a18877df8c9116dfae553c578e98bda4c74cdd95b5df3c54513b6b0022039a8aa83f664016fab85d86d45339d552aef25dbe427fb71819016fea0ad9819", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "08de85ffbf62a6d50669373a68ac792008348ce88244c2c21b2afb1f720c47f1", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APyHyZ16VgCEgLERv86jUx4qYjCZH1NWjq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022058c38374dc01f83559503e3813cdaba2c75da25147b0e564a11df324b1a14cfb022037b1c9d030f8ffa0168b99a395129bece2cd7f21bbb86647a0d91a13cba1e523", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "65300ad39080bd1f96bdf896a2537bd658594a13eebc6f218359c344441b166b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AT447ubYEVkZjS6MesqrRkha1CKfUxVzmi", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220041760cfd3facf18334de6b79c24e6d1e7fdb0bfb4a2f827d0d255b63f68945f022004af0966e25d7acab8b9ed497a5a3420c03f8df10ccd23f09ff4dd5976d36d25", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "75b22e9f34731518b93611f942174a4d3f93b34c47cc6f82c7a6c154f0f46313", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANwc3YQe3EBjuE5sNRacP7fhkngAPaBW4Y", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100df1ee811cc250d9df6a0b634164e7508edb09e98573dfd98d5df9461b28badd602205ab9815a56979e7838e8e539ba4c575357caa26ad9ade783686cafd350ed961b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "0510371b21bdacb08ce194fc533fa82aaaa31fddccbc439f2da8f93d5cbf34ac", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWcwdfqduZx2SsdW6JDpvYqnDT7KbrQ7pZ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100c66b8540a76bd0c2d9cbd2ec0aed0ec31f7e648968c7180a26320072e9cc9c29022059f3a3f8aead4fcba621e77a59c709e89738e773a3e8650b243d0f5efb03e31c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "73ae2a136fe5085970a1ce9e1522cc07c0edffd0c65cf77984b264eb0b039d44", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AHsJaHCVtNbZHprRjZjutEFgQ4LVbxkGCJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220009d0a46ff621e3c73408a4babd9e165927401c150e11f3bcc88501f6842161c02201fd2cde89f1d7bcd524a6a0ba4c1be23e0dc45c8b3b2e7671d9dc11dc5235653", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "06f4158caa35b8e3e10440f544585ecf91d73f78ad3f597570b5294826de9e05", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Adg7FvqeGvnNM9UsvcntypKQnrPEymXhQz", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206c213188492aa5958a932d675c529e77283fb73ae93c9e0e7618184daeb621de02201d38bd6792b71084ee757866eb4715f046e5bac784f1c7419850cbb385d73c3a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f54bdd4e36662a5e8fb6d7453d84d4ea37ecb4741c9e8992d771abcd94c009df", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALQf9fK6iT4RSkwimHepPZ2p2jViAqeA5S", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203ab6ba58ab71faa8d1557340b244b90f7f61541172e7d1dffd0de5475dd8d660022079cd78bd8ea40ec271db7ead0c2bdf65cb7334f9b00cfc523fef8bbbecb92786", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8476b2dde8f481cf18a74104c80bf47c04a473a64b2462fb89511da17e2866f0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AN2cyuquL84qWSQLhEXifdSHod3Nd2a8E3", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100af39fbe4d556c35b5832c8499daced22c6c25148a4c6491734eabf31bff455e1022058148961f68d8a5b94faf17f39947440850b3980de1b0a5d16d590fa9723e245", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4ae0f0533b136bb7bdfcef5bce6b9e380cfdb67e4b10f0eab90727cdde30ff00", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZiXrft2ugGCqhZSTEBgnqpJyL8Tv91ZsW", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022017b878789f1dc5e12970c3ec202acb7b6fd276aeed4ecc634ba66097356c468a022015109996017ac0e9cd3e58ec48c0c9ee76d1fef363110c0f0b95a1cab1cb236c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5c918c703ddc1fc9c173dfeff61fe795f547f97f6a773b6523b8d7bf3a5496ea", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXRk7ZtUZpswU1bKyPmKAyH4Qu7itHBRSK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100f10f5e41f00518a078749daf819a5675f4fb365d165b4c9cc271aceea9143f3202201f3dd4c21117a34188b8684ea76b3f71fe618b9a6afe08e179fd331f0237a0ba", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c42ffd89413e3d7cd3d1497e28a4ce30898fa5ab48869c36640d78c18ff41470", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARYSXqnkS5uC876GTEtfQF4RbJ6Bm9Ytm6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008c878960fa82bfc3f8494f66561735fa53ee107a85b3dc5ea71e90ff9fe2120f0220459efc529df7ff01f23d33aa521f93f18ecc7666fed4b93606de26101587d4e5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2a1768cb587c810d09d97f08b73fe6e9e729d87833535b8e56e8ac3400cafd9e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQkf1rHnN3n5dtFsT6RVfEdrdFf1tzwjH6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e9d965630d1f329baaa6d1cbce306d2cc12dbf5bf825c3d68ad437f5473dce40022031e7f20890ca0028785df1b4eca780b7f70b6dc83042c10a3a2a79242cf65715", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "952732a35e6fafeffb9f4a5eb64724a75a9c0a302a56607fad2f2d92463421aa", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJnvt6gphaN3Kr5gU51jEPkwdk1GFPnxAe", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204acf6dd9d37fc95faf34c127e561a8046bc2c2f954f39837eb839e5275b333540220299f166d9ceac1190734744cfb2bf5ec4fd8c9a9f372a9edc77f58811a51c1ef", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "216daae674a4ea45ce17330a777e33af2e9d2ecfa1fcf623746c01a85af601e9", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZKmryqiPiowe79yvVQ18vgRTLsfcXuZ86", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202d0a4ae36c491669ec7efd7d02bdba56e111bdc5c650829896344905ddd93cc3022064773b2a4399c2ee797a10fe152e23cae43b88db5619aa347823013e49d0cf23", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "eb8278e2e2de773f041adcf4c875a011ea47d94d0809bfe691665723a12f8d33", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWoGgnki5EqT6oKB2gB5xa2FSxnhwqtm3T", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022036fe1ac60836edfd0ed4c48514c980ed7b04cfaeb1415a5a2d186a3956d0e3dc022062ae95e9247fc245dbee957044c11cf68619631a4e8fb39880140f5f340edc05", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1434a6640960d6f35fd14f36d0bccfc1000106c67265a9f7632385003ad22861", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZRMoSEx5YBC7P2s78XzLStbRigMe96HrE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009357edab598aa46f07aaddf5aca8322adda62df4456d84c9f6a291f662571cdc022061d4ee2a09e39fb15e08c9b5024e5413ee9c4b76849290a8ee76efd9fcc0812c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1f47a9994974e14e6b939b1081e8f61debcd71c6c351be1a8315216eea11d6cf", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK3CFg4RFwRydgFb2woho9TCE21tRkzsB7", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200f0d1cbfffe14a34cb82a2ac093be93b501d82eb22ad9cc21ec2590034d83310022040a8947034b408244a8c5931409558422b762d225dddce2bf35977b30ecaadfd", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "38d6938ce3c2dff20e5b041b792b0bedcf77d098f7b95f48a6417dcd367aea45", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZUANHK3Gkk9yGAXmebVkqqRokJuzDNZyE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022029ae998756536639e8cf0d879e2be659a843ceb181b44c624b091d40b1e155f702207bda16f92084dca94e3270b977e09a17dd4f077d655ce9cba8471ca5927869a7", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fc6bd895f874e798cf41b20bc6def70dfbb7ef8cf1bcba29cd2e004bfe0c3085", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYzLFs9vPy2wC8niqP4eEamt2eGmTjsyDk", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b97b63cae8c1b39681724638990e30dbcc57542b2727bf65769e83f5872dbe82022038d55cf9bda1d3bcdf0c3c788eff3b3bda1f9400dfe47051142bdf6d0f81dc3c", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d96c14bc00afbd396696ac66b5601dafaf60a8afcb4206433074854718ea89ff", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AVuRN3XiwUTwTrwTAds3sVN1MVhHUhLn4X", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bced1ebee9bb9bc7f654f95bd084582ea99c87a9ed0617ac98f1785d994c5e320220436d176b847fe6dfd139fc30dfef74346b7b1152f43791a61debb191afdeedbc", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4886ecf094a0b5134f84207e4832f114922efd93d920a612b0d87110e359d640", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AXRp7afm6g4ZYscTJkCmB9VfzrpwMyRr5h", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ff42e460789d4ba65ee6a6211d1145235d5af1e7a38b58328fa9b7b8695bdac60220322370d83709a33f3f5a035a26ae07be26b340bc0832025ee9eaebda46870065", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "76b352e8320d007d120b225b107a36e3ae290acd99e483a304649c39e0c28bfb", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AJn7wi5twb3D9UUPBjV1Y2XnCDhVot8RLp", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100d89c2028faaf114737a93afdd63f7baa6aee8335d904cce2f8314e50f3b7407502202dbaf317c685124c5402866f24b3091f2963b2c8f8955c005278bd2abaf4c020", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8967abfb69e7e25ec14ab8ddbdcfc983c20f6e40e297f8e2f05225cb1e806747", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AckcWKfPcT3xoYzFALHK7i7LYTTM4cHVq6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100aecbf727a2766ac2322a8475702a40f1d15030ab97f1021d1da982e1122f2db70220709d8121129761b7f15234e3301dd13918b6fa881a111fee061c80b6ab10fd92", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5ddd6879bd58253855aa17e49fd6ffc8107fca1310b3566cad7322ac244ce3a7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "APttZLM1BYZAvkxh9LVJt8XRwuJZTXnocM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220605bca4598deeef380b50b6804e561b783cf53060172d5ff62833d817d94fb7f02202da5a575a7d1e9a2bd6d30771c6ea0abeff05159d47cd3c0de222e38330fbff6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ccfe66336c2b54297cbf38059295f076a6d6e50df6ebf36ed650ca283ce9d30d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbHBVzCfCkwiNky9zDmZNNTHx7ZCnpgHCH", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022005646a217c3873e24e28fce61da19103e5bbf8916754fe03af204f7778753185022036f19ea6bdc73f3f7f72a007dee2fda49f9a016f9f47a3006f64d35235083bec", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "07fdf03a4dc65793cca4dcc55c24e10443468097b8af85c48d781586d8fd031d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AS1dhJTJZMJHqea1zqy6h3VnLeDYEsW7Zq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210096287ca81293bf79c425d5265cb438cf06967544a4a4de12b7ec7b2a71d0af4b022058ba5fb1af559e67671877ba2467d16b47c14665920fd07a51a53179a27ef841", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "078d713affb765221a9e1c1277831b824d114baa2af9d6c0d128d85d7a83ff36", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMHwi4hvpkhSaet9VDoeCDYMMTh7hnV2Sa", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009b3ba150626e719639bd4ecd82450ea5e95410f1dbd0b70a4c2e183e9909e275022051cb68725df2a26e0a63d3a3d60939d60435c7310d918e32fe7944baeadd2bca", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d01fd5d1d0ae1b12362288e5a0fbdfb4449c348d621e975ef546e8ed01e2f614", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQKURU7zdHs8tjEHWLt5ipAXaGCgYxknvM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221009f4e8cbb5fb43bd8760d41fd338e7d75f263df3a4bc395d5cfa4f5d29e4bc2ae022065a9b07cabf68ad767f0c1159400b5a8f1ffb65be9c2c22758fc26b911a02a60", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f8afcd4d151b1086c0fe88e5ea1181aee6455052b01e41fd902d057c7e207df8", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKR9GXZGUYqz441D359coxztJonviA2FRM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201b8587b3030213230c38bd19163a1905cf8f0c8ffb78cad152cba66bff387e7502207614b30d9e6b3afab2fcc1d85b10437e44d09b4af90c1fe8fa5038bc1967b900", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a01ad978540a34e9b9c823e8160db6125487df5a70186ca0d28b9854618c7d13", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASn4LHMaH819aCuUwynTBZsaoUwG3uxAhC", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008979c6c1670d77c84ee199a136c8766089ee29bd9fac227a646bfcc5d3f1a0fd022062aa994f7214530e86cc54d1e8d2c8bc33080576f94306451d1cc2bf5d5a8b6d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "2d1eacb315e29c888f6fdfc141174dd3096d68aec8b4dbacdd6685d2454bf4f3", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ATMnnCBtz4mYEi5oEqkCnDWk9RkaeNLvw8", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bee18db9859f3eaef98cb5250751ebe583222f7d348a57295eaf66225f302f5a0220791cee851bd19bd1977d35c9fe5f0e6eb5e93093bc72257d0a42868fbc26fd7a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "d77f1ebcde44ed574f15837ca11c4a96003ce4c6269b6191d28945b8119eb027", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Abu8suR9L2tD7F4gAGHMaQqVbdvXdCFfXG", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203fb66664a3865147d16440b6717c51f6bbe8ddb66823d2f747b40811613bf1ec0220032b2dc85a6ba32dae5f0f26b0c4bb1a5be946988b4db43e853642d7687a5aff", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4f8dca7f4891b8ef5f19a8143459162a60f169fb73e5b2eba3864b5d534916dd", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AL6sZNMWm2VGABvMX4F6eVFLUQJQyJup6i", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022014cfe7dae7e2b16c511d3d46ca0d5187b0d66eaef38fdcab4d3b4c0ed033e86c022031a57b2ce96e90ad18efd72977ef141d4f8c41d3ac65b3105acde652ec85fdb5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8d47f1b6bf907d0a4d1519cc3bf0751af72f478c256b387de484956b892f3a58", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFyrB1QJSh7enu2JpMUYEKUgv3xnJo5gUJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022062a1de80a33c3b11b033ffb5395201cc1a35966c5147bebf08b5e8cb25b3fce80220597ff97c23e9a0f476deb1ef544e560930d7f3b10d038598124b6dc997ea25b6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "94bb60eb4cd0c06c518deede1f34291641f690c9b9e8afb708e63229b2d11c66", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AVQ7sUZaTDi6MgKNuNxzzC1Sn3ee3KjdNg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100cfdcf2052c8d9b7fc8cebcc95c0f478b7ae73c8f3c341c83ec8d53338d32092a02202746050a64e9f885b5a437ea15149564edfd95f5d64df42cc2ae2cc9d6c06a7d", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8d78bbdd92ec1f5a3155b35091e78efd2ea609390e89feb068ba8fb2daeba144", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALf5oWGQhq2xbTSZyCfDuvxbnDSjvYqiKE", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402205777cf25f8fc2b1e4f49956a184fea703cf8d23dc13d463b9ad00d203ac72a0902204c787fc03df8cb5d1b3a123883e75795906f7ab86c648981057ea9cbf6ad5216", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "7f3ba8a2764ac7763b312502c288aa156fe03234b534505a14abc7a41bd97b1b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ATHYUiLRifNafWrFLAVgQYo9dVxntZkow9", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ec3a72421fb511b8862d6c57aa99e5f92ecae3663e2ecbfd18460f74a29dbdab022059b0ec463c271e1891a31dc30fdf8aea925f19c6ac94b8a6d4665acaded60e7a", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "85b5107a1574d236ed625729c6e6f30fce0ed716c4713af97795b72ecdc25647", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALsRw7zX6PeYFVqHpJYeVoeBy3a5mjXCoL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220572d28ae4915d0e4f3b7d0f067e804a3b26fca136f64e52e1c38104f63a9a30d022029e5e9b5ac89528b912aa10de9a82499a9c9804cae6490ad392aebe8202725d2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "9e8b2eafd2065f6d2b3cc9803539a91371308ec683d9ab555cd90d4f5336374d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMGsRHFRL8V6qbUz115q1Wc8pnXBScuS84", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200c85feb2cd8d220b3058310a8eb5211de3b447c7aef7362c9db6365782119b130220276a65cf28e5c13b10cba7356d5ecfa9d534d9519ede5c9bed6505f1f6b0daee", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bfa961e75f14d9c59a347785d2173375770edc0f1bf0be8e4b1f3489a4651c2e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANLm1RbdAqNcM5PMBS99ARjHrvBBfTykZM", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a1a0bb2a4673249f97ab09936ccb02741c9c26f34eddd73fbe411dc6b7763801022006aba02b3064692f4cef1da3407c422ef37b6b955b8523bf57b0f33184cc20db", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "26451f460e007ce959cc3e3e523767cfacf7a38daae563154fa6d307e97057d6", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK8aUnLmspWtccNwDEciSBf2BoxmpRPAow", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220519c73adc7d10d5dc255bbd99591c2de921876f7ffff166d4a844c8fddd9177e02202eea63bbf9895cd42e9161e68f0705cb1a948e6c92938afd66584980093837b6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "55d4f3059db31ba55349992c06edcb19fcc90d0774abeeb827232bc592a818b7", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AGJUdoyjXsfQNsDa1G2x3smFfdJEPB8kyn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210081a95850fb03a338de66b910596f3a2393b71c9639b479f72da1f0bb9c8abd2e0220417847f1a9a9f580bbdb113e2c33a60c1e731f156ce1ccd68b998c1e78d35416", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "cbdc3d4f2c8e6101071807b0a6e772bc70dd2fadd21773d2f7261076964ec89c", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Adm62zHj5cUuJQKsbrz467exSVVgk1bH5j", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100b483316f70425b107545431eaa5cda46401b90c058d424ebe7a3e79e669a3d700220449be77933d84d804620cdb86b03274ef48f2c6ce19551c7624a3b667467f094", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fbd8e580d89cb7ba39eb22dc563f4fba03262961481b21e2deded869753fa0a1", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AR7wLy5nU2kML3Wv9Fdot2RdHAZXn7ftTL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a5fb7b61e2d383a183c3df3b5dcd35dc117817f773b0a559013265615b0f3ef102207bf3145303b739d03a178eed86d556f43f1e0d69ab5ebd775c0e204e7c0477f1", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4b06e580bf1fe51ba4e18edb8cc737bebd9037d3d5c47a3956dfb92dc6126c95", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKTZGxPzLsr3jeaFbBRFJShzrBUpEZHbBA", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210094c4a199937fe607add75465fb4d897619afe089e2bb0540b52b0437f77649bf02205d4015b7a98403f2e630950fb617e62b0a201cf246c9f0c85dc4c2caf84f80a2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "412acb8eff340260f965bd3204818e8dd735ed3148a79320ed0c1cb51fa005c8", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AbJyaYJYZYS4uDwAy7sfNTTcjT1wUnVCew", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100bdfdeaa54eccf67f4b817884d749c616e8e8a0f31ebac750265dd29e319c3198022008befaf9d11fecaac276994f7d055efe2bd3eb062920c40726aa48f4afc912b5", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "eb782b052f5293d3770f8b135230c899c65e6db03eb07df05c93658554bc9a9f", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AX9HUjeUfgZieGsbpkKmUBJLqvZiC7N2aL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202a96e2fae0745187fede717b93002818fbb06f8626169f7b8a9a3efac3b83d7402204efdb6ac12e7da4283849cdbefefe352480b63753e028d76474d63c7ac038270", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c38959c42f58037a788a83f071848f591b8b6f5ed3093ab7464b164e2cabf32b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK9Adi5y1xEftjbr1WVyRCqUuptKsg7xGj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206b832e0894327676b0ac1c045ccba3f6d382a0e117ad75e90a5612d9b81363b702204ef4cabe3c9930bc7d66d5c632d990a7e0cbd2212c48fc2eeedf33c061335b2b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "576f8af16460ade10e986db13c75d0527802d68befee6b621e44f9b78af234ae", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AVxjxrm3bnGSf4yo62f4bbLBWFwAeNhvTq", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201a863874f1973eaec68ea12b911d4c96d46e593874b3cf69228aa1ebc641c20e02206698e10529a352d8d120a1d02842eb7c937d0dad3be09f32942a47f87c6e58e2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c2b3fb3874dd0ab647939456c4d6c4aee1b78391b03975065ad804559736972b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AV4dc3U6awxGiRwxSEY4cgNSwNReBqeKFj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a168e45bdd69a809def1211a68a1a75f15c8959ae7fd89d5bacc22c3f8ae2cca0220100e01bd865f361d6e0ccaac9c15841eca19556da5281b05d0a22e92aeea8367", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "66a7b7b8589537edf28f5c604b8632a697f82acf4ded5257fefc58c337afffdb", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AH9qyvot8HnjcVCvz3s3vCZ3rgg7qumnj6", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ddfbe135233d387ec88e199ed09f0e12f161a9e9081a9dfd7aedf2f67fb0d80202200ab29642f6298459e84b2e8c63efbe9e656d6888f6d38d9593397333612ed7a4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ffa745c5bea46f6bf43495e73c4c73c9443fba611caecdb57747c107a8d99722", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASTDtbrw1i53dbMyaTr6WT4XEbTv81e7pi", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402206bcf836361511ecad0d952991d9e6c8a80079a9cca79f7f4500cc41da4c79c4302205377880c4ab4513c061a4423be280465d6dd519a4e08bd610268c758dfe6f1f3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3be1aeecacd7c09d22aaa89fefe6048e2e547111e8ffa5da6416a2bfd381d25a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AeQwXXZEfMkp2zB9pUJzM85wn52EKX7cE8", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e0579ee6a9cfff8fbfe9b3f379461f0501d24da51396647ffb4f999311437cfb022034064f6ed175a60d20004b811eda3823e24412414cd3eebaac2117335f05234e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "6a8ac0e6ff034f5b68a6337c3d3468779b5caf39b9794285e89e016cfd291dcb", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AWRDMAnZgZp5QEKkQ8fH8E26wtTgwoNUe2", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100d7e8754c4893018358665d0d24093ef6d46ccfbeae729a261cc2164ffcb04ca70220706ee9a83174b8d1007bbb8cfe0b33f181ce8e795ff3ee4606d2b6fb1aefe709", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "a269129b91761a9b44e90545d8a4600cefa0445bd61fb57a691dc06ea152c897", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AHo8ciM7i3ro99RUcZMPV4Ytb5Esq8Xs1E", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ded0a1779b8684fff05c59756ec6fb8628ea48c4554800a40fa346cb969ee6a3022051c763d13821ca260ec6dfb84d433e8036641cb847eb49eeb7b5989b655f3fa8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "8e2c8606b23ed885b2f0f4bda2526303bd62862e220a9763723781829324330e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AV184d6W1B171mb4KkXbmM5nhRQYf9qSYJ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100e17ecf694f779b77c4ab33bc05160b0426b14275545603ba1b5c0a93cba5b51502200b1620f080d3c9eab72418f0df74bd6f3be547c480161cc52e6c1dbff3979372", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "1aeb70719ab7c6e149b7984a884f072b8934ab22ffd85f2228734b72cecc24bc", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AHpqfxf3EA6L4383nw2rq9i5GqoNJadjGD", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100da1d70c7ef34014a8992fc278d394cda2ba27f1e0fb27cd3af1691b691b9418002207c155fe5258ad0c72d0fd964d2caef6644272d1def04085be85086f3378be486", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f133840ad4f0618d7538205e9768815da99286b108de7f9e281ff4f925e7c77e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AUdygMQuvJQ1zBgiEF7EmbisnVN7AM5Aeg", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402204d1a6cd2ad8d056d84b81eba6fb62bed2e1fc7dce9ce52c95dfcc13be5dc7c23022065b2171d8b8ae7d16bac8c13140a73228f230e570503e49d5e2561c6bd12a018", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e745b9bc309d17e9322d46392598a64b9e7776ee2167121731936e18aa6c66d0", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANyRSjSGgb5Lf2bxFkdJG1DRXX5C2GLRwW", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220612a9b44211722b61e4dd299e3f1dda40175f36474ee657ab63cdb83a795475c02205040b4f18184a88ff2850d9fbffbe1a08bc9996824d0e987d537e3296a250523", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "e0507bb6801b2fc35322859788aed6c36d9885f609cb3e70e9eb19058123375e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Aefdn5H3hQHMxfFAYjTXUzbn43HCi13zbF", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022017b920dfc1cd4f2866ca74fc29f94187b10f72442de97991750855cd1552fac302207504ed879538deafce89dcc1338ce04273be493e4b5b4473f4034f3fee4e18c4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ab7f47667098ec957f98b68249ac32a22b3e40d38bffed42a7b5723308965a20", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AKRMzUmHeuHA6nRjW54FLcDFwyg6f4erVK", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30440220262fbb48893c3759c6c19bca588ac3df17819b88a14287c78b58077d8691a271022062c7c2c04b88c424a3fa5d9c3a7f34c896883017d74c64c2c2aad1c091e658b8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "34689da07514153041f8c1520e35abbde604b3e84101f54892728772a1ad5086", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ASdb1P1eikFmmZuywwSKcJ4iFa7LNBZdjC", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402205acf0bcfe01143871ed110d21fd6b2d69e1572f053a612e646473cb91a18273c02203665fe5328d7256e5630bee18816048c263d2cd4c16749ccdd88ed283c0ab596", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "f6e3515d45b1d8fb060ae5216e574428e165e61893022848f36954a68a21bd3e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARF2AUrWPyEMSY7w6SCqg6jeGgi6QxBUTT", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022025b82bdc0abd9ce35d67d8e45a08010fd63585bd9443ad3f83df8d8ad4abaa1f022002630834071f926d0e0f841f2c733dd6703513378d45b12bdf1dc603a38866e6", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5eee7b1993181729bef38d17f4578c8c726833289ef1a15a6b6914007415c77a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AK4MTqy8SoHsrATVDEkVDKTVDX12XcJzjo", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100ba3507c30d2732ceaad71f9e78bfd4f941c0cfbf2c76f1b7c763bcf88cfeb2c20220165b637442ba57c05cbd787fe99bf165e6ea2e365add2d67f423f464bdb4ed1b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3fd3b767215cc7ec93691da2308e98cc39bdcf825dfc55a7043a0ca849c687f8", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "Ab3oWYGX4mMuXsNtE6MXJoPafHG1EbVkS2", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a984c45ea0f2529b8f70519d420c668d95674ebe137ae36eab222b6758acdd1902205e5c3380f6f6fa89dec7a54a17db33597f8209f99e98e74c2ebf198e345361bc", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "16d7a16b5b79ff20dfcb71d4f48410416efc574eaa7fc18b37de7ac2954ea89a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQiMw9hxzdss2js4HN2L1jeEio8Pdpd9yB", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022027b0c0b08bb9cf60137ae89ecca629a3cfa954d8993bc4db473b74a76cd79ea5022018e9b9260e137fce64d31b55d6c307417023552be6720890dc5b3624bd5ae4a2", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5dcdeb5e8aebd4cd0e90782067bc9153f0bfc8f0756c07880d95dfbeb915ff67", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ARzo499fWcgSDomQdquwxt9DzcdkMYDQvw", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3044022062d3b9fd8bb8825f3e4d854041a6a6498a62fd7d0b9afeb8ca30a3d901aa6a3c022073d44fe06624066f7b493e50df17675bbaf95ee7f58d918a264a148d4301ab76", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "c492f2910b1bb03da512e7de4fe9118572ad2fbc6bc50cc527f16578ab7cd567", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZHEyQWc4p15fKRhaK9zmV7gc3mAVa6AF1", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100902528a4b27f364e03c8b64ee440cb12a675c944a61354e785767ceef907aadf022071d2903350cf631d59731714f4023e8f0be3ee2a06bed6fbe003462867382bd8", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "924906d74ec751189887708df2f871205aab83639822608aba5ac3ad8d365e8e", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFnYJTirDrhEmadpc6pqq1sE55RreeiB8P", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304502210092f3e7a7a51f60b3bb066913db06af947de935bd3d2dca15aa4a50186dc55e6e02200f25c64eac24a656c81ced4cf2f52117c7b4c7a488d354e53ee1bdf9bdfed362", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "44923020dd138a6aea4d524109e7209e33e9241e0ab46d55e8f8e6a2f7956467", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AST3nbdEcDc4uVWzDe9hpgZHJxCJxmmysY", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a6b930eb41963b041e2cd4d47c66c264c9a07a4e2c9322eddd5353221fa44c6202200870a6e4c05c03850958f5f9526040452398dde09fc93c750e8e7972f57cef7b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "4efc7395530379cd73b60350282be549a24ee820c31233e6f53697eee30cc2e2", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQRSAa6sFA9oCd1XV6ZbNg5KJwEiFaJH21", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100fb5e70beda3cbf396466465a7503a5a2caae8f6a6905ac2590e859351f6f9f1b02206a5a869c2a41bcb275bc9b446fec87490d2f151fefb958ea0d04347175f231e4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "adbc0691f2d644faeafadbfdca2674e1bd26a246296ec03c4784c771e577d39a", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ANzBg5Kw43X2CSQgaBeHHubzw1swY25Frz", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402203131c4f34b81744095aa9a651310ea6ff9cac0a88d38c4a05105c732816c2f79022020760518c29255e19cd30f677b9eef5a3c4d10c4085071b4ac69f6659e102b15", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "7bdf981c420c4632b9958b0d99e955b4e4c723d99e9f9bbc4a47c935f8d498ff", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMzFowdfLaxLKNThawWEq3QyvDP2SfUv8b", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008dcea7c514e22a165fbe1931fbf6859458417762c3ddfe02756b57c31779a803022030a651befb9afe798ab38f7b4dd9b8f473b292711ba3ebe38d5ff8b2abb08c2e", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "ed0471000a0cdb90359cf94688b6c42f00f47cf4223ee670eeb268cbadb4fd7b", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AFrWisLirag5Upjiik6rnFVLptCpaE9yZj", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "30450221008c7c71fb49fe61b53fe678147be01d4c82b0d9f81189c4478512c30a58b67ef70220485c9bc14feec32aad01c79b65adc9ee20c356d5efce72a23ad7f658d20a9b4b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "22446441b1d6df010bbfe2efd34b6d3e73a3345784a6d4ecdacbf456ec02725d", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "ALtiFFMTVY7uL7hXUpAa2NiCcqUfAKkYqZ", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402201dee9b2181fb6c631e908c80b5e8f133edfb158bdbc04479ca509084c6cede5202200fa9b3a4dacd37b3bbdf270883219b236a16800353880eb708f8ea3e0bc816c4", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "fcda2a2d21852bafa0dd63786dd664a03a707de946ed2caeea101b3ba4ae6099", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AMWaGRkxUMc7EnZVRGKxuihYjQxkXJTGv5", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402200955a4f98d719ba3f9cc6adeb62344631589a544548e62c6b0d5b444fc9d83cb022017323d3f7809b42b2bea159f6178b5db2bb46152ba0aa07eecba84a5dc810ffa", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "0df9638f07dc701a9c6dcb415467ea128fe093bfaf1e865c57fe3d61701c2934", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AQuppbFyZJoY5D7H1vd2bdJdtG86jHhiHL", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100a472f360d3d2971f09017b8dfd0992eebc228d8332eda827c069e16481a749860220127469400183f8732bce62479335f4c71f49377ee4ee274afdd3c38c463783f3", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "5554e8811b7ffddb8b046d9f7f56c1f78a28100e26dfa70a582aac4e14157ce4", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AZm1iZfCLyqAqnWMFhekpTeh6XAspxCUYY", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "3045022100f5a54138359baa3cf068b7b158b0fc12d54a98b6767bd91def4b37282197f0ab02205891142737810a31fa38343f8dca3a64eed666263a08b8fa629be7c87df9308b", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "3ef7e5802a86f2d7888af2ee7ec9bc9870906adc419ffdd719049634edbc44f6", + "timestamp": 0, + "version": 1, + "type": 0, + "fee": 0, + "amount": 1000000000000, + "recipientId": "AYzQYGbwgcmrCgZM3xx5Mv7E9j2TEcAhAn", + "senderPublicKey": "032c1d9fa03f82460735b859cf1c5e1178bb03bc4373a311eacc1d570fcbf3f77f", + "expiration": 0, + "network": 23, + "signature": "304402202291196aa89541305c68dd4ef7cefba16648e71abadedb51fff6d5dd6816ea70022058aa3f59f0eae41d92f568c0d0b05a420ed04bee0cb2f96fb60bd80d1c1dfbde", + "senderId": "AKDXMYVRkxF3W5J4X6KMaNv8hrPaGh5xJX" + }, + { + "id": "bb32309a08d91dbb8caa7aae4b19b373fbabb901b97753066a804da0db81a038", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "026039e8cb61e6050ea0ef22706eecf6f880fcf7581bafda135f0d496ed88e2bbe", + "asset": { + "signature": { + "publicKey": "0309a74e99521a5f4c3fef42a68a4b5c65b5ea89cfad444cfa11fd896009561708" + } + }, + "signature": "3045022100b40c6bda165542258f7f54590bfbe792c652d3eb7a1367363d93cd4121020c9c0220190aa9126905e8199c7bde279a443901dda9b249bb95caf838a4512ad60c9bd6", + "senderId": "AZm1iZfCLyqAqnWMFhekpTeh6XAspxCUYY" + }, + { + "id": "bba6d5be1970a5b24b94010b1fc624721d66ada2e67d5a545f1ea866902d711f", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b717b22da94ce06e2b6f76b20df13cea230fa0c3f411dc45ea98b9a2e2674623", + "asset": { + "signature": { + "publicKey": "03b6d6299759f40d29760a9e2cbaff0fe8159b84a479d3865450e93adb9442239a" + } + }, + "signature": "3045022100b56b35f9ea060ad1f61124a28bcb5909f516bf58727d70af9d9f1446795a28c602205142ae67d4c94b536d29f0a05308ff0e924ef30bd0c1f3bbb02fe448d1aa23a6", + "senderId": "AQuppbFyZJoY5D7H1vd2bdJdtG86jHhiHL" + }, + { + "id": "47a96b97d3a1f6e2db5f2d77c61a419a2850bbde3ab51d97cb6faae6a1c3b4f3", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032cb55adb5adc85a666b10b101e8ec595bcaa4993f3ce9119375f35ddad6c5104", + "asset": { + "signature": { + "publicKey": "020d5db68f8c33612971e0c8a00576e1516b344191b88b7edd608bce7c953a60ae" + } + }, + "signature": "3045022100c0cec62e2a7a2d548df4b60401b747744711869fda9fdc1dd438bf639d74006302200c8523fd9bddf2597055480a965173cecb624ca8b42ebe1319b80cd937858841", + "senderId": "AMWaGRkxUMc7EnZVRGKxuihYjQxkXJTGv5" + }, + { + "id": "397db1ea74e602cc705c33855228cd3239bd18a63b85cdb156b43b613d2f16d8", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02e1714b7bf5c6101368bb47e68e8904b93ed5dd9e358eadee4bef89e1e25e5c76", + "asset": { + "signature": { + "publicKey": "02ae7019155478100138e458536f15dca4dfbb88e3f393185f52e9cd9edffa5bda" + } + }, + "signature": "3045022100f359a27ffad33510cfd770e0b19867ef9c327a7cce9f40d47a0835097d65ac9102202e26caa61ac8ac11dfd150d86081724df8313d0ea5befd86376c46425dd8ad96", + "senderId": "ALtiFFMTVY7uL7hXUpAa2NiCcqUfAKkYqZ" + }, + { + "id": "513b4b98806ff1916ba9774007659a9e4881a73d5d50e6722410f4000bd881d6", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "022134c050b767b87991bdb17582302fa1ab6ae0549b0ff36fe1259cc348084e68", + "asset": { + "signature": { + "publicKey": "03708880de2a462cbc0077dabecc85ecbc27d5085cbde2f23186972b96d04abe0a" + } + }, + "signature": "3045022100afc767fa625e0e055b239a6c040670457037195ed22b61ae95b72ed22800fdaa022071e70bf4b3d8b7b08a66caf00914e4dcf737ee9cb30aede12f69685503d78ec7", + "senderId": "AFrWisLirag5Upjiik6rnFVLptCpaE9yZj" + }, + { + "id": "14c63f36bc4086a5fc848f654c4def6fea8daeb4b6cdde91c07bd6e348299112", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0230dd82d88f144160176270f09ebe50f365d94837563ab7c47630759b45cb4121", + "asset": { + "signature": { + "publicKey": "032069ac5f3ea991c800da6a52efab48269d4bb1a287d449f75b4963e8b97aebe5" + } + }, + "signature": "3045022100d15b5bafd3ac96e51e16263e05667ecbae694e726299983211892a6443b6628c02203d6e3e67f6b399e99b0819ef12c0ea9c713985ca412a38109165d2ff396014a1", + "senderId": "AMzFowdfLaxLKNThawWEq3QyvDP2SfUv8b" + }, + { + "id": "6dbd7c01b04d18fc5b503a943aaa9412f7e6d8bc60daae94ed62a71fce683787", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02e9725fb335e3e4a0163a43292428d696a71aa3e02a5d3bf1c400263360b13aa5", + "asset": { + "signature": { + "publicKey": "021aaad76814e52441b7d6c1119c731ca9e677804aaacaedaebed49ee5f1263843" + } + }, + "signature": "3045022100a6f2eb6a9f96a8e80e0ad7e88a0cbbcaacdd31f6da8bebba1d94a615c4f3bb0802206ffd8effcdcd2f5dd67f79ff861173bd6d16baadd6a680141d40c3cffcbd1509", + "senderId": "ANzBg5Kw43X2CSQgaBeHHubzw1swY25Frz" + }, + { + "id": "5c43c5018379663b344fdad44a287b3d702b4ecb44eb1015b7431571044f4838", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "026f5418fc21d9300c51d4294c2482217db2f756b79899abfd68c57bb8cb7e6d33", + "asset": { + "signature": { + "publicKey": "036415c49027e08b725be09bee1df772a0f97967911944284553991f131cd53f52" + } + }, + "signature": "30450221009e32b798c7da49bf26f19f6af2286e2a8454a9d698ec4f335ebd2d8c4634b2590220696032103713d41f0ee76814cd18eae9715e5362b60d1f38e7f253b1d7d239e4", + "senderId": "AQRSAa6sFA9oCd1XV6ZbNg5KJwEiFaJH21" + }, + { + "id": "cedaddb346da1f549e538e5078f059706e06c260c07a523c19c29dca3963e9a4", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03bf66b51d43f9b373635a04dd785882d5b920463b845127dc50dc61323b6899fa", + "asset": { + "signature": { + "publicKey": "02da7401be77578c5816a686e490c2634958566ef4539a2ef8dbd99dfc3bbc1622" + } + }, + "signature": "3045022100fdf1ecbb5500298ea3c365785e20a40b05b5ccbadc86ce9aa04536b1f1293d8e022029f6d6b8f586207959dd3f6699f22d353c3984c93a0402677884aa1288925ca5", + "senderId": "AST3nbdEcDc4uVWzDe9hpgZHJxCJxmmysY" + }, + { + "id": "b149fc01e1fabcde67114ca832e7dea4a4afcc8fa8bf3ed8b08aabf2ed11b35c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f8a36f6c492c081bbe1943764d962eee5dbae0b1b06b299751380a979c620e54", + "asset": { + "signature": { + "publicKey": "030fb10bbba9c58b60fe077151e60f5afa44ffff896b23953032f9c8a342ca74b2" + } + }, + "signature": "304402201e7d7a8cbb170d58a6b5f6f7333d19305dd1de234aa8b7fbed087f3691cd947f02201ce02ddbacd7d7b0cb7d961157f1494227229198a3651505dfa4967fa4b96f13", + "senderId": "AFnYJTirDrhEmadpc6pqq1sE55RreeiB8P" + }, + { + "id": "81b39fbe0126dc4f663f988c99d823c7a97438df0665a8e18fea7c3db6761688", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03167b1b5a15097c60d4510425c8cb9ba440f2d48cbedf184a66e40ef5adde1400", + "asset": { + "signature": { + "publicKey": "028902efe250d06bc09d89a3789ee754652bd2d57d28178563cf889e7ef158b0f3" + } + }, + "signature": "3045022100be305b78d0257ae214c4c66a5862c64563e67326efb9291297a8671863f0b7f802205fa48e5abfb5eb07c259970344960c9b32c252e4817daced7667b4b810bcfca1", + "senderId": "AZHEyQWc4p15fKRhaK9zmV7gc3mAVa6AF1" + }, + { + "id": "85057fbb9dfb583406cce3a90d33ce438a51e854e176e96eed8969d551ee5077", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f9dd1b5a26d9c9919909ed33c3d832734c18e3bd876b24f0a74c96d35d2937d0", + "asset": { + "signature": { + "publicKey": "0201204f290bf5c8a826693dd85eadd514a752f788a3ae04027acf2fe91524fccf" + } + }, + "signature": "3045022100a924a821e968651e8ee2c8b69a18514d89b0bbb21aed2ae406b15f7e20c6f583022018f062385e6862b04d2d37beb011debb5d8ceeae40b25426b2ce13765d3ba48d", + "senderId": "ARzo499fWcgSDomQdquwxt9DzcdkMYDQvw" + }, + { + "id": "ff498fe16a3885ead3f447e6c1f12fa716591b053558a1f9aac50a117056a9e7", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02072106d6946a9de07cdfe30d70683ffcc7bae860fb29025fb8398b4de0e27f3e", + "asset": { + "signature": { + "publicKey": "0361416b34d1b9411119d19fe9fbabd92a3ffcb40f2f9809c5556d018a511a2090" + } + }, + "signature": "30440220153a84425518156f836d2df4200913156019b890d05185b1f1d092135cf16942022054f886da164e14057dddf61e145a073e3151a1ffb4b53e226a98eea1e7be5564", + "senderId": "AQiMw9hxzdss2js4HN2L1jeEio8Pdpd9yB" + }, + { + "id": "05ce849497a37c5d4a3d2f4a6e3f2ab0a03d668ebfca63e26c8670783c7f77fe", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032994c3c42dbe940d4e1aa91beaff6b6746e917cbe1709a36def3389095afbd4a", + "asset": { + "signature": { + "publicKey": "028efcad1f72b84857d8459b326fcc213a1098c0b3c04a35b60bed46c20feaef5f" + } + }, + "signature": "304502210099ada522fa643080d35ae0303f7bf9fd42f2bc0e82c345286f9ee09bc52a3b9502201a3cd043d51b79b5df6df4b334cd219e1981ada9675ba4117dbbc18e4d77f82e", + "senderId": "Ab3oWYGX4mMuXsNtE6MXJoPafHG1EbVkS2" + }, + { + "id": "eb227e6876955d0b610566c4d76c3a3c5c0f0d439dc26a019015ccb918ac027a", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "036c1748887645da79305f6b62da3df56725355538b56a3072ddeb32ee228c016e", + "asset": { + "signature": { + "publicKey": "03a1be2ffb0c08b7747757ac30f491d680ece8c7aebf93244deff599b49e9ac091" + } + }, + "signature": "3045022100f779704abe115d0a9450d98c740ca09c44f468f714ace07953d7efcd5ab5257902206206eac5e0ddb74a61724a8bd479480c92f5e6caa5be13d10d51389450fbc1fe", + "senderId": "AK4MTqy8SoHsrATVDEkVDKTVDX12XcJzjo" + }, + { + "id": "be606de2880d97099af92d50a761dd79830505513b9e4f1f1b64e6ceb5932b44", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f30cdb63b466e72751b34ebc8f98c68dfcbfd842486128f98b81e8e54c14cf47", + "asset": { + "signature": { + "publicKey": "0337be3b271c1ad0ad619ad68b5df7974e964e7e0e48f86ae2a4e02c7533885d26" + } + }, + "signature": "30440220513c70ebdee8dfeb6d5bdc6dd73b208b07040a09e820b5c8121a9b5ce2b387b3022055ef15a5214433bbfdcaf53eb5e4d74aed81fa04b1d92ce3b5c504ee0052f701", + "senderId": "ARF2AUrWPyEMSY7w6SCqg6jeGgi6QxBUTT" + }, + { + "id": "3e10c5b31d8a467d6ef56d4dcdca12595987ded73ab368e134a314a69b398ee3", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "037a5ab54742b6088fec6c487c37d961de076999eeaa462a9b25fadd131a93a75f", + "asset": { + "signature": { + "publicKey": "02a0653d79f2315557161cf5a2805a2bd0453fddf5ff71671a17d5921e1e309d3c" + } + }, + "signature": "304402204e76e4210b2439b57f6bbee6123abdd58bdc089069b6a76fa8f2793c8723f5bd02201eddfec1efccdc74fc0e3a6243dc53ce4c65896530a3003c9101bb51647b0c09", + "senderId": "ASdb1P1eikFmmZuywwSKcJ4iFa7LNBZdjC" + }, + { + "id": "8766e770f746dc80f9b037ed8a5ee5efefc91007c4f82e217607baa140f089c9", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f40978e58affc1bb5ae6746e1c481f6e4f2093b1fe4f627f33b2b677fc8b82a2", + "asset": { + "signature": { + "publicKey": "0389c87413dc7b58e8edcbdf31d30ac2085d1711272a29ceb84b352d1dd8cdc756" + } + }, + "signature": "3044022069db71173d696853191c8850f04d31e363aa6eac9031fa796a4df697b5efad3e02202a40b3704f4fdc72e85e89e027e84ea66b075e76a5518556ef85c7eead101094", + "senderId": "AKRMzUmHeuHA6nRjW54FLcDFwyg6f4erVK" + }, + { + "id": "226933126ae1fe568f8528ee2e703ebf31a341938255c80b4d4fae965d12bedb", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02d3e51bb756f2522139daccd06aacba36982536338210ac6f43b3dacaf22cde48", + "asset": { + "signature": { + "publicKey": "03ed895b981b6b7314f2bc6ccfa8aed69973be83bc0e8f0b37e861907110d744e3" + } + }, + "signature": "3045022100e2939bf631d1774359e8d517af94249da7ca01f8cc5193eb4448ed187198bbd20220507204da957078aa0f70cd4b8d51b71b715b1cd7db3628014942abe0a03de90f", + "senderId": "Aefdn5H3hQHMxfFAYjTXUzbn43HCi13zbF" + }, + { + "id": "3a6ef84e0713394c79cd3eb0f28e4f0bc9795fdeb120432ca231ed27c10c31a7", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "021b9a39a3281e7f4402021450f561b439fb2faa3b16a15c87b92fcfc5e4aac15c", + "asset": { + "signature": { + "publicKey": "03f0facc3075b52961d5eb6b5b8fc8a0653ee3e50749fab38cb30b8acb39423455" + } + }, + "signature": "3044022063da2f47889a8a5e655959677db7fd05897d109da21f050ad0bd57715379c0730220670968331b43dcd853bb969b94092690f5ffdc9729ba91f371570774125b954b", + "senderId": "ANyRSjSGgb5Lf2bxFkdJG1DRXX5C2GLRwW" + }, + { + "id": "7ec6ca17804529e63147d4a35de323d4e8e4c9ed8c5fac4aaaebcc172a88203d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "033425c8ebeda418aa1f0d2b7400923d53168e921a8be8ebd3c5584e793c17b038", + "asset": { + "signature": { + "publicKey": "02aba8f5cdabf7d5437590273c497ed4435703e4bc1af0e4018e5a670827fd6136" + } + }, + "signature": "304402205ac5f2f7dfed6d6baa8424d7eac78d7456fafe412837955eb5a95c17a5516ebc02207d6c03c145b754caba9eccb4814da30977080cbaeb9c6d1b4e2c9683cb2fb42e", + "senderId": "AUdygMQuvJQ1zBgiEF7EmbisnVN7AM5Aeg" + }, + { + "id": "20e49a49f39e0e581e1e5e2c9fb9435c45abaffd1c35340f27f084590e801caa", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03b2d0bc348b254f8bf98612e56af68b52ff84bf0daf35690b4d60455911092efb", + "asset": { + "signature": { + "publicKey": "0387fea8e631fd122838f8fcde005995f55865775b0ff9544f881245924873b5d9" + } + }, + "signature": "30440220772d2631aa0628899ca37376cd875a0ec1154c026c7e533687aa3cbefa0f7efa0220577d0cd09926e60e3d57d886050e282f6bafd8abab239bc5dd168bf48f4dae58", + "senderId": "AHpqfxf3EA6L4383nw2rq9i5GqoNJadjGD" + }, + { + "id": "6a932b3ff1e7afbe114138c3939ee91b62a16ece4c8ec68722c0b7ef21fbc524", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02fb92a875f324b45c5168b0f19c4cd9f23041861640cf13abf07c8919e4754c31", + "asset": { + "signature": { + "publicKey": "0264f4c774094302809ec7674ac4b113174c2d937338a4fe175300f96882dbf385" + } + }, + "signature": "3045022100fd3fd1eb2f934bb90c7c292de1abcfe5c73547654a815ae5fbb3f6de4fadd28502200e324eb03390b20be3668ddb53cf508393728003febb46e21f5266a48e04f2a8", + "senderId": "AV184d6W1B171mb4KkXbmM5nhRQYf9qSYJ" + }, + { + "id": "7e1c44418686926e9f19d17a0799173d8f5519cab9a12eb4b8e906e27a066f5c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02aaafe40f4c7b541084802f4fc3d6ffe8e26061478898c3589d449e83de80dc91", + "asset": { + "signature": { + "publicKey": "03ad89ced7870b796e5c2ddfb4538f5c65d2d97ce3c9fe9df40758622027d2231c" + } + }, + "signature": "3044022033a002c1cc78fb2d1c907de9f5ac8379f7aa10df9c49565aaf7321c35bfcfe66022028a7422a109b87e5e085a75195db9a8ad7ccdca9ba6e58f183f297fe12019d2c", + "senderId": "AHo8ciM7i3ro99RUcZMPV4Ytb5Esq8Xs1E" + }, + { + "id": "626d8158749179c7b3dfdf6baf34738e4ec2b6559a2fa3035a72db6b4f1ee52d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "025acf14ffa2ec86954c252784cd6e3931bbffa5ab8a490afb36ea250c01b72694", + "asset": { + "signature": { + "publicKey": "02f29581ed2bbb9e124c925ee5695a327fc52579bb0ebc39db32d625ca527e8b19" + } + }, + "signature": "3044022029ebd1d9db59978d59559b408770b738b80198ddc5020e523ec176003fb1e89a022074ff4088094885b1da1d44e4c4966b757c223908d994b879e27388e6924a96ee", + "senderId": "AWRDMAnZgZp5QEKkQ8fH8E26wtTgwoNUe2" + }, + { + "id": "ebaa86c90419ca638c27b69bc9da17675b03d2c9b123893226845649972776f4", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "028e16ce65270805eb06a1671cc5ddcff9f8d8ffce13387118fa5ab0fe39616052", + "asset": { + "signature": { + "publicKey": "02e5c23a1023c43f52f3b4d142072f851bebca3550b4dd13b6462af62724d8ee0b" + } + }, + "signature": "3044022034415ce70b229515b468865c5097aaa033316f3f6eeca2ca89e9483215f5c9c502205b711d05ed7636e01bb52735002838888bb2685a8a1e845b5d537de050257ce8", + "senderId": "AKR9GXZGUYqz441D359coxztJonviA2FRM" + }, + { + "id": "e48d3f6d60e72f705d923730ebfa58b6f8dcdb0502fd647dfee17d198787bb5c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "031639a30d2a92746da86629be2fa190cff2ba2871a4fbbee4badc9bfd466a0753", + "asset": { + "signature": { + "publicKey": "03a9baa9f916c6469d220266b6911c9feffb00734bd4a3bbc1cb09a98f0ef26ab3" + } + }, + "signature": "3045022100869a1abca4f666df149ea61f7c22a1f1335a008414751df91c677dfceebea48b02200e2c0b403f9402a12fe5f40a5747d3372aba29e6ed994b6617640f8ed189f90e", + "senderId": "AeQwXXZEfMkp2zB9pUJzM85wn52EKX7cE8" + }, + { + "id": "036eee5579480e1d4cb8fd33b4feb2d6b36ca87fc471b43ecc1e4878790b1a73", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f453fe29be65f3ae31b5c68554e777cf0ae6422fe48e598784adc293e0d1faad", + "asset": { + "signature": { + "publicKey": "039d12faf1218abc7ab623785367e4c4513678adc175940c5cc01283049d4ae0e4" + } + }, + "signature": "3045022100bba24cc848902ca32bb4a56e8608c23c3a044b1b2cf4d0474fda127a3b2e1b1e02202153cc6058645ecefe945e05677d29bb4dba0a44a72610f2e92a106eabaa4074", + "senderId": "ASTDtbrw1i53dbMyaTr6WT4XEbTv81e7pi" + }, + { + "id": "135d0cc36c89ea8155d27dad38112c51a84bfca6ba4e66d849588d1388e1973c", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02a146b249c588574ff70a2f40ccfd27b6cc324209f8858a8f7b5063930d869756", + "asset": { + "signature": { + "publicKey": "0387dc56838f7a454ffcd73aa44184d8b8280a76319750758c59ef1d830296670a" + } + }, + "signature": "3044022000f885d9fedb98502b5b234da63c7960927677cef71b4a3ec9010de199064b4702203c0f1e103cb2a7b52a4a791d14ab1fcd9a278e841ab9fbe6c1d8a4307eee60c5", + "senderId": "AH9qyvot8HnjcVCvz3s3vCZ3rgg7qumnj6" + }, + { + "id": "aaf4767bd15bb77c4efbd09da9d3f63f4a0389488bcaa5debba23e2d29f365f8", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02dec8953912902e1ea7ca3c6d994b99dc0156165810656d1863e0a239a7ff2f72", + "asset": { + "signature": { + "publicKey": "02efb7cc98087861c1139edbf0cefd075750a110dc7e076de77812bbde9db70615" + } + }, + "signature": "3044022051bafbe54508fd5ee33d0603c510e753939597807980705a031f6cd7f2bdd8e702204b4d83622763db7837234aac353bf902d92a13b959225c9ba297698cf50d303f", + "senderId": "AV4dc3U6awxGiRwxSEY4cgNSwNReBqeKFj" + }, + { + "id": "96e8897859172603446d5643791b2b9dd82ae6520ca3faef1bac25a52717d4a2", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f4a9e1c98fd828d93e96ecc133187174c5d91566d09fa2701fbed36ac70fe445", + "asset": { + "signature": { + "publicKey": "02ea3a03e92f9c23e9c9cf5fd35c5a56233a1295f10b60c31d26bd36059e568933" + } + }, + "signature": "3045022100cd70b956d91c4f50b1d30434a238c44d334a91f503d3f52ef70b997245019fec0220503427277d6396b1ab4f41ffdcfd5551f1c010af0c572aaa0dd5a0c95d445532", + "senderId": "AVxjxrm3bnGSf4yo62f4bbLBWFwAeNhvTq" + }, + { + "id": "c59fb20f57fa13ba4524b4c6ff55f15ede3042cc99c03b7ed5f7eb51ad4f09e2", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0363a68f9cf32ad3d323f4f09cbe883bbb45c8e419d0e54e519a665b135d9be7d9", + "asset": { + "signature": { + "publicKey": "0210f73be877105655825d824c7b89d65e07c09ab4cd8ab5870a5b3050a5a46516" + } + }, + "signature": "30450221009a56f67b2210fec148fe11bd0ca24adb4e4e5bcc82c8d8ea77c6136921d0e46402204902536f1afba01e25cdbdf929873fece0abc1f749410b6bbce03c201e1429e9", + "senderId": "AK9Adi5y1xEftjbr1WVyRCqUuptKsg7xGj" + }, + { + "id": "b98137e28210c509e00827ec41e51487ef535a5af21bf71c664998de29ec16ef", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "020c8b7e3c16f5a1819e047f4be56f7a95b23b1eeac9078f3917016c6c1c22ecf0", + "asset": { + "signature": { + "publicKey": "03f0342418258c74ac75ab2329939f9b897447a79ffd483825d977ac4fba5acf6f" + } + }, + "signature": "3045022100dac7ef8efc848cafbe323c0af0234075d6d6946fa0748382a6e805965dbd2eac022026703ae9bd88e4af3c5c7ffebe6044582ee853b880312bbf5b8931ad9d7ac62c", + "senderId": "AX9HUjeUfgZieGsbpkKmUBJLqvZiC7N2aL" + }, + { + "id": "c1b9d9e12df90c6020e815267c2fa6c9e5c3b2a074287fc3cc327766fb122f38", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a7d7272168c98037e9c3c01f5b4b6a53eb88efb06057bf82785bddae0ade39c5", + "asset": { + "signature": { + "publicKey": "02cec2ff64ac4173530ab9d171ca8965c8275ac3bbbf00ac9284d716d407098fbe" + } + }, + "signature": "3044022031e4303e55ff6d0e4e69cdfabf8ac0c284d4a26c0faa1540b789c1fe83ebe9cb022032faa92f33bd2ade0e8ec8a28781e5ac0203a832fc9a707bae06bca61a75856f", + "senderId": "AbJyaYJYZYS4uDwAy7sfNTTcjT1wUnVCew" + }, + { + "id": "3ef0769da2b23e434c33364e4f43384caf3a3c08702f2288b0be46178632a346", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0294f19b7b5e338dc911810257a89277a487f35b0dc146fa78c2015d2489ba0db3", + "asset": { + "signature": { + "publicKey": "030f9f3e6fc33a40103e14d9981023b21b8f49ad95ad39adcb17449378cbe8ea24" + } + }, + "signature": "3045022100d107c6b7419eb3d16a4e3d6560132ba9376f2229850f3d8a9e1b77ee773506f6022045f37269fc1fb3a1cd6fbd467823ae4800bd972fb903f26b81622960990ca636", + "senderId": "AKTZGxPzLsr3jeaFbBRFJShzrBUpEZHbBA" + }, + { + "id": "e47ed307eb694798005c0f378002e83d201d6f184467de1127479f43478d4bb7", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02c48118c725c269653cd46b2836ab1c2d8a89ad7ba214b0b309eec5174b7f590b", + "asset": { + "signature": { + "publicKey": "035317f6d364bb4b61a89deaebef594f5285db398d0cf18060118b7b9625a34dad" + } + }, + "signature": "3045022100bc3414e2a2a411d40409b859e06b3b8a35682648c144eb1906ccd70ed975975102201fb43226e7e9b5fc1960ba4ae2157f0bf1e9a848abdded6e29ddcd5830a5b02e", + "senderId": "AR7wLy5nU2kML3Wv9Fdot2RdHAZXn7ftTL" + }, + { + "id": "425cb8ef0b1b41aa6bba2bf53621a4c874ed00ad08ff026e4e61ccedd249d0e1", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "033401817bf6e94157e8c6ee248e39037d28e0b1fdb1cfab726b06220323e0f29e", + "asset": { + "signature": { + "publicKey": "03037ed3a91937416c47e4bb8366437758c5343c0dfa672a8dcab592754792fe13" + } + }, + "signature": "30440220665c621aea56ba29768176b98f409871a1ac8a01c41e667a57552c814f96c297022028a05983e61c0f7d93bff39e226f7e52c3d663e185fe27d9c36276a131eed19c", + "senderId": "Adm62zHj5cUuJQKsbrz467exSVVgk1bH5j" + }, + { + "id": "f82d1388cd7db45d81d6b7573e5952462f8a41423d28fce2231dd25ed5d4eef5", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b4e3968198a714e9a2c81adfde1211a24fea24c578a7033312429a693a4e93c7", + "asset": { + "signature": { + "publicKey": "02394123b84cdb74f3e783523e372db0506245c9c73ced73ee93e53b06d5c2de80" + } + }, + "signature": "3045022100b54fe851008f68eafa42b5df55a10a9836ad065b927b2969a06578f4dc5c65dd0220588745a145c3bbf0b2dc001127c8862c435c7a1e900721f8da49d1897bef863b", + "senderId": "AGJUdoyjXsfQNsDa1G2x3smFfdJEPB8kyn" + }, + { + "id": "7869c6f8f682e33db9b32fa0e40370ca7d36914a1a282903878268ac9dec42f8", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f238b9dd9e43216c1e63390eb50adf9519d00c00541c29cce98f12ed3bab55fa", + "asset": { + "signature": { + "publicKey": "021093f23f1f74ba7c51f65f544c310be34014e348790accb81fc88f4f96a66056" + } + }, + "signature": "3045022100db22661430e150600a394a15366afab10d9ba47bef72e9a88af052aed0a0d952022067e66f731b35389f973525316729e37447bf8dbc2ed77bde30775c649c69ebb4", + "senderId": "AK8aUnLmspWtccNwDEciSBf2BoxmpRPAow" + }, + { + "id": "fe60ec2f0a4a8f33a168a8737962e17f421e98ad22425d4600d261b5867aa148", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03fcec41228c64a39dffbbe8466b23bc1db40efb1970e32de0360cc828ba953883", + "asset": { + "signature": { + "publicKey": "023f13954139fb1783ea9290606f2bc4973f697a0798e6b0976ed699662f11b045" + } + }, + "signature": "30440220020ec39a9b9ccc91e7e0ff98055010a2da3c08728529f2d85b96444cea16f474022057ec7c1cd5fb7947edfcb6175dc50b22748da8e2fa1af4ae90a524137fb4771f", + "senderId": "ANLm1RbdAqNcM5PMBS99ARjHrvBBfTykZM" + }, + { + "id": "3c7bbdb21be84ad8dea2c712bbbda704c0c33db905023ccc1f62d81972d606f5", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "037f4d9190cfa3c916820a3d410010226c399f6b4278eb07ecf22d41904c1d1217", + "asset": { + "signature": { + "publicKey": "02de982a6ad5c8bebdf3edce638747a4dfb065be8f631a3d20fbb63d172bf36cca" + } + }, + "signature": "30450221008bd6e023179aabac29caaac75581bd7daa8085ebb99a7ab0fc7dbc533728645b022032411ff25b2c0f33978e7cfa838f2e506e6adea90bb0d89530f5150b805187ff", + "senderId": "AMGsRHFRL8V6qbUz115q1Wc8pnXBScuS84" + }, + { + "id": "1daa6e3e318479739f39ed7f6d9a165ad459756d89df7de46ff7f101eb0287df", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "023244209672266ac90bf64d0fbd9903028c2483b33f3cf1a0de41822a3628c749", + "asset": { + "signature": { + "publicKey": "02687dff0ce2d481f69d27bf91b372ea1f215b339c6ceba900d7be430bfb2ca564" + } + }, + "signature": "304402201da228d0242749002721505a3db42435e4ade24621fe72c9d40d4926910bd19602201f871d5834995752d2419f05b2eadeca3913ea1eeaa5f29d183d571f2cebd047", + "senderId": "ALsRw7zX6PeYFVqHpJYeVoeBy3a5mjXCoL" + }, + { + "id": "a66c450fa242b6bf2ebed8912f0b69ae2ab3c0efa73e3882709484ec2dee0f2d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0384825d94953e85e3b6c3dc5ee27d363fec7c6dee2c1baa83dd6760458e05a402", + "asset": { + "signature": { + "publicKey": "02805584f70440601dbe3a89ecbfb3d93282c3acb8b2d3fe8dafe13c03bd4fff0a" + } + }, + "signature": "304402202b1219ffc989d3dca8ece9b0021da0dacde95ff2578c60b5c1270ce693ad3fc102201305aa4f510910f922ddd3e2652a19f84af8a67c65713afd755694bd93c25630", + "senderId": "ATHYUiLRifNafWrFLAVgQYo9dVxntZkow9" + }, + { + "id": "6a2be03d17d88936c9818f41aa474a2102dff7beff1760c4673cad20d3924276", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0264d678a13dddf54000ef95a38be00aa6e8698d82cbefa43c5c35808749d5053e", + "asset": { + "signature": { + "publicKey": "02ce8de343754d5b22fa7dfb72c383ba926b6b363e524cd35323f43090cf7bd2fa" + } + }, + "signature": "304402206e2dee8deec4633e10389937c2959c7ecd1ee457ab0ac9d09570e516c8f7139302205b1eb9e49c142d5a38294a8af570c14fe19659c9553a19fe8795e3037791e636", + "senderId": "ALf5oWGQhq2xbTSZyCfDuvxbnDSjvYqiKE" + }, + { + "id": "cca052b5e4a0010ee301ba166faeba8451bb8f4f5228c61cd39e6d1555227424", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03412d936ec21e8b69f43c4c4945e24defeb956c777d71b974740fe2f1b90dd4d1", + "asset": { + "signature": { + "publicKey": "02ecc545862c025a70bd0b1c1a2dd2798a46b64976d2a30670911d645fd1ae799d" + } + }, + "signature": "304402201aa22e71c5903b1c9c933e25af1290dea0cdf928ca9b5bdfd69228fcf0fccd370220524d005d9a92eb65e3c1b12ba1a856377902de0df8665c721bb36c14cc91299f", + "senderId": "AVQ7sUZaTDi6MgKNuNxzzC1Sn3ee3KjdNg" + }, + { + "id": "34aebfa8eddceaf9f55d82aa992753690ac46fb946f520f69f5281b89e763a8d", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03882346ba5e7e2874d1cfd1ec8d30f984be9f57021b72f56ec5b4d4aee79caebf", + "asset": { + "signature": { + "publicKey": "03099b500c089faa04a89ef29f23da9f056f0a217d4a4966c6f88b49719313de1f" + } + }, + "signature": "3045022100c700dd147d1794d20023537c280334698c7122e05bd664741e8adcb1c2d0e77f0220529a8868e1044c524133e8a23461ce76983f1e918b1189bbee80140c4b2b5066", + "senderId": "AFyrB1QJSh7enu2JpMUYEKUgv3xnJo5gUJ" + }, + { + "id": "faa3968c129583d6e46d1e3ca4cf4c912a34e900c5c0fd76053e32c81b2f7a8a", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "039c0311d587b5bf354c2d42965436048dfadaf9d5f6fce7c5f842e5480f298c5c", + "asset": { + "signature": { + "publicKey": "029ee9628cc63f01c63b7537688bdad4d51f10211e1ceaf6810bd046c7bba5c8f2" + } + }, + "signature": "304502210084d61fa2e3afa0b17c4531196571a58e6c595ea94a12b47934ccbe24c7ad49c702204e1d2d02eb38ba653e23999d523b769bffca9466ce1992b066c8bd9b8a62565c", + "senderId": "AL6sZNMWm2VGABvMX4F6eVFLUQJQyJup6i" + }, + { + "id": "8e29afde6c611dee82ca0e3f089794fcde2f928af7087b5c9d5b3f1b78f7d8b6", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f4fc615d167b6e6e4ffd2c6d63ae7ff621341dacb8b0ab5809b82cd478304bc2", + "asset": { + "signature": { + "publicKey": "02ec72b44b00d8125a0b9a8baeada6d0d2642ff6c85885284b22b89326bef0fd78" + } + }, + "signature": "3044022062a8769e33ee2d25e559c88f6e0e23981094687fe9590cd699d19c5003bf51cf022028685a95e7886123e301677bffd63dd20d024e5fd9e90e54c5748abe41faf2e7", + "senderId": "Abu8suR9L2tD7F4gAGHMaQqVbdvXdCFfXG" + }, + { + "id": "1ccc824939323375f439978517cce8e551a9e1e9a30fad3828dd985db2c8de06", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02d59edc9009c55f4b271e0880ccc2e710eff3a69b5e41ee8f0573d93c639e7cf2", + "asset": { + "signature": { + "publicKey": "02c4da308fb28ec9f23ddc8122115594b7a3dc407e341359ae1d0f4457d86b3175" + } + }, + "signature": "3045022100b2f628b89a6c41edc6441546c93ace5aa289d7a5bd4731d3a58f48d8a592048702201282c7c3ecfb6be23001820c88fbd2e84c34dc2de8f65a73e5bd5bcf5bac3b70", + "senderId": "ATMnnCBtz4mYEi5oEqkCnDWk9RkaeNLvw8" + }, + { + "id": "671a995a541bfeae7c908fb8f3b6d4ba48bf8868d69cd07d69f1b9c4431d35db", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0223566d2ee34ed38a542e6bab5aa82feada724c86322a42ba544ac0a0d3cb90be", + "asset": { + "signature": { + "publicKey": "034ae1df2c1aaf5cc67cf6952297fec13711a2753a13e236a3ae617c1cb4800960" + } + }, + "signature": "3044022017df39dd35a0fb79d8b3e1e192c2ffe5c84546cceaf270811b48a2558928981b022046daf555ada51be01ff1e4528afd118484b28f9f6b88e31af19121df561c0f2b", + "senderId": "ASn4LHMaH819aCuUwynTBZsaoUwG3uxAhC" + }, + { + "id": "5f3a87e8640dc16175ed464fbe8c3a92e5769bf5d868713e0bd9391c92057888", + "timestamp": 0, + "version": 1, + "type": 1, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032813004bc61bb9125232369724c23f5d0c417f1690f0beefdae359694ed49eaf", + "asset": { + "signature": { + "publicKey": "02600925a8c12aa769aa81222246fe1b392f68c08e3cfd6cb014ee472797c4d9e9" + } + }, + "signature": "3045022100fe872625e0df7ce1a61bdd5e8b0a771a6ae71d8b4d74389574f2b2c093b1222b022008242b52199db1af7f2484cd47730ee7afc4cf496d762139219ff4bf0353dc95", + "senderId": "AYzQYGbwgcmrCgZM3xx5Mv7E9j2TEcAhAn" + }, + { + "id": "06fb5269ff12cb471119788c455d63c3decc9d04d58f0a15a6839d5ea97a1a78", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a6a69602037a74be19103d7666270ec9898fb25ae9b1a1d7be4e2e3c7c21a4cd", + "asset": { + "delegate": { + "username": "genesis_27", + "publicKey": "03a6a69602037a74be19103d7666270ec9898fb25ae9b1a1d7be4e2e3c7c21a4cd" + } + }, + "signature": "304402207bd968f7154cfe278823f6d4316f42126da1961fafd9125591212638d18589bb02204b04af0bb3980d4ff28a177d683933bc3515e5e754cfd22305264ac67ce61c85", + "senderId": "AdyH7jMm8yK3QpucKYbrxWJBFfekmSkNtj" + }, + { + "id": "7e383f2c52a79957bef0a177151769a2f73bfffb02500bf80d37c4caa9a730f5", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02aed6613d71afac9c58f6d7dc50a8b83ef970a965134289b5fdb1316cb5ce4043", + "asset": { + "delegate": { + "username": "genesis_2", + "publicKey": "02aed6613d71afac9c58f6d7dc50a8b83ef970a965134289b5fdb1316cb5ce4043" + } + }, + "signature": "304502210098430311b5d3284b92dc2f5a8aae2ec641973a88cdf4e70a294b799d043a26d202205900c0a9a701e77bdc15131b78b5f9607355177d1716e636c07e00b980179c94", + "senderId": "AQoAJwxR9AzoaJfJWiuJ1S6mvLHfKtY5BJ" + }, + { + "id": "6c7226c00be70e01178baeecc39b91b9949ef2a48f452e7c2c0e8e057a5ff952", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0224696d2d359eb27ba7f303e2cdad6b205b16958887f297fbb1006862733b93f4", + "asset": { + "delegate": { + "username": "genesis_50", + "publicKey": "0224696d2d359eb27ba7f303e2cdad6b205b16958887f297fbb1006862733b93f4" + } + }, + "signature": "3044022052a4a69430fe6bbfe5248a04d228445ff304341d7c3c2a71ecc89b6681521177022037f3c06b993ca2a85b5fdc5886f43845c15170b42b4779a4072ff69f890eed1b", + "senderId": "APM5mRCaLXyifdsfmd3x2SKdnq324aW5xT" + }, + { + "id": "75154a668d529af7b8873acc7cca19ade919e75c94caf3747d6d980a64f8b18d", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0386b0158ffb50648b980feb1cdf8bcf7091c55b46f46a76e54ea53a31b0c10e25", + "asset": { + "delegate": { + "username": "genesis_49", + "publicKey": "0386b0158ffb50648b980feb1cdf8bcf7091c55b46f46a76e54ea53a31b0c10e25" + } + }, + "signature": "3044022078bb479258bb419cfd683751913de3c34de6b073a7bba4c3d625e34aa879774102205cce0a6e1a781b77c3380d2bcc16cdb527f79c8782400dbd6f6a0922cbc163b7", + "senderId": "AJ1bRNQ4onQPvs9AMWoKtheVStwcf528zh" + }, + { + "id": "636addbe1c5dcab66f4cf232d433202fc1eb12280119a9e023bfd0af557af441", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03eef7da2ddb36f2becd143e94b96c9bdbcc855dc3a635f300473b692a27de1d0d", + "asset": { + "delegate": { + "username": "genesis_48", + "publicKey": "03eef7da2ddb36f2becd143e94b96c9bdbcc855dc3a635f300473b692a27de1d0d" + } + }, + "signature": "3044022020472507dd7cae4477371c31d44bc158a784d644ddec9778f0c3eab7c1eafe8f0220478573d95c64ca73595826ecac04bbfeb94d228a38588facf6ae6031ac69f304", + "senderId": "AcnNYB6HhHzfwzfyquZTXmn9FCLPFsXugf" + }, + { + "id": "57878a82635004330c65c383840322a03d1b5ee3f23140bc767b8af1e5f502ec", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0286f55873cbda98f6b56d6b63dcd837c86ee2e8effd329a99ad9f724ab913e71f", + "asset": { + "delegate": { + "username": "genesis_47", + "publicKey": "0286f55873cbda98f6b56d6b63dcd837c86ee2e8effd329a99ad9f724ab913e71f" + } + }, + "signature": "3044022064c97e36960a2de0dadcd777747bd826259c469e993a68dd1e32db5ac8ee8d7b02207b2e0b8356e88c12de6f8bb695c44dc3c8ba5011dcdb884234d0bb5615e123fa", + "senderId": "AJZeVY3wgx3VwbPs9MrVXWz1mK8quD6omE" + }, + { + "id": "73e44bc7a2f996a6b545ec3941626eb076e51beea67cac41e86e71cd01143171", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "036627ad7e990668b92abd09cd34cff49737ee5024f949b3cf060104bc26796dfa", + "asset": { + "delegate": { + "username": "genesis_46", + "publicKey": "036627ad7e990668b92abd09cd34cff49737ee5024f949b3cf060104bc26796dfa" + } + }, + "signature": "304502210082371253e03d6b78ab733453f8b3271c6c8f016638824555d09c3494c1050e85022064249d21ce7253c62e60de0a1f2b98ecb4c9eec1cd08b4e68a82521923a6658f", + "senderId": "AXYAfJVjupjT29R7g7V8ZQXGtfBAYnMFZn" + }, + { + "id": "60eb7247c6d15dc51197109dbed260ff53164a01f12000d6ed49b32a115929e8", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0261861fce10c2d7329a7c5e9436ddc9f6e4cc7638a0313c365315d71c370aabcc", + "asset": { + "delegate": { + "username": "genesis_45", + "publicKey": "0261861fce10c2d7329a7c5e9436ddc9f6e4cc7638a0313c365315d71c370aabcc" + } + }, + "signature": "3045022100a8b13c392f5b0ef3756e050af4502d565f93f0ac48ae55e192cc1ebd666ee15a0220049354377ba9b159c213b8a68c675f1e09f16fc93744717730577b4a8051dc00", + "senderId": "AFzhSbX7MMxZEeoz9mNFCvUeNX2iBxNK5o" + }, + { + "id": "39ca94093ca3d0e9074b88268f7db06613aef065021079f8eb31e53e2c4956e8", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "026bf3b6a49b53bf2fb4c3f3bf129080ab16027748ca3eae99382785deb9f20e64", + "asset": { + "delegate": { + "username": "genesis_44", + "publicKey": "026bf3b6a49b53bf2fb4c3f3bf129080ab16027748ca3eae99382785deb9f20e64" + } + }, + "signature": "304402204d0fc85a8c3a11b72ac1bcc87d3ceb31ff5a396c03ab4611cfa1fd482d71abed02200843a09a02dd48635d89778d20fa5c7b51478f1ceef93f58eb800ec87bd90ccf", + "senderId": "AdFL6sbWYiJG2AFsAevb3juTiJseEH2rrr" + }, + { + "id": "f0fc54085f308bb8b0eee9f765d6d19c1e05c3a1eb79454bbc1b92adeb56f969", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0340c96462c621b849ed19ccbb43ebc99702b36e39eb31df6427d1040148b3e7df", + "asset": { + "delegate": { + "username": "genesis_43", + "publicKey": "0340c96462c621b849ed19ccbb43ebc99702b36e39eb31df6427d1040148b3e7df" + } + }, + "signature": "3044022026416a12b3486bce56842a71f5ace25fadbf1ae124c3d9f77b1c1b64e41831d502204621759bc9d06e54f4f5594e3b72f4d6dfe7b09075af26b986ffe373836d8fd3", + "senderId": "AdXg3Gchp8XBiPUdKCH9oqCgYSxbezXNdn" + }, + { + "id": "4bb8d39521a1b8182b3917c7f0167094d6878f79da8ee4913c97a6b562a43f47", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0355d22bdc0f63688b11924ae3819b8ef1036f7e74f5ad5f9c7fb508bc219d1793", + "asset": { + "delegate": { + "username": "genesis_42", + "publicKey": "0355d22bdc0f63688b11924ae3819b8ef1036f7e74f5ad5f9c7fb508bc219d1793" + } + }, + "signature": "3045022100a28852e9cdd300a702122ebd211e6f2a94935dabd6c50b8723ccbdda361781cd02207faa2db4c25cf427b77cb7ffad058014e02f0a802198b627f3717ce6ddd0a642", + "senderId": "AJ9MXucUFg8wqPCNmd32iUZmYa5roXhYiW" + }, + { + "id": "d8f77f20641273d42c747722749bc886b702dea8382ae8387a51cecf91919147", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "022f5905a84c78ab156196e220c4d816302d8ff121dca90d19c43b026c17a648f3", + "asset": { + "delegate": { + "username": "genesis_41", + "publicKey": "022f5905a84c78ab156196e220c4d816302d8ff121dca90d19c43b026c17a648f3" + } + }, + "signature": "304402205c4a9c488fd269a10629466ed70ab6690ca8b5f62a0d748af17260919d75aafb022006ae186e2843af542e6cd7e4f70d2b7d082a954a5a27ea51447ffcfa70f401cc", + "senderId": "ARyFEoh7kXmPjDjZtbbGQEm3o8rz9bxmGa" + }, + { + "id": "357fd501799d9402daa580449c1a977ff4ecc6186f36d4188eb427d1da56ccfb", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03743e523107e718ef752267f983fb5d97d033225dede131c1b910c842c6845371", + "asset": { + "delegate": { + "username": "genesis_40", + "publicKey": "03743e523107e718ef752267f983fb5d97d033225dede131c1b910c842c6845371" + } + }, + "signature": "304402201fc0040151885d641fd7ef76d930c7860400c6723564b927cb081872f9d36cb50220174bbf8fab2dedecb6417f5c2c3cc8fd9ef5b31a92805811adea2e5d703aa6b6", + "senderId": "AdYJP7AmU5DJfsmg1Lycc9ytGAbMz2wuf6" + }, + { + "id": "a0564512fed50a4a28d4456cdaab3528a08de330aa0e8318d05de8fe4a91fe7e", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a8e79a66f9abd185f6d273895357bf7f70b9024ea9c6ba6992c70769d2bbcd36", + "asset": { + "delegate": { + "username": "genesis_39", + "publicKey": "03a8e79a66f9abd185f6d273895357bf7f70b9024ea9c6ba6992c70769d2bbcd36" + } + }, + "signature": "3045022100a1140072e432e0b9192d6bba5ed46bc20593af38a7ad538a5321de19815b364f022060018e75a58d0a39eff9e8981beb767b4582f94478f9bd1172903009cc1d05e0", + "senderId": "AQko9usk8N7wGV6VF8QsxXtsqbsm5YdCDL" + }, + { + "id": "c6267e48bd4e167c42c4bf95f459df6c3dcde1a3a9994523807a98ac5f05c30a", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "034e97723d50ff5a208279b95de3fa6c8d4cb719a8097bd42d7c35ca5df24d77aa", + "asset": { + "delegate": { + "username": "genesis_38", + "publicKey": "034e97723d50ff5a208279b95de3fa6c8d4cb719a8097bd42d7c35ca5df24d77aa" + } + }, + "signature": "3044022065c542a6a9817f8835a6f938628d5ecc83b750549564547baa9cbb5791b7ae7902203646d3fc72e097b8296ab7198579bdfe7d412ecb09aa8cfd2b0dbe9318b0b81b", + "senderId": "AUP95N5fHeqCuvXVXQZWZpxm8bj77FmASL" + }, + { + "id": "cc0faa4b591b7b71dfee9f7512f31ebd48aea6888497cb726c3aedbd07ded68c", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032998b64be9fec881c3026a5aca7c6a6a726ff9410cdb96d30296724b2822c49d", + "asset": { + "delegate": { + "username": "genesis_37", + "publicKey": "032998b64be9fec881c3026a5aca7c6a6a726ff9410cdb96d30296724b2822c49d" + } + }, + "signature": "304402202ddd18aaa1f7114be4e4ef8c00e05d24e247c8ae7c8d8d653bfbfdacdac9a9bd02206721cf2f013fe3ccba01a97988cec6b196ddff64028f93893ef95b7f5ae19ea6", + "senderId": "AH5S3Pc7it3gEzUUJN3bZePDzrNN6pEjcR" + }, + { + "id": "e31313b483d887d858db651292ae92ee18b980cba27dc883d678517af47275f1", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032bb244b81b8fe941f4f093c44185a4c4e8dd8aa06aea2c89cd31c5cb5095bbcf", + "asset": { + "delegate": { + "username": "genesis_36", + "publicKey": "032bb244b81b8fe941f4f093c44185a4c4e8dd8aa06aea2c89cd31c5cb5095bbcf" + } + }, + "signature": "30450221009186d262e10ceadfa04535278ac4d3498365d26e7ca8c3acb68e2bce9a34179f02203daa360c21f3bc7dc6ac211428f44568019e3b43dafdeaac96fa05891d2c6185", + "senderId": "ATF3vBJKFQPKjNyUxieoArZ8TQgso9GGMK" + }, + { + "id": "bf652a118da818f07efa545ec558e8dd051b45c96748516f78125cb6f459da53", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02e89dbafbcb5b131c7855063d403cc9342b4ec56453c7106d2a5fe09e36fee3b2", + "asset": { + "delegate": { + "username": "genesis_35", + "publicKey": "02e89dbafbcb5b131c7855063d403cc9342b4ec56453c7106d2a5fe09e36fee3b2" + } + }, + "signature": "304402202bdff7b629061c9af9e5ca8c87eff0114e89ddc6085100f90cf161ff8248c94302204ecf909fe1ab09717e29d46afbe360b468d43797a97aec30645caa7887b5c54a", + "senderId": "AXysAKqGRaUaw3XrwXpj91b8CDPY8buvQA" + }, + { + "id": "b2baf710d2146778248d7c05a87bb993ea1dce47a0b91351aafe60debe5f33dd", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03492a56295d3074c25f2fd1aadbb5363bd8b771eee4d3fd1fd900b7dba0ee00e5", + "asset": { + "delegate": { + "username": "genesis_34", + "publicKey": "03492a56295d3074c25f2fd1aadbb5363bd8b771eee4d3fd1fd900b7dba0ee00e5" + } + }, + "signature": "3045022100a0971ea6d4f3ea0488acf6921e01668ad7ddb82b0bb79fe6eeb4cf31ede039f302205c9330a7b539afced21037fd1a05fc507621bf5264295f200f72deb90aa3b565", + "senderId": "AMefC8bZdYZPgYjcnsEBgVqR4ZXGt3HEmJ" + }, + { + "id": "adf551c6dee08ea329d1d0afece90e5c22fbfc4ed05c5355a767b1a4773e43d7", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0254315ae8629f844cf258cfd85ee5143a1b63fbdff674c59f7e24650ab9912a14", + "asset": { + "delegate": { + "username": "genesis_33", + "publicKey": "0254315ae8629f844cf258cfd85ee5143a1b63fbdff674c59f7e24650ab9912a14" + } + }, + "signature": "3045022100ef495e0da088d59b74dcbd7846d72617a03fdd5bc865a3b9914211047de30e860220386f587e904838af31d4c7cf1dde35714944fbe82cffea3b27a088c2f6fa1b3a", + "senderId": "AGPnbhUdoCoqdQWi6trWnCjJ3jxb1NuZYo" + }, + { + "id": "1577b0e2c726bee7f8d32a580e40037470d9e387efdc69a8d125d0a24e8093b2", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03c636148f8fe31233ae945d442cb82bc98a9183bc6cad582a23135b7b5136ca11", + "asset": { + "delegate": { + "username": "genesis_32", + "publicKey": "03c636148f8fe31233ae945d442cb82bc98a9183bc6cad582a23135b7b5136ca11" + } + }, + "signature": "304402202a8db9b5bd862268f1005d2ca517946a338e5c6fb7b478b94891b44e79a1e1bb022014fa1cc48efa8927c47a42708f6e6fe9ab2e61d34b057f9144e07cb4d77b4074", + "senderId": "AJXSVBkY4A65aDLudX8DohM6f7gyzYqLqF" + }, + { + "id": "3eb7ca7b8d2e52dd68da27b54b91573b54cf69c8d6472939a30e67a394592ab0", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0214bd8ac8fcbcb3d04085942a20e8b6294251c5a02e77315d42f48c6ba7c1cd9e", + "asset": { + "delegate": { + "username": "genesis_31", + "publicKey": "0214bd8ac8fcbcb3d04085942a20e8b6294251c5a02e77315d42f48c6ba7c1cd9e" + } + }, + "signature": "3045022100c7d1fe468fea8c045c8c5e8cb874aa65b63e3a4d50d48a2c809921ec674128f50220151cc061c7f80a3740e9a58ddcd342c94f049fed44049e1dc841475b06797e6e", + "senderId": "AKFbuM4jtwfXCTqCJMXin6Mp3tTubSxPya" + }, + { + "id": "20be520c39e7a5fddef6d596153ebbe9acc96af9956dfada460b301c42be9ac3", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03e01363edc5bd7ab7ff7d35485b376ce666270beb620900d928ee3b6a0cb10fb8", + "asset": { + "delegate": { + "username": "genesis_30", + "publicKey": "03e01363edc5bd7ab7ff7d35485b376ce666270beb620900d928ee3b6a0cb10fb8" + } + }, + "signature": "304402201d2bcbffc0d798084495e8ae0380a4fa68d0eae725d608a0c749949e00eb373e02202dc70ddd3a8cadcef099141eeb52b9982103524ff28f08d3e867a2de5505f049", + "senderId": "AP5j7BWDUrncx8QRedbFKrxTmGxa3n4uSg" + }, + { + "id": "923ccd9b5490b0d14afa70797cbfb56f529d12cb420934ed8c6fcd894eb5b2ed", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f5dec188574f0b819cde2c0247b4f072f8f91ddf042d550921bc319050aed400", + "asset": { + "delegate": { + "username": "genesis_29", + "publicKey": "02f5dec188574f0b819cde2c0247b4f072f8f91ddf042d550921bc319050aed400" + } + }, + "signature": "3045022100e8d332132ed581de7471caed3969f28407e8d9b7163bc37364bb62737400f10502207768680444b6d6764f0357b5dab89d9a53ed0c740b3a4eb983909c72b9540efe", + "senderId": "AdAd7MpFF8HYVL5PkZ173wsYutFiWs3svS" + }, + { + "id": "a0398a62b9600d93781a46ef14f04777399c441fb696206c9abee8931f213fca", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03f8b1935b22d42ed3b2127e60775299808437a0b641b841bc9376a2898c4f44a1", + "asset": { + "delegate": { + "username": "genesis_28", + "publicKey": "03f8b1935b22d42ed3b2127e60775299808437a0b641b841bc9376a2898c4f44a1" + } + }, + "signature": "30450221009aa14486ae557e22ebadcab2f7226e0e03b85a235947d772949127478c0ac60a022049452d58b3f1a2f2cb883b6a30d96ee5bdfb103bccbf782f3457a131d12e6f4b", + "senderId": "ASChm8xtb6tHD75zfszZKsscQnjXk5KVo9" + }, + { + "id": "7df3a30f9c5d27618c0d516b4bb5c862aaa31e95cbac726f56e895f2004e018c", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b63f38feb83a17fad2aacf6f953c557f50c94de636b874da14290bbf63f1517e", + "asset": { + "delegate": { + "username": "genesis_51", + "publicKey": "02b63f38feb83a17fad2aacf6f953c557f50c94de636b874da14290bbf63f1517e" + } + }, + "signature": "304502210085d8047d4535228b429ed85855839732dbf91f019fd75d3888b81e786d5e322f02207d3d10085abadece5bba681981cd66969ab6d448100e1445b90961f0dee979d7", + "senderId": "AUqfHo5psb2xs2vzfikJVkJgfUodjreDuk" + }, + { + "id": "db6e50f672232615205267f3d77a260d0a1441654f598f86c2533dfc004d6c45", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02b5957878800178932dab3313d6723bbf506f211fcb83cfb1d5ee22f1274a4c68", + "asset": { + "delegate": { + "username": "genesis_26", + "publicKey": "02b5957878800178932dab3313d6723bbf506f211fcb83cfb1d5ee22f1274a4c68" + } + }, + "signature": "304402206b12d4c69f9248c77f71ae5dd7365cebdad16c03cf4780786dc6bbf886eb64d702206abd91d3dceeee7b84444b9c04a747b122de677ec38c0c2495640e7e6fa268fe", + "senderId": "ASQqwPSZhBpeaMRt6FinqfDRLwQNUwdLou" + }, + { + "id": "be19a8216ce30743a838d5343ab43427a735eb748604a55834d1d0bf27b623ea", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0281a4d86b3393af3011992b3f6be550aac7eee71993e1425562d260e83c6ead66", + "asset": { + "delegate": { + "username": "genesis_25", + "publicKey": "0281a4d86b3393af3011992b3f6be550aac7eee71993e1425562d260e83c6ead66" + } + }, + "signature": "3045022100e48825377f2d2876ee85b36ec04ab55565a821e59e39cb4f42c4ad6333509372022079ec71df7ba0ce104b4194155e954f48053023104a075b8a0d9ee5c273b018c5", + "senderId": "AKcewanzTJGJ6akaUBV1zU8EVgFcZ6L6iJ" + }, + { + "id": "ea72c392f6256e9ccba1687efc5df9a3116d58fe05e0e3f199b39660de0bf58a", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02de366d3711a1a5932022f27d6388e5e75d3c1b8d9440db599b2931b03780368d", + "asset": { + "delegate": { + "username": "genesis_24", + "publicKey": "02de366d3711a1a5932022f27d6388e5e75d3c1b8d9440db599b2931b03780368d" + } + }, + "signature": "3045022100acf8e6c4bfd634100df7b5605110a2187363ca6569bbba16384ce12c79d83b4a022029f58f54ca9313f268b078d48cafb4d6a2f66ecd3dc2f02b768cd36e96421b3e", + "senderId": "APCQx6efoAU2i9aALsmp7VtKcSgteuSwQR" + }, + { + "id": "95338770498b812527409cccf1f864c11b780cd20ab169bcabb995922da8c8ab", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "032292a0c76cbf0d24faf582afa6323dfe5384e3bfd2bdf0310d3107f02b0e47be", + "asset": { + "delegate": { + "username": "genesis_23", + "publicKey": "032292a0c76cbf0d24faf582afa6323dfe5384e3bfd2bdf0310d3107f02b0e47be" + } + }, + "signature": "304402205ab26c60f8eb59a1dcb4f6e89d779df575ab91ce35fe2d04d90ddb0d959532de02205c43da266517a550b23c3929c8564be62ea6818aa9472c7a27110ab89ccb9067", + "senderId": "Abr5cUc7zCXKAyTjz5irn4JcxEmihPG84U" + }, + { + "id": "6d6d335d9f1b399b9ccf888c8bed12a8e77f74eb4c4b15cc9819589a318981c3", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "020d8cc9a44b3cb46316879146eef90584d37ea5af3c2aa0c169a3fd7232f0b282", + "asset": { + "delegate": { + "username": "genesis_22", + "publicKey": "020d8cc9a44b3cb46316879146eef90584d37ea5af3c2aa0c169a3fd7232f0b282" + } + }, + "signature": "304402200dd0dcae3715024746a993282c9ca686953b5ef2c84551e05d5d4e4147595595022017708d03073a8993ba1c97e500ce262562ce8f4086dedf9f47a8e1c9fd4a9657", + "senderId": "APazxbmv7XuY3HGPv78dKBrmVEcwM7WvQT" + }, + { + "id": "fac36ab057bf4bdaa2d2a0820dd4704a94c0d1202563dbb946a64fe141ff3c6b", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0316510c1409d3307d9f205cac58f1a871499c3ffea3878ddbbb48c821cfbc079a", + "asset": { + "delegate": { + "username": "genesis_21", + "publicKey": "0316510c1409d3307d9f205cac58f1a871499c3ffea3878ddbbb48c821cfbc079a" + } + }, + "signature": "304402207ba71a7ce535aac054211ffe95aeb6aedc8b555dc9365a28fb8e657c495c077a02204cc218e73621a06ee167b9093911d7aaca6a15ccb1415655e916f938b1cb0a9f", + "senderId": "ASt5oBHKDW8AeJe2Ybc1RucMLS7mRCiuRe" + }, + { + "id": "598fa34389ac8b2ec8582f988a6c5e6d5a1cbd597669fd5ec379506393ae584a", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "036e5e9b5956c8f56d5ecaef8a24141aa0de681ad89a2318b6c2d4125676576866", + "asset": { + "delegate": { + "username": "genesis_20", + "publicKey": "036e5e9b5956c8f56d5ecaef8a24141aa0de681ad89a2318b6c2d4125676576866" + } + }, + "signature": "3045022100d6d69dae087229727cdb034799d2dff9d83524b87a7c812a3cdcd0585686f1be0220056fa97bc4b37e5515d0c21fefa6b9af3b37243881796c1d1254b9ae74e1982e", + "senderId": "AGuv1r9zRCLhe2Tk2K5r8miaEq4gKvUhwp" + }, + { + "id": "189ccd5e56fdfd5a4c3dc46cec1dc843bacdb83cf26ba287c0a1e4e90de935ea", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02f9ec59435f27d92667e7d6e8e929cb4f71cb441a2b403beff607a0894c3377d5", + "asset": { + "delegate": { + "username": "genesis_19", + "publicKey": "02f9ec59435f27d92667e7d6e8e929cb4f71cb441a2b403beff607a0894c3377d5" + } + }, + "signature": "304502210095ddfb261e2201e4e47ffdc7015b2542cb5e29e93205e588c2b4edb40317698702204d84f60ba494660b1247408f23fa69cc9ecdd039484e5950074a1c50b8e1a73b", + "senderId": "AbN7uVxpy8SPWQeCtF8dJWbHghUDU85NVb" + }, + { + "id": "d8027d83d251f8bdb2750843ecd14401184e7d66c63a8d2e7ed76806670c2e66", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "022cc19322e8a4edd93e5988b775b237692fe14e7475e520c757e1b32669faf3da", + "asset": { + "delegate": { + "username": "genesis_18", + "publicKey": "022cc19322e8a4edd93e5988b775b237692fe14e7475e520c757e1b32669faf3da" + } + }, + "signature": "304402204a90de4583cab218eac30c05e73b63f978072ec102f080946de9abad3bf80fc1022005d19f67a73ac1550ef64e9a17c2384752231700f204caf97eab315d524c7dcd", + "senderId": "ANpBNG2abwurGKggb8MhbiLYaoSqujaUfB" + }, + { + "id": "104e1ab044b094af536e08b372a6b645c42f9428f8faad0e7fcef73051cfe1b2", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0334cc485a8ff9fbf1ce5221153bec8bfa7f2ece3ce25ff31dacfa1eae91dcf143", + "asset": { + "delegate": { + "username": "genesis_17", + "publicKey": "0334cc485a8ff9fbf1ce5221153bec8bfa7f2ece3ce25ff31dacfa1eae91dcf143" + } + }, + "signature": "3045022100a026b68b342e3128caefabeb2686a2144d8de1dfd4f9060835538f0b3e4e078902205ee6b7986d3c9f8e56dd1b3999f7f9fde5c438ef10725d91535e0d345fb49106", + "senderId": "AGYtkWXBUD3ohy7a8Yow2sqEuXZpmUWMA2" + }, + { + "id": "2b874377024000078f489c26802852e71c3bb87c16f6d8334d593cd77e55fe68", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03a21fb105682f83a4149575fadabc16d836fc255d450bf4e15a6f9733a9c9e46a", + "asset": { + "delegate": { + "username": "genesis_16", + "publicKey": "03a21fb105682f83a4149575fadabc16d836fc255d450bf4e15a6f9733a9c9e46a" + } + }, + "signature": "304402201bfba24659a4326d599cb34804496993b2777b4c3380108b3dc180815bca65200220010a5f16c9eef85c2586e4da6b5b92e3b5de12f9bdb993f843741ca8e4872b5d", + "senderId": "ALx5FYHiVKrUEogvVAaBHBWtKdrpY5u6Ur" + }, + { + "id": "340ca8dcb274bb729b039d535509e219639556cbf62afec0ba6161c8147b243e", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0374b638d5909814023679e91511744d8be43e7ea92a96ac813fc54170956b9a72", + "asset": { + "delegate": { + "username": "genesis_15", + "publicKey": "0374b638d5909814023679e91511744d8be43e7ea92a96ac813fc54170956b9a72" + } + }, + "signature": "3044022059d15e6aa1997ee07c16a7992a10d256a60b836be2f66fc37fd6a32a25b06c2d02207cfa37bf469eeba9aad77239f39deb2ba67de12c97224cfbf1c0d9154bb57507", + "senderId": "AFnRoALAnBAd2mZaZYGFT9bBr2Y7re8gCX" + }, + { + "id": "366061dc1f5cf15c1d4881834b18a5eb0a34da527dbd1240099490fa4a6a255d", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0325a3a8faff0d859d0e01d970b6da791c6d40980e6d999ebf50902add2db649ae", + "asset": { + "delegate": { + "username": "genesis_14", + "publicKey": "0325a3a8faff0d859d0e01d970b6da791c6d40980e6d999ebf50902add2db649ae" + } + }, + "signature": "3045022100daa7ff5f6fd68c4be4106eaeebf6d863116ed1b937b6409d936a7d377b76220e0220179adc41168ec44f3d5b857f27d34dd916c6f477ae5ba221a4cd353fb4994463", + "senderId": "AdHgri2tVkUto6CWb8pgsxyW9ouSteJULN" + }, + { + "id": "ec0b8642bea3659d5f3944692a8c18f379cd35ef56e38594592625e8b2ac2176", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0234b0c5728ad84f77c363893d8724c1030449dab292bf50985c1b4826116488d5", + "asset": { + "delegate": { + "username": "genesis_13", + "publicKey": "0234b0c5728ad84f77c363893d8724c1030449dab292bf50985c1b4826116488d5" + } + }, + "signature": "304502210092d9331d84eb19a13bd508cd8850f5653cb4411a7b091a11d08ddaa18a8946ad02202bc063031e9749a42e41ea51846df5b5bac72c3e5ecd280c2d231ba0752007d0", + "senderId": "Af2hTgerw9M8GHuWLC7PjJQetsNKy96XG9" + }, + { + "id": "175db25ddfeb3c1d1bc062f4ffbd7ef34f9158c92c945ef5bffb2ba064427599", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "024fa1bc40902ce33ad42cce978299fdb9cfcd9e550e233713a005cc967fe43339", + "asset": { + "delegate": { + "username": "genesis_12", + "publicKey": "024fa1bc40902ce33ad42cce978299fdb9cfcd9e550e233713a005cc967fe43339" + } + }, + "signature": "3045022100fd6766846b0615314f6d6be650cfcd1249c05b113343cbce868c37dc0c5043a0022070614d7bc62eb391596d5aa783fb3b8f86c44af19eafa7aff950aa7e216771b6", + "senderId": "AKA3HJAnRgJf6pRtuR8zyUXXkAygiY1gYE" + }, + { + "id": "12c83679491456b08126f56c22d2f020cd75a48c4601a5556bdfd0b8ded26a0b", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03bf04879563f0dfee9b9733af248847a03c5b9e89a9a81ca028fc0c44349c7170", + "asset": { + "delegate": { + "username": "genesis_11", + "publicKey": "03bf04879563f0dfee9b9733af248847a03c5b9e89a9a81ca028fc0c44349c7170" + } + }, + "signature": "304502210094283aa8f0142d2cd3e41191f54b13496d74944953bc9d8faeb047aa55e01e3402200874890848d1319d58e9b2c39d88938567cf6b617c841378fd59a032ffaeeed9", + "senderId": "ANTzfMjau4R5cDdYZrbdbgkeXgYaAny7DP" + }, + { + "id": "93d85a8e0f2eab120f5cd708189c891277ba22d21e96e1520b7bb68acb09bd42", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03e03e8ec4c1de10ec8b4fd9269cbf9584927b99fe2585c5cdd8791b06bd806d5b", + "asset": { + "delegate": { + "username": "genesis_10", + "publicKey": "03e03e8ec4c1de10ec8b4fd9269cbf9584927b99fe2585c5cdd8791b06bd806d5b" + } + }, + "signature": "304502210097c4aa5a741b76f425c82c2c9a980bebb2619e538ee7570341ced6121a23de0d0220161403379c8348a4359c3ec555a85d9f29af41ef07620273cfed8fe3c4b82218", + "senderId": "AQVGV9A42N9rBhgvMd7FDzRW4A7nzwzqMr" + }, + { + "id": "7dda93252f1ad57c5f658b9f1bd15724e56ffd1609e2522888617295039ebeec", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03fe050cd6a95d41f5bf6f6286c7e0c8029a12ebf38101821842993a545fee0154", + "asset": { + "delegate": { + "username": "genesis_9", + "publicKey": "03fe050cd6a95d41f5bf6f6286c7e0c8029a12ebf38101821842993a545fee0154" + } + }, + "signature": "3044022044c6a4fe59a94a040be667a6f41c98820abb364b39077bc28ad77c853d7c29f2022053f16744fdc2784ec21dd4a32c9fde339d314fd8efaa7c14e9f9f4764d76e78a", + "senderId": "AV5DuSRJ7o37refVkVcFNkpCbYbsQQFfV1" + }, + { + "id": "8bc5a4be57e9d77b42aef6f0f779059e6ff8736ba7e5fcbf389aff00a6de2d22", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03069b9b610fc540de79e3e3ec75a237a055a0021cedaadde39c260b890eca6453", + "asset": { + "delegate": { + "username": "genesis_8", + "publicKey": "03069b9b610fc540de79e3e3ec75a237a055a0021cedaadde39c260b890eca6453" + } + }, + "signature": "3044022000951abf3d20273b51ae33a4142ca64c4939bcb45963102980ac05352d97d7ac02201f7f6b1c1a61001568a41a20ea8f0c3ceace13ac2b6c6c492b5f5a2320a09dd7", + "senderId": "AZz7W8kf7FZPaHcTT4CHgUiiTUnKK8hxEg" + }, + { + "id": "6ce884f4cd379476e988483ed41c5a5941765972617df44973970d8d3809b60b", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03852d45a7d7657c972fce42344d5be31166be83d227d757980c8edbd4944505ac", + "asset": { + "delegate": { + "username": "genesis_7", + "publicKey": "03852d45a7d7657c972fce42344d5be31166be83d227d757980c8edbd4944505ac" + } + }, + "signature": "304402201eb3248466785fe74fa83a5fb317e8fcfcf1c332e24454f30cb951166622b63b0220073c686386cecd62f81cce1ddb5eeda9ad54e439eeb93dd8e43462b2a6781c6f", + "senderId": "AdvTKR9AT1MSALnaiAmvRBukvomZDj86gx" + }, + { + "id": "dab21d6e4c577eb183ca33a78654b4ddc0d0447184087de10c8db1eda6bf61de", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "03aab25b27a4eedbcbcc8aa4f8e5a722f1ad03db9a107ef68465c6ea1d18337116", + "asset": { + "delegate": { + "username": "genesis_6", + "publicKey": "03aab25b27a4eedbcbcc8aa4f8e5a722f1ad03db9a107ef68465c6ea1d18337116" + } + }, + "signature": "3045022100a7a9139985357e8dfa690be74c76eeffd7e7781cd53e09ec866c9f0bac96c9ea02202c48381a310d671a12d8a5210906d3ba75ba8bec783f5d0661da2defb1436676", + "senderId": "ALCLA26axFSNRsWHnECjAggxbkSEnU2ncB" + }, + { + "id": "ad97ad8e2a19da3a48b784e75ea24f5f8e55f046cbe24657f55bc8a15eec7d61", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02d1a5ad2e0a094a430d5158447b9be383d1415c2678c890e39d2c811e27789359", + "asset": { + "delegate": { + "username": "genesis_5", + "publicKey": "02d1a5ad2e0a094a430d5158447b9be383d1415c2678c890e39d2c811e27789359" + } + }, + "signature": "3045022100f8bf332d13c699f4dbc12446dc7a360096bf0698632bf8374d243fbf5b184f3a022007b806b1081f4555555e69bf99813e14ea900012deba396005d8a9aca33741b8", + "senderId": "ALvY3DscFkU4H5EXSRe59wpE2CHuHRSaqo" + }, + { + "id": "a9d20c478a6eff3e1411c62356258eb625ae3986273b481c8cfc220352d69454", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "025d124dd255096ab185771bd9284c69bfea670a497b04a1bba054f8feda60fd3a", + "asset": { + "delegate": { + "username": "genesis_4", + "publicKey": "025d124dd255096ab185771bd9284c69bfea670a497b04a1bba054f8feda60fd3a" + } + }, + "signature": "3045022100dba697809453a29db7aa55fa2ac912fb94249cde40eed4bc6364ab0c2eb69dc302207fbb59d91e874ff7d89f1f0e43d1c2b631f8a6725518e39bb3946d787bd52837", + "senderId": "AbsErLb34KfWNLV7ChC3QheZpqgxdp4Fxv" + }, + { + "id": "d7b061c3912f7230a48d0e783da2ebf1903b848433c3bddc1417997760f0a397", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "0316b3dc139c1a35927ecbdcb8d8b628ad06bd4f1869fe3ad0e23c8106678a460f", + "asset": { + "delegate": { + "username": "genesis_3", + "publicKey": "0316b3dc139c1a35927ecbdcb8d8b628ad06bd4f1869fe3ad0e23c8106678a460f" + } + }, + "signature": "304502210086b4b602e0ff8cc73c1a74fdc3aa25e81f3786a8d67f90481647e76b93c2c64702206bd52e147c1e247205c8fa0fbc83d6035727d52035cf08df62b29f3a7378c968", + "senderId": "AWoysqF1xm1LXYLQvmRDpfVNKzzaLVwPVM" + }, + { + "id": "bd507d6ffea6cc86c518b0111bbb0ce573ef558e95c5e88d3d70ecbe8183febc", + "timestamp": 0, + "version": 1, + "type": 2, + "fee": 0, + "amount": 0, + "recipientId": null, + "senderPublicKey": "02778aa3d5b332965ea2a5ef6ac479ce2478535bc681a098dff1d683ff6eccc417", + "asset": { + "delegate": { + "username": "genesis_1", + "publicKey": "02778aa3d5b332965ea2a5ef6ac479ce2478535bc681a098dff1d683ff6eccc417" + } + }, + "signature": "3045022100ff6988ff42fef54d0afc6c0e83f120fa128948969ca5074ed95625dbcba526dc0220567856547a4984692a6d56fece27862eb747232d3969435c00c2798efba09c3a", + "senderId": "ANwc3YQe3EBjuE5sNRacP7fhkngAPaBW4Y" + } + ], + "height": 1, + "id": "4881670189836572019", + "blockSignature": "3045022100b0cbfdfabb77b7d431cb7fdc3acd148032898eb6b0026d4e8f6f08f8e5ca23b5022044cfad1c8e0df615b0969c5d1fe4965b2c18e6656becc2d5410c68ed19452770" +} diff --git a/packages/crypto/src/networks/unitnet/index.ts b/packages/crypto/src/networks/unitnet/index.ts new file mode 100644 index 0000000000..baec59a415 --- /dev/null +++ b/packages/crypto/src/networks/unitnet/index.ts @@ -0,0 +1,6 @@ +import exceptions from "./exceptions.json"; +import genesisBlock from "./genesisBlock.json"; +import milestones from "./milestones.json"; +import network from "./network.json"; + +export const unitnet = { exceptions, genesisBlock, milestones, network }; diff --git a/packages/crypto/src/networks/unitnet/milestones.json b/packages/crypto/src/networks/unitnet/milestones.json new file mode 100644 index 0000000000..51c32313cf --- /dev/null +++ b/packages/crypto/src/networks/unitnet/milestones.json @@ -0,0 +1,31 @@ +[ + { + "height": 1, + "reward": 0, + "activeDelegates": 51, + "blocktime": 8, + "block": { + "version": 0, + "maxTransactions": 150, + "maxPayload": 2097152 + }, + "epoch": "2017-03-21T13:00:00.000Z", + "fees": { + "staticFees": { + "transfer": 10000000, + "secondSignature": 500000000, + "delegateRegistration": 2500000000, + "vote": 100000000, + "multiSignature": 500000000, + "ipfs": 0, + "timelockTransfer": 0, + "multiPayment": 0, + "delegateResignation": 0 + } + } + }, + { + "height": 75600, + "reward": 200000000 + } +] diff --git a/packages/crypto/src/networks/unitnet/network.json b/packages/crypto/src/networks/unitnet/network.json new file mode 100644 index 0000000000..68f42e50e4 --- /dev/null +++ b/packages/crypto/src/networks/unitnet/network.json @@ -0,0 +1,17 @@ +{ + "name": "unitnet", + "messagePrefix": "UNIT message:\n", + "bip32": { + "public": 70617039, + "private": 70615956 + }, + "pubKeyHash": 23, + "nethash": "a63b5a3858afbca23edefac885be74d59f1a26985548a4082f4f479e74fcc348", + "wif": 186, + "aip20": 0, + "client": { + "token": "UARK", + "symbol": "UѦ", + "explorer": "http://uexplorer.ark.io" + } +} diff --git a/packages/crypto/src/serializers/block.ts b/packages/crypto/src/serializers/block.ts new file mode 100644 index 0000000000..114af78989 --- /dev/null +++ b/packages/crypto/src/serializers/block.ts @@ -0,0 +1,61 @@ +import ByteBuffer from "bytebuffer"; +import { Transaction } from "../models"; +import { Block, IBlockData } from "../models/block"; +import { Bignum } from "../utils"; + +class BlockSerializer { + public serializeFull(block: IBlockData): Buffer { + const transactions = block.transactions || []; + block.numberOfTransactions = block.numberOfTransactions || transactions.length; + + const serializedHeader = this.serialize(block); + + const buffer = new ByteBuffer(serializedHeader.length + transactions.length * 4, true) + .append(serializedHeader) + .skip(transactions.length * 4); + + for (let i = 0; i < transactions.length; i++) { + const serialized: any = Transaction.serialize(transactions[i]); + buffer.writeUint32(serialized.length, serializedHeader.length + i * 4); + buffer.append(serialized); + } + + return Buffer.from(buffer.flip().toBuffer()); + } + + public serialize(block: IBlockData, includeSignature: boolean = true): Buffer { + const buffer = new ByteBuffer(512, true); + + this.serializeHeader(block, buffer); + + if (includeSignature) { + this.serializeSignature(block, buffer); + } + + return Buffer.from(buffer.flip().toBuffer()); + } + + private serializeHeader(block: IBlockData, buffer: ByteBuffer): any { + block.previousBlockHex = Block.toBytesHex(block.previousBlock); + + buffer.writeUint32(block.version); + buffer.writeUint32(block.timestamp); + buffer.writeUint32(block.height); + buffer.append(block.previousBlockHex, "hex"); + buffer.writeUint32(block.numberOfTransactions); + buffer.writeUint64(+new Bignum(block.totalAmount).toFixed()); + buffer.writeUint64(+new Bignum(block.totalFee).toFixed()); + buffer.writeUint64(+new Bignum(block.reward).toFixed()); + buffer.writeUint32(block.payloadLength); + buffer.append(block.payloadHash, "hex"); + buffer.append(block.generatorPublicKey, "hex"); + } + + private serializeSignature(block: IBlockData, buffer: ByteBuffer): any { + if (block.blockSignature) { + buffer.append(block.blockSignature, "hex"); + } + } +} + +export const blockSerializer = new BlockSerializer(); diff --git a/packages/crypto/src/serializers/index.ts b/packages/crypto/src/serializers/index.ts new file mode 100644 index 0000000000..6e622c69da --- /dev/null +++ b/packages/crypto/src/serializers/index.ts @@ -0,0 +1,2 @@ +export { transactionSerializer as TransactionSerializer } from "./transaction"; +export { blockSerializer as BlockSerializer } from "./block"; diff --git a/packages/crypto/src/serializers/transaction.ts b/packages/crypto/src/serializers/transaction.ts new file mode 100644 index 0000000000..76908ed4d0 --- /dev/null +++ b/packages/crypto/src/serializers/transaction.ts @@ -0,0 +1,154 @@ +import bs58check from "bs58check"; +import ByteBuffer from "bytebuffer"; +import { TransactionTypes } from "../constants"; +import { TransactionTypeError } from "../errors"; +import { configManager } from "../managers"; +import { Transaction } from "../models"; +import { ITransactionData } from "../models/transaction"; +import { Bignum } from "../utils"; + +// Reference: https://github.com/ArkEcosystem/AIPs/blob/master/AIPS/aip-11.md +class TransactionSerializer { + public serialize(transaction: ITransactionData): Buffer { + const buffer = new ByteBuffer(512, true); + + this.serializeCommon(transaction, buffer); + this.serializeVendorField(transaction, buffer); + this.serializeType(transaction, buffer); + this.serializeSignatures(transaction, buffer); + + return Buffer.from(buffer.flip().toBuffer()); + } + + private serializeCommon(transaction: ITransactionData, buffer: ByteBuffer): void { + buffer.writeByte(0xff); // fill, to disambiguate from v1 + buffer.writeByte(transaction.version || 0x01); // version + buffer.writeByte(transaction.network || configManager.get("pubKeyHash")); // ark = 0x17, devnet = 0x30 + buffer.writeByte(transaction.type); + buffer.writeUint32(transaction.timestamp); + buffer.append(transaction.senderPublicKey, "hex"); + buffer.writeUint64(+new Bignum(transaction.fee).toFixed()); + } + + private serializeVendorField(transaction: ITransactionData, buffer: ByteBuffer): void { + if (Transaction.canHaveVendorField(transaction.type)) { + if (transaction.vendorField) { + const vf = Buffer.from(transaction.vendorField, "utf8"); + buffer.writeByte(vf.length); + buffer.append(vf); + } else if (transaction.vendorFieldHex) { + buffer.writeByte(transaction.vendorFieldHex.length / 2); + buffer.append(transaction.vendorFieldHex, "hex"); + } else { + buffer.writeByte(0x00); + } + } else { + buffer.writeByte(0x00); + } + } + + private serializeType(transaction: ITransactionData, buffer: ByteBuffer): void { + if (transaction.type === TransactionTypes.Transfer) { + this.serializeTransfer(transaction, buffer); + } else if (transaction.type === TransactionTypes.SecondSignature) { + this.serializeSecondSignature(transaction, buffer); + } else if (transaction.type === TransactionTypes.DelegateRegistration) { + this.serializeDelegateRegistration(transaction, buffer); + } else if (transaction.type === TransactionTypes.Vote) { + this.serializeVote(transaction, buffer); + } else if (transaction.type === TransactionTypes.MultiSignature) { + this.serializeMultiSignature(transaction, buffer); + } else if (transaction.type === TransactionTypes.Ipfs) { + this.serializeIpfs(transaction, buffer); + } else if (transaction.type === TransactionTypes.TimelockTransfer) { + this.serializeTimelockTransfer(transaction, buffer); + } else if (transaction.type === TransactionTypes.MultiPayment) { + this.serializeMultiPayment(transaction, buffer); + } else if (transaction.type === TransactionTypes.DelegateResignation) { + this.serializeDelegateResignation(transaction, buffer); + } else { + throw new TransactionTypeError(transaction.type); + } + } + + private serializeTransfer(transaction: ITransactionData, buffer: ByteBuffer): void { + buffer.writeUint64(+new Bignum(transaction.amount).toFixed()); + buffer.writeUint32(transaction.expiration || 0); + buffer.append(bs58check.decode(transaction.recipientId)); + } + + private serializeSecondSignature(transaction: ITransactionData, buffer: ByteBuffer): void { + buffer.append(transaction.asset.signature.publicKey, "hex"); + } + + private serializeDelegateRegistration(transaction: ITransactionData, buffer: ByteBuffer): void { + const delegateBytes = Buffer.from(transaction.asset.delegate.username, "utf8"); + buffer.writeByte(delegateBytes.length); + buffer.append(delegateBytes, "hex"); + } + + private serializeVote(transaction: ITransactionData, buffer: ByteBuffer): void { + const voteBytes = transaction.asset.votes.map(vote => (vote[0] === "+" ? "01" : "00") + vote.slice(1)).join(""); + buffer.writeByte(transaction.asset.votes.length); + buffer.append(voteBytes, "hex"); + } + + private serializeMultiSignature(transaction: ITransactionData, buffer: ByteBuffer): void { + let joined = null; + + if (!transaction.version || transaction.version === 1) { + joined = transaction.asset.multisignature.keysgroup.map(k => (k[0] === "+" ? k.slice(1) : k)).join(""); + } else { + joined = transaction.asset.multisignature.keysgroup.join(""); + } + + const keysgroupBuffer = Buffer.from(joined, "hex"); + buffer.writeByte(transaction.asset.multisignature.min); + buffer.writeByte(transaction.asset.multisignature.keysgroup.length); + buffer.writeByte(transaction.asset.multisignature.lifetime); + buffer.append(keysgroupBuffer, "hex"); + } + + private serializeIpfs(transaction: ITransactionData, buffer: ByteBuffer): void { + buffer.writeByte(transaction.asset.ipfs.dag.length / 2); + buffer.append(transaction.asset.ipfs.dag, "hex"); + } + + private serializeTimelockTransfer(transaction: ITransactionData, buffer: ByteBuffer): void { + buffer.writeUint64(+new Bignum(transaction.amount).toFixed()); + buffer.writeByte(transaction.timelockType); + buffer.writeUint64(transaction.timelock); + buffer.append(bs58check.decode(transaction.recipientId)); + } + + private serializeMultiPayment(transaction: ITransactionData, buffer: ByteBuffer): void { + buffer.writeUint32(transaction.asset.payments.length); + transaction.asset.payments.forEach(p => { + buffer.writeUint64(+new Bignum(p.amount).toFixed()); + buffer.append(bs58check.decode(p.recipientId)); + }); + } + + private serializeDelegateResignation(transaction: ITransactionData, buffer: ByteBuffer): void { + return; + } + + private serializeSignatures(transaction: ITransactionData, buffer: ByteBuffer): void { + if (transaction.signature) { + buffer.append(transaction.signature, "hex"); + } + + if (transaction.secondSignature) { + buffer.append(transaction.secondSignature, "hex"); + } else if (transaction.signSignature) { + buffer.append(transaction.signSignature, "hex"); + } + + if (transaction.signatures) { + buffer.append("ff", "hex"); // 0xff separator to signal start of multi-signature transactions + buffer.append(transaction.signatures.join(""), "hex"); + } + } +} + +export const transactionSerializer = new TransactionSerializer(); diff --git a/packages/crypto/src/utils/bignum.ts b/packages/crypto/src/utils/bignum.ts new file mode 100644 index 0000000000..756ac3daeb --- /dev/null +++ b/packages/crypto/src/utils/bignum.ts @@ -0,0 +1,10 @@ +import BigNumber from "bignumber.js"; + +class Bignum extends BigNumber { + public static readonly ZERO = new BigNumber(0); + public static readonly ONE = new BigNumber(1); +} + +Bignum.config({ DECIMAL_PLACES: 0 }); + +export { Bignum }; diff --git a/packages/crypto/src/utils/format-arktoshi.ts b/packages/crypto/src/utils/format-arktoshi.ts new file mode 100644 index 0000000000..2cc9ef91e2 --- /dev/null +++ b/packages/crypto/src/utils/format-arktoshi.ts @@ -0,0 +1,15 @@ +import { ARKTOSHI } from "../constants"; +import { configManager } from "../managers"; +import { Bignum } from "./bignum"; + +/** + * Get human readable string from arktoshis + */ +export const formatArktoshi = (amount: Bignum | number | string): string => { + const localeString = (+amount / ARKTOSHI).toLocaleString("en", { + minimumFractionDigits: 0, + maximumFractionDigits: 8, + }); + + return `${localeString} ${configManager.config.client.symbol}`; +}; diff --git a/packages/crypto/src/utils/index.ts b/packages/crypto/src/utils/index.ts new file mode 100644 index 0000000000..62c27f08dc --- /dev/null +++ b/packages/crypto/src/utils/index.ts @@ -0,0 +1,4 @@ +export { Bignum } from "./bignum"; +export { formatArktoshi } from "./format-arktoshi"; +export { isException } from "./is-exception"; +export { sortTransactions } from "./sort-transactions"; diff --git a/packages/crypto/src/utils/is-exception.ts b/packages/crypto/src/utils/is-exception.ts new file mode 100644 index 0000000000..af37c99a70 --- /dev/null +++ b/packages/crypto/src/utils/is-exception.ts @@ -0,0 +1,12 @@ +import { configManager } from "../managers"; +import { IBlockData, ITransactionData } from "../models"; + +/** + * Check if the given block or transaction id is an exception. + */ +export function isException(blockOrTransaction: IBlockData | ITransactionData): boolean { + return ["blocks", "transactions"].some(key => { + const exceptions = configManager.get(`exceptions.${key}`); + return Array.isArray(exceptions) && exceptions.includes(blockOrTransaction.id); + }); +} diff --git a/packages/crypto/src/utils/sort-transactions.ts b/packages/crypto/src/utils/sort-transactions.ts new file mode 100644 index 0000000000..45c380708b --- /dev/null +++ b/packages/crypto/src/utils/sort-transactions.ts @@ -0,0 +1,25 @@ +import { ITransactionData } from "../models"; + +/** + * Sort transactions by type, then id. + */ +export const sortTransactions = (transactions: ITransactionData[]): ITransactionData[] => + transactions.sort((a, b) => { + if (a.type < b.type) { + return -1; + } + + if (a.type > b.type) { + return 1; + } + + if (a.id < b.id) { + return -1; + } + + if (a.id > b.id) { + return 1; + } + + return 0; + }); diff --git a/packages/crypto/src/validation/extensions/address.ts b/packages/crypto/src/validation/extensions/address.ts new file mode 100644 index 0000000000..fb79c564f5 --- /dev/null +++ b/packages/crypto/src/validation/extensions/address.ts @@ -0,0 +1,7 @@ +export const address = joi => ({ + name: "address", + base: joi + .string() + .alphanum() + .length(34), +}); diff --git a/packages/crypto/src/validation/extensions/bignumber.ts b/packages/crypto/src/validation/extensions/bignumber.ts new file mode 100644 index 0000000000..05bb51afb3 --- /dev/null +++ b/packages/crypto/src/validation/extensions/bignumber.ts @@ -0,0 +1,76 @@ +import BigNumber from "bignumber.js"; + +export const bignumber = joi => ({ + name: "bignumber", + base: joi.object().type(BigNumber), + language: { + min: "is less than minimum", + max: "is greater than maximum", + only: "is different from allowed value", + integer: "is not an integer", + positive: "is not positive", + }, + rules: [ + { + name: "min", + params: { + q: joi.number().required(), + }, + validate(params, value, state, options) { + if (value.isLessThan(params.q)) { + return this.createError("bignumber.min", { v: value }, state, options); + } + + return value; + }, + }, + { + name: "max", + params: { + q: joi.number().required(), + }, + validate(params, value, state, options) { + if (value.isGreaterThan(params.q)) { + return this.createError("bignumber.max", { v: value }, state, options); + } + + return value; + }, + }, + { + name: "only", + params: { + q: joi.number().required(), + }, + validate(params, value, state, options) { + if (!value.isEqualTo(params.q)) { + return this.createError("bignumber.only", { v: value }, state, options); + } + + return value; + }, + }, + { + name: "integer", + params: {}, + validate(_, value, state, options) { + if (!value.isInteger()) { + return this.createError("bignumber.integer", { v: value }, state, options); + } + + return value; + }, + }, + { + name: "positive", + params: {}, + validate(_, value, state, options) { + if (!value.isPositive() || value.isZero()) { + return this.createError("bignumber.positive", { v: value }, state, options); + } + + return value; + }, + }, + ], +}); diff --git a/packages/crypto/src/validation/extensions/block-id.ts b/packages/crypto/src/validation/extensions/block-id.ts new file mode 100644 index 0000000000..43bfb2caa8 --- /dev/null +++ b/packages/crypto/src/validation/extensions/block-id.ts @@ -0,0 +1,4 @@ +export const blockId = joi => ({ + name: "blockId", + base: joi.string().regex(/^[0-9]+$/, "numbers"), +}); diff --git a/packages/crypto/src/validation/extensions/block.ts b/packages/crypto/src/validation/extensions/block.ts new file mode 100644 index 0000000000..d58c2d7b40 --- /dev/null +++ b/packages/crypto/src/validation/extensions/block.ts @@ -0,0 +1,63 @@ +export const block = joi => ({ + name: "block", + base: joi.object().keys({ + id: joi.blockId().required(), + idHex: joi.string().hex(), + version: joi + .number() + .integer() + .min(0), + timestamp: joi + .number() + .integer() + .min(0) + .required(), + previousBlock: joi.blockId().required(), + previousBlockHex: joi.string().hex(), + height: joi + .number() + .integer() + .positive() + .required(), + numberOfTransactions: joi + .number() + .integer() + .only(joi.ref("transactions.length")), + totalAmount: joi.alternatives().try( + joi + .number() + .integer() + .min(0) + .required(), + joi + .string() + .regex(/[0-9]+/) + .required(), + ), + totalFee: joi + .number() + .integer() + .min(0) + .required(), + reward: joi + .number() + .integer() + .min(0) + .required(), + payloadLength: joi + .number() + .integer() + .min(0), + payloadHash: joi.string().hex(), + generatorPublicKey: joi + .string() + .hex() + .length(66) + .required(), + blockSignature: joi + .string() + .hex() + .required(), + transactions: joi.transactionArray(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/index.ts b/packages/crypto/src/validation/extensions/index.ts new file mode 100644 index 0000000000..946adf7911 --- /dev/null +++ b/packages/crypto/src/validation/extensions/index.ts @@ -0,0 +1,10 @@ +import { address } from "./address"; +import { bignumber } from "./bignumber"; +import { block } from "./block"; +import { blockId } from "./block-id"; +import { publicKey } from "./public-key"; +import { transactionArray } from "./transaction-array"; +import { transactions } from "./transactions"; +import { username } from "./username"; + +export const extensions = [address, bignumber, publicKey, username, blockId, ...transactions, transactionArray, block]; diff --git a/packages/crypto/src/validation/extensions/public-key.ts b/packages/crypto/src/validation/extensions/public-key.ts new file mode 100644 index 0000000000..7686920ba6 --- /dev/null +++ b/packages/crypto/src/validation/extensions/public-key.ts @@ -0,0 +1,7 @@ +export const publicKey = joi => ({ + name: "publicKey", + base: joi + .string() + .hex() + .length(66), +}); diff --git a/packages/crypto/src/validation/extensions/transaction-array.ts b/packages/crypto/src/validation/extensions/transaction-array.ts new file mode 100644 index 0000000000..9e4917c8b0 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transaction-array.ts @@ -0,0 +1,16 @@ +export const transactionArray = joi => ({ + name: "transactionArray", + base: joi + .array() + .items( + joi + .alternatives() + .try( + joi.transfer(), + joi.secondSignature(), + joi.delegateRegistration(), + joi.vote(), + joi.multiSignature(), + ), + ), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/base.ts b/packages/crypto/src/validation/extensions/transactions/base.ts new file mode 100644 index 0000000000..00da6eb713 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/base.ts @@ -0,0 +1,74 @@ +import { configManager } from "../../../managers"; + +export const base = joi => + joi.object().keys({ + id: joi + .string() + .alphanum() + .required(), + blockid: joi.alternatives().try( + // TODO: remove in 2.1 + joi.blockId(), + // @ts-ignore + joi.number().unsafe(), + ), + network: joi.lazy( + () => + joi + .number() + .only(configManager.get("pubKeyHash")) + .optional(), + { once: false }, + ), + version: joi + .number() + .integer() + .min(1) + .max(2) + .optional(), + timestamp: joi + .number() + .integer() + .min(0) + .required(), + amount: joi + .alternatives() + .try( + joi + .bignumber() + .integer() + .positive(), + joi + .number() + .integer() + .positive(), + ) + .required(), + fee: joi + .alternatives() + .try( + joi + .bignumber() + .integer() + .positive(), + joi + .number() + .integer() + .positive(), + ) + .required(), + senderId: joi.address(), // TODO: remove in 2.1 + recipientId: joi.address().required(), + senderPublicKey: joi.publicKey().required(), + signature: joi + .string() + .alphanum() + .required(), + signatures: joi.array(), + secondSignature: joi.string().alphanum(), + signSignature: joi.string().alphanum(), // TODO: remove in 2.1 + confirmations: joi // TODO: remove in 2.1 + .number() + .integer() + .min(0), + }); diff --git a/packages/crypto/src/validation/extensions/transactions/delegate-registration.ts b/packages/crypto/src/validation/extensions/transactions/delegate-registration.ts new file mode 100644 index 0000000000..0a4a9891e6 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/delegate-registration.ts @@ -0,0 +1,27 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const delegateRegistration = joi => ({ + name: "delegateRegistration", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.DelegateRegistration) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().only(0)) + .optional(), + asset: joi + .object({ + delegate: joi + .object({ + username: joi.delegateUsername().required(), + publicKey: joi.publicKey(), + }) + .required(), + }) + .required(), + recipientId: joi.empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/delegate-resignation.ts b/packages/crypto/src/validation/extensions/transactions/delegate-resignation.ts new file mode 100644 index 0000000000..1751bac049 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/delegate-resignation.ts @@ -0,0 +1,18 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const delegateResignation = joi => ({ + name: "delegateResignation", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.DelegateResignation) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().valid(0)) + .optional(), + asset: joi.object().required(), + recipientId: joi.empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/index.ts b/packages/crypto/src/validation/extensions/transactions/index.ts new file mode 100644 index 0000000000..ed5f497b89 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/index.ts @@ -0,0 +1,21 @@ +import { delegateRegistration } from "./delegate-registration"; +import { delegateResignation } from "./delegate-resignation"; +import { ipfs } from "./ipfs"; +import { multiPayment } from "./multi-payment"; +import { multiSignature } from "./multi-signature"; +import { secondSignature } from "./second-signature"; +import { timelockTransfer } from "./timelock-transfer"; +import { transfer } from "./transfer"; +import { vote } from "./vote"; + +export const transactions = [ + transfer, + secondSignature, + delegateRegistration, + vote, + multiSignature, + ipfs, + timelockTransfer, + multiPayment, + delegateResignation, +]; diff --git a/packages/crypto/src/validation/extensions/transactions/ipfs.ts b/packages/crypto/src/validation/extensions/transactions/ipfs.ts new file mode 100644 index 0000000000..6e5bba5c10 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/ipfs.ts @@ -0,0 +1,18 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const ipfs = joi => ({ + name: "ipfs", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.Ipfs) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().valid(0)) + .optional(), + asset: joi.object().required(), + recipientId: joi.empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/multi-payment.ts b/packages/crypto/src/validation/extensions/transactions/multi-payment.ts new file mode 100644 index 0000000000..aeae21929b --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/multi-payment.ts @@ -0,0 +1,14 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const multiPayment = joi => ({ + name: "multiPayment", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.MultiPayment) + .required(), + asset: joi.object().required(), + recipientId: joi.empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/multi-signature.ts b/packages/crypto/src/validation/extensions/transactions/multi-signature.ts new file mode 100644 index 0000000000..6d2c96ff38 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/multi-signature.ts @@ -0,0 +1,61 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const multiSignature = joi => ({ + name: "multiSignature", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.MultiSignature) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().only(0)) + .optional(), + recipientId: joi.empty(), + signatures: joi + .array() + .length(joi.ref("asset.multisignature.keysgroup.length")) + .required(), + asset: joi + .object({ + multisignature: joi + .object({ + min: joi + .when(joi.ref("keysgroup.length"), { + is: joi.number().greater(16), + then: joi + .number() + .positive() + .max(16), + otherwise: joi + .number() + .positive() + .max(joi.ref("keysgroup.length")), + }) + .required(), + keysgroup: joi + .array() + .unique() + .min(2) + .items( + joi + .string() + .not(`+${(transaction as any).senderPublicKey}`) + .length(67) + .regex(/^\+/) + .required(), + ) + .required(), + lifetime: joi + .number() + .integer() + .min(1) + .max(72) + .required(), + }) + .required(), + }) + .required(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/second-signature.ts b/packages/crypto/src/validation/extensions/transactions/second-signature.ts new file mode 100644 index 0000000000..7d52691f49 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/second-signature.ts @@ -0,0 +1,27 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const secondSignature = joi => ({ + name: "secondSignature", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.SecondSignature) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().only(0)) + .optional(), + secondSignature: joi.string().only(""), + asset: joi + .object({ + signature: joi + .object({ + publicKey: joi.publicKey().required(), + }) + .required(), + }) + .required(), + recipientId: joi.empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/timelock-transfer.ts b/packages/crypto/src/validation/extensions/transactions/timelock-transfer.ts new file mode 100644 index 0000000000..049203acf7 --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/timelock-transfer.ts @@ -0,0 +1,27 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const timelockTransfer = joi => ({ + name: "timelockTransfer", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.MultiPayment) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().only(0)) + .optional(), + asset: joi.object().required(), + vendorFieldHex: joi + .string() + .max(64, "hex") + .optional(), + vendorField: joi + .string() + .max(64, "utf8") + .allow("", null) + .optional(), // TODO: remove in 2.1 + recipientId: joi.empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/transfer.ts b/packages/crypto/src/validation/extensions/transactions/transfer.ts new file mode 100644 index 0000000000..1db77c6bfa --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/transfer.ts @@ -0,0 +1,26 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const transfer = joi => ({ + name: "transfer", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.Transfer) + .required(), + expiration: joi + .number() + .integer() + .min(0), + vendorField: joi + .string() + .max(64, "utf8") + .allow("", null) + .optional(), // TODO: remove in 2.1 + vendorFieldHex: joi + .string() + .max(64, "hex") + .optional(), + asset: joi.object().empty(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/transactions/vote.ts b/packages/crypto/src/validation/extensions/transactions/vote.ts new file mode 100644 index 0000000000..b8b54268bc --- /dev/null +++ b/packages/crypto/src/validation/extensions/transactions/vote.ts @@ -0,0 +1,34 @@ +import { TransactionTypes } from "../../../constants"; +import { base as transaction } from "./base"; + +export const vote = joi => ({ + name: "vote", + base: transaction(joi).append({ + type: joi + .number() + .only(TransactionTypes.Vote) + .required(), + amount: joi + .alternatives() + .try(joi.bignumber().only(0), joi.number().only(0)) + .optional(), + asset: joi + .object({ + votes: joi + .array() + .items( + joi + .string() + .length(67) + .regex(/^(\+|-)[a-zA-Z0-9]+$/), + ) + .length(1) + .required(), + }) + .required(), + recipientId: joi + .address() + .allow(null) + .optional(), + }), +}); diff --git a/packages/crypto/src/validation/extensions/username.ts b/packages/crypto/src/validation/extensions/username.ts new file mode 100644 index 0000000000..0bff850c80 --- /dev/null +++ b/packages/crypto/src/validation/extensions/username.ts @@ -0,0 +1,8 @@ +export const username = joi => ({ + name: "delegateUsername", + base: joi + .string() + .regex(/^[a-z0-9!@$&_.]+$/) + .min(1) + .max(20), +}); diff --git a/packages/crypto/src/validation/index.ts b/packages/crypto/src/validation/index.ts new file mode 100644 index 0000000000..b316d7add4 --- /dev/null +++ b/packages/crypto/src/validation/index.ts @@ -0,0 +1,5 @@ +export { transactionValidator } from "./validators/transaction"; + +import { Validator } from "./validator"; + +export const Joi = Validator.joi; diff --git a/packages/crypto/src/validation/validator.ts b/packages/crypto/src/validation/validator.ts new file mode 100644 index 0000000000..336c00edef --- /dev/null +++ b/packages/crypto/src/validation/validator.ts @@ -0,0 +1,29 @@ +import Joi from "joi"; +import { extensions } from "./extensions"; + +export class Validator { + public static joi: any; + + public static init(): void { + this.joi = Joi.extend(extensions); + } + + public static validate(attributes, rules, options?) { + try { + return this.joi.validate( + attributes, + rules, + Object.assign( + { + convert: true, + }, + options, + ), + ); + } catch (error) { + return { value: null, error: error.stack }; + } + } +} + +Validator.init(); diff --git a/packages/crypto/src/validation/validators/transaction.ts b/packages/crypto/src/validation/validators/transaction.ts new file mode 100644 index 0000000000..340b7b3384 --- /dev/null +++ b/packages/crypto/src/validation/validators/transaction.ts @@ -0,0 +1,25 @@ +import { transactions } from "../extensions/transactions"; +import { Validator } from "../validator"; + +export class TransactionValidator { + public rules: any; + + constructor() { + this.rules = Object.keys(transactions).reduce((rules, type) => { + rules[type] = transactions[type](Validator.joi).base; + return rules; + }, {}); + } + + public validate(transaction) { + const { value, error } = Validator.validate(transaction, this.rules[transaction.type], { allowUnknown: true }); + return { + data: value, + errors: error ? error.details : null, + passes: !error, + fails: error, + }; + } +} + +export const transactionValidator = new TransactionValidator(); diff --git a/packages/crypto/tsconfig.json b/packages/crypto/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/crypto/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/scripts/docker/generate-docker.js b/scripts/docker/generate-docker.js new file mode 100644 index 0000000000..0201c5d996 --- /dev/null +++ b/scripts/docker/generate-docker.js @@ -0,0 +1,28 @@ +const fs = require("fs"); +const { ensureDirSync } = require("fs-extra"); + +const regex = new RegExp("{token}", "g"); +const templateRoot = './scripts/docker/templates' +const templateDirs = fs.readdirSync(templateRoot); + +if (process.argv.length !== 3) { + throw new Error("Expected 1 argument for the token name"); +} + +const token = process.argv[2]; + +console.log(`Generating docker files for '${token}':`) + +templateDirs.forEach(templateDir => { + ensureDirSync(`./docker/development/${templateDir}`) + const templateFiles = fs.readdirSync(`${templateRoot}/${templateDir}`) + templateFiles.forEach(templateFile => { + const template = fs.readFileSync(`${templateRoot}/${templateDir}/${templateFile}`, { encoding: "utf8" }) + const target = `./docker/development/${templateDir}/${templateFile}` + console.log(`${target}`) + fs.writeFileSync(target, template.replace(regex, token)); + if (templateFile.endsWith(".sh")) { + fs.chmodSync(target, "755"); + } + }) +}) diff --git a/scripts/docker/templates/devnet/Dockerfile b/scripts/docker/templates/devnet/Dockerfile new file mode 100644 index 0000000000..7e1971047e --- /dev/null +++ b/scripts/docker/templates/devnet/Dockerfile @@ -0,0 +1,16 @@ +FROM node:10 + +WORKDIR /core + +COPY entrypoint.sh / + +RUN apt-get update && \ + apt-get -y install --no-install-recommends \ + build-essential \ + jq \ + iptables \ + python \ + vim && \ + rm -rf /var/lib/apt/lists/* + +EXPOSE 4002 4003 diff --git a/scripts/docker/templates/devnet/docker-compose.yml b/scripts/docker/templates/devnet/docker-compose.yml new file mode 100644 index 0000000000..7cfadabbd8 --- /dev/null +++ b/scripts/docker/templates/devnet/docker-compose.yml @@ -0,0 +1,43 @@ +version: '2' +services: + + postgres: + image: "postgres:alpine" + container_name: {token}-devnet-postgres + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: {token}_devnet + POSTGRES_USER: {token} + + core: + build: . + image: core + container_name: {token}-devnet-core + ports: + - "4002:4002" + - "4003:4003" + volumes: + - core:/core + tty: true + privileged: true + links: + - postgres + depends_on: + - postgres + command: + - /bin/sh + - -c + - | + /entrypoint.sh + +volumes: + postgres: + core: + driver_opts: + type: none + device: $PWD/../../../ + o: bind diff --git a/scripts/docker/templates/devnet/entrypoint.sh b/scripts/docker/templates/devnet/entrypoint.sh new file mode 100755 index 0000000000..3e8a3ef710 --- /dev/null +++ b/scripts/docker/templates/devnet/entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +sysctl -w net.ipv4.conf.all.route_localnet=1 + +POSTGRES=$(ping -c 1 {token}-devnet-postgres | awk -F'[()]' '/PING/{print $2}') +CORE=$(ping -c 1 {token}-devnet-core | awk -F'[()]' '/PING/{print $2}') + +iptables -I OUTPUT -t nat -o lo -d localhost -p tcp --dport 5432 -j DNAT --to-destination ${POSTGRES}:5432 +iptables -I POSTROUTING -t nat -p tcp --dport 5432 -d ${POSTGRES} -j SNAT --to ${CORE} + +cd /core +rm -rf node_modules package-lock.json > /dev/null 2>&1 +rm -rf packages/core/node_modules packages/core/package-lock.json 2>&1 +yarn setup + +bash diff --git a/scripts/docker/templates/devnet/purge_all.sh b/scripts/docker/templates/devnet/purge_all.sh new file mode 100755 index 0000000000..7be429bc03 --- /dev/null +++ b/scripts/docker/templates/devnet/purge_all.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +docker stop $(docker ps -aq) +docker rm $(docker ps -aq) +docker rmi $(docker images -q) +docker volume prune -f +docker network prune -f diff --git a/scripts/docker/templates/devnet/restore.sh b/scripts/docker/templates/devnet/restore.sh new file mode 100755 index 0000000000..efd5ae4c03 --- /dev/null +++ b/scripts/docker/templates/devnet/restore.sh @@ -0,0 +1,8 @@ +DOCKER_DB_NAME="$(docker-compose ps -q postgres)" +DB_HOSTNAME=postgres +DB_USER={token} +LOCAL_DUMP_PATH="snapshot.dump" + +docker-compose up -d postgres +docker exec -i "${DOCKER_DB_NAME}" pg_restore -C --clean --no-acl --no-owner -U "${DB_USER}" -d "${DB_HOSTNAME}" < "${LOCAL_DUMP_PATH}" +docker-compose stop postgres diff --git a/scripts/docker/templates/mainnet/docker-compose.yml b/scripts/docker/templates/mainnet/docker-compose.yml new file mode 100644 index 0000000000..5076e90d71 --- /dev/null +++ b/scripts/docker/templates/mainnet/docker-compose.yml @@ -0,0 +1,17 @@ +version: '2' +services: + + postgres: + image: "postgres:alpine" + container_name: {token}-mainnet-postgres + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: {token}_mainnet + POSTGRES_USER: {token} + +volumes: + postgres: diff --git a/scripts/docker/templates/testnet/Dockerfile b/scripts/docker/templates/testnet/Dockerfile new file mode 100644 index 0000000000..df51b718cf --- /dev/null +++ b/scripts/docker/templates/testnet/Dockerfile @@ -0,0 +1,17 @@ +FROM node:10 + +WORKDIR /core + +COPY entrypoint.sh / + +RUN apt-get update && \ + apt-get -y install --no-install-recommends \ + build-essential \ + jq \ + iptables \ + python \ + vim && \ + rm -rf /var/lib/apt/lists/* + +EXPOSE 4000 4003 + diff --git a/scripts/docker/templates/testnet/docker-compose.yml b/scripts/docker/templates/testnet/docker-compose.yml new file mode 100644 index 0000000000..587ebef98c --- /dev/null +++ b/scripts/docker/templates/testnet/docker-compose.yml @@ -0,0 +1,43 @@ +version: '2' +services: + + postgres: + image: "postgres:alpine" + container_name: {token}-testnet-postgres + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: {token}_testnet + POSTGRES_USER: {token} + + core: + build: . + image: core + container_name: {token}-testnet-core + ports: + - "4000:4000" + - "4003:4003" + volumes: + - core:/core + tty: true + privileged: true + links: + - postgres + depends_on: + - postgres + command: + - /bin/sh + - -c + - | + /entrypoint.sh + +volumes: + postgres: + core: + driver_opts: + type: none + device: $PWD/../../../ + o: bind diff --git a/scripts/docker/templates/testnet/entrypoint.sh b/scripts/docker/templates/testnet/entrypoint.sh new file mode 100755 index 0000000000..77d8976835 --- /dev/null +++ b/scripts/docker/templates/testnet/entrypoint.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +sysctl -w net.ipv4.conf.all.route_localnet=1 + +POSTGRES=$(ping -c 1 {token}-testnet-postgres | awk -F'[()]' '/PING/{print $2}') +CORE=$(ping -c 1 {token}-testnet-core | awk -F'[()]' '/PING/{print $2}') + +iptables -I OUTPUT -t nat -o lo -d localhost -p tcp --dport 5432 -j DNAT --to-destination ${POSTGRES}:5432 +iptables -I POSTROUTING -t nat -p tcp --dport 5432 -d ${POSTGRES} -j SNAT --to ${CORE} + +cd /core +rm -rf node_modules package-lock.json > /dev/null 2>&1 +rm -rf packages/core/node_modules packages/core/package-lock.json 2>&1 +yarn setup + +bash diff --git a/docker/testnet/purge_all.sh b/scripts/docker/templates/testnet/purge_all.sh similarity index 100% rename from docker/testnet/purge_all.sh rename to scripts/docker/templates/testnet/purge_all.sh diff --git a/scripts/docker/templates/testnet/restore.sh b/scripts/docker/templates/testnet/restore.sh new file mode 100755 index 0000000000..efd5ae4c03 --- /dev/null +++ b/scripts/docker/templates/testnet/restore.sh @@ -0,0 +1,8 @@ +DOCKER_DB_NAME="$(docker-compose ps -q postgres)" +DB_HOSTNAME=postgres +DB_USER={token} +LOCAL_DUMP_PATH="snapshot.dump" + +docker-compose up -d postgres +docker exec -i "${DOCKER_DB_NAME}" pg_restore -C --clean --no-acl --no-owner -U "${DB_USER}" -d "${DB_HOSTNAME}" < "${LOCAL_DUMP_PATH}" +docker-compose stop postgres diff --git a/scripts/docker/templates/unitnet/docker-compose.yml b/scripts/docker/templates/unitnet/docker-compose.yml new file mode 100644 index 0000000000..f64964d32f --- /dev/null +++ b/scripts/docker/templates/unitnet/docker-compose.yml @@ -0,0 +1,17 @@ +version: '2' +services: + + postgres: + image: "postgres:alpine" + container_name: {token}-unitnet-postgres + ports: + - '127.0.0.1:5432:5432' + volumes: + - 'postgres:/var/lib/postgresql/data' + environment: + POSTGRES_PASSWORD: password + POSTGRES_DB: {token}_unitnet + POSTGRES_USER: {token} + +volumes: + postgres: diff --git a/scripts/docker/templates/unitnet/purge.sh b/scripts/docker/templates/unitnet/purge.sh new file mode 100755 index 0000000000..97e09a6f07 --- /dev/null +++ b/scripts/docker/templates/unitnet/purge.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env sh + +docker stop {token}-unitnet-postgres +docker rm -v {token}-unitnet-postgres +docker volume rm unitnet_postgres +docker network rm unitnet_default diff --git a/scripts/pre-commit.sh b/scripts/pre-commit.sh index d901b9a3ea..8600dd82c0 100755 --- a/scripts/pre-commit.sh +++ b/scripts/pre-commit.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + echo "Running pre-commit script..." node .circleci/generateConfig.js diff --git a/scripts/pre-test.sh b/scripts/pre-test.sh new file mode 100644 index 0000000000..c2facd27c5 --- /dev/null +++ b/scripts/pre-test.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +yarn lint +yarn build +rm -rf ~/.core/database diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 0000000000..79f87ee52b --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +./node_modules/lerna/cli.js publish from-package --yes diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh new file mode 100755 index 0000000000..f9ebbd0d69 --- /dev/null +++ b/scripts/upgrade.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +pm2 delete ark-core > /dev/null 2>&1 +pm2 delete ark-core-relay > /dev/null 2>&1 +pm2 delete ark-core-forger > /dev/null 2>&1 + +pm2 delete core > /dev/null 2>&1 +pm2 delete core-relay > /dev/null 2>&1 +pm2 delete core-forger > /dev/null 2>&1 + +node ./scripts/upgrade/upgrade.js + +# Sometimes the upgrade script doesn't properly replace ARK_ with CORE_ +# https://github.com/ArkEcosystem/core/blob/develop/scripts/upgrade/upgrade.js#L206 +cd ~ + +if [ -f .config/ark-core/devnet/.env ]; then + sed -i 's/ARK_/CORE_/g' .config/ark-core/devnet/.env +fi + +if [ -f .config/ark-core/devnet/plugins.js ]; then + sed -i 's/ARK_/CORE_/g' .config/ark-core/devnet/plugins.js +fi + +if [ -f .config/ark-core/mainnet/.env ]; then + sed -i 's/ARK_/CORE_/g' .config/ark-core/mainnet/.env +fi + +if [ -f .config/ark-core/mainnet/plugins.js ]; then + sed -i 's/ARK_/CORE_/g' .config/ark-core/mainnet/plugins.js +fi + +cd ~/ark-core +yarn setup diff --git a/scripts/upgrade/test.sh b/scripts/upgrade/test.sh new file mode 100644 index 0000000000..c4063a6f4b --- /dev/null +++ b/scripts/upgrade/test.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +rm -rf /home/ark/ark-core +git clone https://github.com/ArkEcosystem/core -b upgrade /home/ark/ark-core + +mkdir /home/ark/.ark +touch /home/ark/.ark/.env + +mkdir /home/ark/.ark/config + +mkdir /home/ark/.ark/database +touch /home/ark/.ark/database/json-rpc.sqlite +touch /home/ark/.ark/database/transaction-pool.sqlite +touch /home/ark/.ark/database/webhooks.sqlite + +mkdir /home/ark/.ark/logs +mkdir /home/ark/.ark/logs/mainnet +touch /home/ark/.ark/logs/mainnet/test.log diff --git a/scripts/upgrade/upgrade.js b/scripts/upgrade/upgrade.js new file mode 100644 index 0000000000..2a170926b7 --- /dev/null +++ b/scripts/upgrade/upgrade.js @@ -0,0 +1,227 @@ +const envPaths = require('env-paths'); +const expandHomeDir = require('expand-home-dir'); +const fs = require('fs-extra'); +const Joi = require('joi'); +const prompts = require('prompts'); +const { EOL } = require('os'); + +const main = async () => { + let { + corePath, + coreData, + coreNetwork + } = await prompts([{ + type: 'text', + name: 'corePath', + initial: expandHomeDir('~/ark-core'), + message: 'Where is the installation located at? [press ENTER to use default]', + validate: value => fs.existsSync(value) ? true : `${value} does not exist.` + }, { + type: 'text', + name: 'coreData', + initial: expandHomeDir('~/.ark'), + message: 'Where is the configuration located at? [press ENTER to use default]', + validate: value => fs.existsSync(value) ? true : `${value} does not exist.` + }, { + type: 'select', + name: 'coreNetwork', + message: 'What network are you on?', + validate: value => ['mainnet', 'devnet', 'testnet'].includes(value) ? true : `${value} is not a valid network.`, + choices: [ + { title: 'mainnet', value: 'mainnet' }, + { title: 'devnet', value: 'devnet' }, + { title: 'testnet', value: 'testnet' } + ], + }]); + + // Paths + const corePaths = envPaths('ark', { + suffix: 'core' + }); + + corePath = expandHomeDir(corePath); + + const paths = { + cache: { + old: expandHomeDir(`${coreData}/database`), + new: `${corePaths.cache}/${coreNetwork}`, + }, + config: { + old: expandHomeDir(`${coreData}/config`), + new: `${corePaths.config}/${coreNetwork}`, + }, + log: { + old: expandHomeDir(`${coreData}/logs`), + new: `${corePaths.log}/${coreNetwork}`, + }, + temp: { + old: expandHomeDir(`${coreData}/temp`), + new: `${corePaths.temp}/${coreNetwork}`, + }, + data: { + old: expandHomeDir(coreData), + new: `${corePaths.data}/${coreNetwork}`, + }, + }; + + // update commander file if present + const commanderEnv = expandHomeDir('~/.commander') + + if (fs.existsSync(commanderEnv)) { + const commanderContents = fs.readFileSync(commanderEnv).toString(); + + if (!commanderContents.includes('CORE_PATH_DATA')) { + fs.appendFileSync(commanderEnv, `CORE_PATH_DATA=${paths.data.new}${EOL}`); + } + + if (!commanderContents.includes('CORE_PATH_CONFIG')) { + fs.appendFileSync(commanderEnv, `CORE_PATH_CONFIG=${paths.config.new}${EOL}`); + } + + if (!commanderContents.includes('CORE_PATH_CACHE')) { + fs.appendFileSync(commanderEnv, `CORE_PATH_CACHE=${paths.cache.new}${EOL}`); + } + + if (!commanderContents.includes('CORE_PATH_LOG')) { + fs.appendFileSync(commanderEnv, `CORE_PATH_LOG=${paths.log.new}${EOL}`); + } + + if (!commanderContents.includes('CORE_PATH_TEMP')) { + fs.appendFileSync(commanderEnv, `CORE_PATH_TEMP=${paths.temp.new}${EOL}`); + } + + fs.writeFileSync(commanderEnv, commanderContents); + } + + // Create directories + for (const value of Object.values(paths)) { + fs.ensureDirSync(value.new); + } + + // Ensure we copy the .env file + if (!fs.existsSync(`${paths.data.old}/.env`)) { + console.log(`The ${paths.data.old}/.env file does not exist.`) + process.exit(1); + } + + const envCurrent = fs.readFileSync(`${paths.data.old}/.env`).toString(); + + // Move files & directories + for (const value of Object.values(paths)) { + if (fs.existsSync(value.old)) { + console.error(`Moving ${value.old} to ${value.new}.`); + + fs.moveSync(value.old, value.new, { + overwrite: true + }); + } else { + console.error(`Folder ${value.old} does not exist.`); + } + } + + // Move files + if (fs.existsSync(`${paths.cache.new}/json-rpc.sqlite`)) { + fs.moveSync(`${paths.cache.new}/json-rpc.sqlite`, `${paths.data.new}/json-rpc.sqlite`); + } + + if (fs.existsSync(`${paths.cache.new}/transaction-pool-${coreNetwork}.sqlite`)) { + fs.moveSync(`${paths.cache.new}/transaction-pool-${coreNetwork}.sqlite`, `${paths.data.new}/transaction-pool.sqlite`); + } + + if (fs.existsSync(`${paths.cache.new}/webhooks.sqlite`)) { + fs.moveSync(`${paths.cache.new}/webhooks.sqlite`, `${paths.data.new}/webhooks.sqlite`); + } + + if (fs.existsSync(`${corePaths.log}/core/${coreNetwork}`)) { + fs.moveSync(`${corePaths.log}/core/${coreNetwork}`, `${paths.log.new}/${coreNetwork}`); + } + + if (fs.existsSync(`${paths.data.new}/snapshots/${coreNetwork}`)) { + fs.moveSync(`${paths.data.new}/snapshots/${coreNetwork}`, `${paths.data.new}/snapshots.tmp`) + fs.rmdirSync(`${paths.data.new}/snapshots`) + fs.renameSync(`${paths.data.new}/snapshots.tmp`, `${paths.data.new}/snapshots`) + } + + // Remove old or temp files + fs.removeSync(`${paths.config.old}/genesisBlock.json`); + fs.removeSync(`${paths.config.old}/peers_backup.json`); + fs.removeSync(`${paths.config.old}/network.json`); + fs.removeSync(`${paths.config.new}/genesisBlock.json`); + fs.removeSync(`${paths.config.new}/peers_backup.json`); + fs.removeSync(`${paths.config.new}/network.json`); + + // Ensure that all files core needs exist + const requiredFiles = [ + { + copy: `${paths.config.new}/delegates.json`, + original: `${corePath}/packages/core/src/config/${coreNetwork}/delegates.json`, + }, { + copy: `${paths.config.new}/peers.json`, + original: `${corePath}/packages/core/src/config/${coreNetwork}/peers.json`, + }, { + copy: `${paths.config.new}/plugins.js`, + original: `${corePath}/packages/core/src/config/${coreNetwork}/plugins.js`, + }, + ]; + + for (const file of requiredFiles) { + if (!fs.existsSync(file.copy)) { + if (fs.existsSync(file.original)) { + console.error(`Copying ${file.original} to ${file.copy} because it is missing.`); + + fs.copySync(file.original, file.copy); + } else { + console.error(`Original ${file.original} does not exist.`); + } + } + } + + // Update delegate configuration + console.log('Update delegate configuration'); + let configDelegates = require(`${paths.config.new}/delegates.json`) + delete configDelegates.dynamicFee + delete configDelegates.dynamicFees + fs.writeFileSync(`${paths.config.new}/delegates.json`, JSON.stringify(configDelegates, null, 4)); + + // Update environment file + console.log('Update environment configuration'); + fs.writeFileSync(`${paths.config.new}/.env`, envCurrent.replace(new RegExp('ARK_', 'g'), 'CORE_')); + + // Update plugins file + console.log('Update plugins configuration'); + let pluginContents = fs.readFileSync(`${paths.config.new}/plugins.js`).toString(); + pluginContents = pluginContents.replace('@arkecosystem/core-transaction-pool-mem', '@arkecosystem/core-transaction-pool'); + pluginContents = pluginContents.replace('"@arkecosystem/core-config": {},', ''); + pluginContents = pluginContents.replace("'@arkecosystem/core-config': {},", ''); + pluginContents = pluginContents.replace(new RegExp('ARK_', 'g'), 'CORE_'); + fs.writeFileSync(`${paths.config.new}/plugins.js`, pluginContents); + + // Validate configuration files + console.log('Validating configuration'); + const { error } = Joi.validate({ + delegates: require(`${paths.config.new}/delegates.json`), + peers: require(`${paths.config.new}/peers.json`), + plugins: require(`${paths.config.new}/plugins.js`), + }, Joi.object({ + delegates: Joi.object({ + secrets: Joi.array().items(Joi.string()), + bip38: Joi.string(), + }), + peers: Joi.object().required(), + plugins: Joi.object().required(), + }).unknown()); + + if (error) { + console.log(error); + } + + // Clean up + console.log('Performing clean up'); + for (const value of Object.values(paths)) { + if (fs.existsSync(value.old)) { + fs.removeSync(value.old); + } + } +} + +main() diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100644 index 0000000000..227367f29d --- /dev/null +++ b/scripts/version.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +./node_modules/lerna/cli.js version $1 --no-git-tag-version --yes diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..e783fb3af2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "@sindresorhus/tsconfig", + "compilerOptions": { + "lib": ["es2018"], + "typeRoots": ["node_modules/@types"], + "target": "es2018", + "module": "commonjs", + "moduleResolution": "node", + "sourceMap": true, + "strict": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "resolveJsonModule": true + }, + "exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000000..febb1c3f92 --- /dev/null +++ b/tslint.json @@ -0,0 +1,10 @@ +{ + "extends": ["tslint:recommended", "tslint-config-prettier"], + "rules": { + "interface-name": false, + "no-console": false, + "no-default-export": true, + "object-literal-sort-keys": false, + "radix": false + } +} diff --git a/upgrade/2.1.0/exchange.sh b/upgrade/2.1.0/exchange.sh new file mode 100644 index 0000000000..ef5b0c5c49 --- /dev/null +++ b/upgrade/2.1.0/exchange.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +cd ~/ark-core +pm2 delete ark-core +pm2 delete ark-core-relay +git reset --hard +git pull +git checkout master +yarn run bootstrap +yarn run upgrade + +pm2 --name 'ark-core-relay' start ~/ark-core/packages/core/dist/index.js -- relay --network mainnet diff --git a/upgrade/2.1.0/normal.sh b/upgrade/2.1.0/normal.sh new file mode 100644 index 0000000000..be61882355 --- /dev/null +++ b/upgrade/2.1.0/normal.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +cd ~/ark-core +pm2 delete all +git reset --hard +git pull +git checkout master +yarn run bootstrap +yarn run upgrade + +# If you do not use Core Commander you can skip this step. +cd ~/core-commander +git reset --hard +git pull +git checkout master +bash commander.sh diff --git a/yarn.lock b/yarn.lock index 4ca715656a..7d46f00bea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,31 +3,17 @@ "@apollographql/apollo-tools@^0.2.6": - version "0.2.8" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.2.8.tgz#f755baa3576eabdd93afa2782be61f5ae8a856dc" - integrity sha512-A7FTUigtpGCFBaLT1ILicdjM6pZ7LQNw7Vgos0t4aLYtvlKO/L1nMi/NO7bPypzZaJSToTgcxHJPRydP1Md+Kw== + version "0.2.9" + resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.2.9.tgz#1e20999d11728ef47f8f812f2be0426b5dde1a51" + integrity sha512-AEIQwPkS0QLbkpb6WyRhV4aOMxuErasp47ABv5niDKOasQH8mrD8JSGKJAHuQxVe4kB8DE9sLRoc5qeQ0KFCHA== dependencies: apollo-env "0.2.5" -"@apollographql/apollo-upload-server@^5.0.3": - version "5.0.3" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-upload-server/-/apollo-upload-server-5.0.3.tgz#8558c378ff6457de82147e5072c96a6b242773b7" - integrity sha512-tGAp3ULNyoA8b5o9LsU2Lq6SwgVPUOKAqKywu2liEtTvrFSGPrObwanhYwArq3GPeOqp2bi+JknSJCIU3oQN1Q== - dependencies: - "@babel/runtime-corejs2" "^7.0.0-rc.1" - busboy "^0.2.14" - object-path "^0.11.4" - "@apollographql/graphql-playground-html@^1.6.6": version "1.6.6" resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.6.tgz#022209e28a2b547dcde15b219f0c50f47aa5beb3" integrity sha512-lqK94b+caNtmKFs5oUVXlSpN3sm5IXZ+KfhMxOtr0LR2SqErzkoJilitjDvJ1WbjHlxLI7WtCjRmOLdOGJqtMQ== -"@arkecosystem/eslint-config-base@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@arkecosystem/eslint-config-base/-/eslint-config-base-0.1.0.tgz#c4e39d1051598b1ecc7f7ce4ca54f577deecaf0c" - integrity sha512-5Fx2xy+gOkwW92ahUACAjv7cHHKtxPS5Sz6Ef5ejbglrkXl/7Wzh+OKDJ1yXS1lkjI0CQr6ooWiSh7nRp4HHqw== - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" @@ -35,18 +21,18 @@ dependencies: "@babel/highlight" "^7.0.0" -"@babel/core@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.1.6.tgz#3733cbee4317429bc87c62b29cf8587dba7baeb3" - integrity sha512-Hz6PJT6e44iUNpAn8AoyAs6B3bl60g7MJQaI0rZEar6ECzh6+srYO1xlIdssio34mPaUtAb1y+XlkkSJzok3yw== +"@babel/core@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.2.tgz#07adba6dde27bb5ad8d8672f15fde3e08184a687" + integrity sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.1.6" - "@babel/helpers" "^7.1.5" - "@babel/parser" "^7.1.6" - "@babel/template" "^7.1.2" - "@babel/traverse" "^7.1.6" - "@babel/types" "^7.1.6" + "@babel/generator" "^7.2.2" + "@babel/helpers" "^7.2.0" + "@babel/parser" "^7.2.2" + "@babel/template" "^7.2.2" + "@babel/traverse" "^7.2.2" + "@babel/types" "^7.2.2" convert-source-map "^1.1.0" debug "^4.1.0" json5 "^2.1.0" @@ -56,11 +42,22 @@ source-map "^0.5.0" "@babel/generator@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.1.6.tgz#001303cf87a5b9d093494a4bf251d7b5d03d3999" - integrity sha512-brwPBtVvdYdGxtenbQgfCdDPmtkmUBZPjUoK5SXJEBuHaA5BCubh9ly65fzXz7R6o5rA76Rs22ES8Z+HCc0YIQ== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.0.tgz#eaf3821fa0301d9d4aef88e63d4bcc19b73ba16c" + integrity sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg== dependencies: - "@babel/types" "^7.1.6" + "@babel/types" "^7.2.0" + jsesc "^2.5.1" + lodash "^4.17.10" + source-map "^0.5.0" + trim-right "^1.0.1" + +"@babel/generator@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.2.tgz#18c816c70962640eab42fe8cae5f3947a5c65ccc" + integrity sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg== + dependencies: + "@babel/types" "^7.2.2" jsesc "^2.5.1" lodash "^4.17.10" source-map "^0.5.0" @@ -212,23 +209,23 @@ "@babel/types" "^7.0.0" "@babel/helper-wrap-function@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.1.0.tgz#8cf54e9190706067f016af8f75cb3df829cc8c66" - integrity sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz#c4e0012445769e2815b55296ead43a958549f6fa" + integrity sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/template" "^7.1.0" "@babel/traverse" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/types" "^7.2.0" -"@babel/helpers@^7.1.5": - version "7.1.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.1.5.tgz#68bfc1895d685f2b8f1995e788dbfe1f6ccb1996" - integrity sha512-2jkcdL02ywNBry1YNFAH/fViq4fXG0vdckHqeJk+75fpQ2OH+Az6076tX/M0835zA45E0Cqa6pV5Kiv9YOqjEg== +"@babel/helpers@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.2.0.tgz#8335f3140f3144270dc63c4732a4f8b0a50b7a21" + integrity sha512-Fr07N+ea0dMcMN8nFpuK6dUIT7/ivt9yKQdEEnjVS83tG2pHwPi03gYmk/tyuwONnZ+sY+GFFPlWGgCtW1hF9A== dependencies: "@babel/template" "^7.1.2" "@babel/traverse" "^7.1.5" - "@babel/types" "^7.1.5" + "@babel/types" "^7.2.0" "@babel/highlight@^7.0.0": version "7.0.0" @@ -239,116 +236,126 @@ esutils "^2.0.2" js-tokens "^4.0.0" +"@babel/parser@^7.1.0": + version "7.2.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489" + integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA== + "@babel/parser@^7.1.2", "@babel/parser@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.1.6.tgz#16e97aca1ec1062324a01c5a6a7d0df8dd189854" - integrity sha512-dWP6LJm9nKT6ALaa+bnL247GHHMWir3vSlZ2+IHgHgktZQx0L3Uvq2uAWcuzIe+fujRsYWBW2q622C5UvGK9iQ== + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.0.tgz#02d01dbc330b6cbf36b76ac93c50752c69027065" + integrity sha512-M74+GvK4hn1eejD9lZ7967qAwvqTZayQa3g10ag4s9uewgR7TKjeaT0YMyoq+gVfKYABiWZ4MQD701/t5e1Jhg== -"@babel/plugin-proposal-async-generator-functions@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.1.0.tgz#41c1a702e10081456e23a7b74d891922dd1bb6ce" - integrity sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew== +"@babel/parser@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.2.tgz#37ebdbc88a2e1ebc6c8dd3d35ea9436e3e39e477" + integrity sha512-UNTmQ5cSLDeBGBl+s7JeowkqIHgmFAGBnLDdIzFmUNSuS5JF0XBcN59jsh/vJO/YjfsBqMxhMjoFGmNExmf0FA== + +"@babel/plugin-proposal-async-generator-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" + integrity sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" - "@babel/plugin-syntax-async-generators" "^7.0.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" -"@babel/plugin-proposal-json-strings@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.0.0.tgz#3b4d7b5cf51e1f2e70f52351d28d44fc2970d01e" - integrity sha512-kfVdUkIAGJIVmHmtS/40i/fg/AGnw/rsZBCaapY5yjeO5RA9m165Xbw9KMOu2nqXP5dTFjEjHdfNdoVcHv133Q== +"@babel/plugin-proposal-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz#568ecc446c6148ae6b267f02551130891e29f317" + integrity sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-json-strings" "^7.0.0" + "@babel/plugin-syntax-json-strings" "^7.2.0" -"@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0.tgz#9a17b547f64d0676b6c9cecd4edf74a82ab85e7e" - integrity sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw== +"@babel/plugin-proposal-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.2.0.tgz#88f5fec3e7ad019014c97f7ee3c992f0adbf7fb8" + integrity sha512-1L5mWLSvR76XYUQJXkd/EEQgjq8HHRP6lQuZTTg0VA4tTGPpGemmCdAfQIz1rzEuWAm+ecP8PyyEm30jC1eQCg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" -"@babel/plugin-proposal-optional-catch-binding@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0.tgz#b610d928fe551ff7117d42c8bb410eec312a6425" - integrity sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw== +"@babel/plugin-proposal-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz#135d81edb68a081e55e56ec48541ece8065c38f5" + integrity sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" -"@babel/plugin-proposal-unicode-property-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0.tgz#498b39cd72536cd7c4b26177d030226eba08cd33" - integrity sha512-tM3icA6GhC3ch2SkmSxv7J/hCWKISzwycub6eGsDrFDgukD4dZ/I+x81XgW0YslS6mzNuQ1Cbzh5osjIMgepPQ== +"@babel/plugin-proposal-unicode-property-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz#abe7281fe46c95ddc143a65e5358647792039520" + integrity sha512-LvRVYb7kikuOtIoUeWTkOxQEV1kYvL5B6U3iWEGCzPNRus1MzJweFqORTj+0jkxozkTSYNJozPOddxmqdqsRpw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" regexpu-core "^4.2.0" -"@babel/plugin-syntax-async-generators@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0.tgz#bf0891dcdbf59558359d0c626fdc9490e20bc13c" - integrity sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA== +"@babel/plugin-syntax-async-generators@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz#69e1f0db34c6f5a0cf7e2b3323bf159a76c8cb7f" + integrity sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-json-strings@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.0.0.tgz#0d259a68090e15b383ce3710e01d5b23f3770cbd" - integrity sha512-UlSfNydC+XLj4bw7ijpldc1uZ/HB84vw+U6BTuqMdIEmz/LDe63w/GHtpQMdXWdqQZFeAI9PjnHe/vDhwirhKA== +"@babel/plugin-syntax-json-strings@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz#72bd13f6ffe1d25938129d2a186b11fd62951470" + integrity sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0.tgz#37d8fbcaf216bd658ea1aebbeb8b75e88ebc549b" - integrity sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw== +"@babel/plugin-syntax-object-rest-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" + integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-optional-catch-binding@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0.tgz#886f72008b3a8b185977f7cb70713b45e51ee475" - integrity sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw== +"@babel/plugin-syntax-optional-catch-binding@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz#a94013d6eda8908dfe6a477e7f9eda85656ecf5c" + integrity sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0.tgz#a6c14875848c68a3b4b3163a486535ef25c7e749" - integrity sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w== +"@babel/plugin-transform-arrow-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550" + integrity sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-async-to-generator@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.1.0.tgz#109e036496c51dd65857e16acab3bafdf3c57811" - integrity sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g== +"@babel/plugin-transform-async-to-generator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.2.0.tgz#68b8a438663e88519e65b776f8938f3445b1a2ff" + integrity sha512-CEHzg4g5UraReozI9D4fblBYABs7IM6UerAVG7EJVrTLC5keh00aEuLUT+O40+mJCEzaXkYfTCUKIyeDfMOFFQ== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-remap-async-to-generator" "^7.1.0" -"@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0.tgz#482b3f75103927e37288b3b67b65f848e2aa0d07" - integrity sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ== +"@babel/plugin-transform-block-scoped-functions@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz#5d3cc11e8d5ddd752aa64c9148d0db6cb79fd190" + integrity sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-block-scoping@^7.1.5": - version "7.1.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.1.5.tgz#3e8e0bc9a5104519923302a24f748f72f2f61f37" - integrity sha512-jlYcDrz+5ayWC7mxgpn1Wj8zj0mmjCT2w0mPIMSwO926eXBRxpEgoN/uQVRBfjtr8ayjcmS+xk2G1jaP8JjMJQ== +"@babel/plugin-transform-block-scoping@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.2.0.tgz#f17c49d91eedbcdf5dd50597d16f5f2f770132d4" + integrity sha512-vDTgf19ZEV6mx35yiPJe4fS02mPQUUcBNwWQSZFXSzTSbsJFQvHt7DqyS3LK8oOWALFOsJ+8bbqBgkirZteD5Q== dependencies: "@babel/helper-plugin-utils" "^7.0.0" lodash "^4.17.10" -"@babel/plugin-transform-classes@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.1.0.tgz#ab3f8a564361800cbc8ab1ca6f21108038432249" - integrity sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg== +"@babel/plugin-transform-classes@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.2.0.tgz#374f8876075d7d21fea55aeb5c53561259163f96" + integrity sha512-aPCEkrhJYebDXcGTAP+cdUENkH7zqOlgbKwLbghjjHpJRJBWM/FSlCjMoPGA8oUdiMfOrk3+8EFPLLb5r7zj2w== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-define-map" "^7.1.0" @@ -359,95 +366,95 @@ "@babel/helper-split-export-declaration" "^7.0.0" globals "^11.1.0" -"@babel/plugin-transform-computed-properties@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0.tgz#2fbb8900cd3e8258f2a2ede909b90e7556185e31" - integrity sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA== +"@babel/plugin-transform-computed-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz#83a7df6a658865b1c8f641d510c6f3af220216da" + integrity sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-destructuring@^7.0.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.1.3.tgz#e69ff50ca01fac6cb72863c544e516c2b193012f" - integrity sha512-Mb9M4DGIOspH1ExHOUnn2UUXFOyVTiX84fXCd+6B5iWrQg/QMeeRmSwpZ9lnjYLSXtZwiw80ytVMr3zue0ucYw== +"@babel/plugin-transform-destructuring@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.2.0.tgz#e75269b4b7889ec3a332cd0d0c8cff8fed0dc6f3" + integrity sha512-coVO2Ayv7g0qdDbrNiadE4bU7lvCd9H539m2gMknyVjjMdwF/iCOM7R+E8PkntoqLkltO0rk+3axhpp/0v68VQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-dotall-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0.tgz#73a24da69bc3c370251f43a3d048198546115e58" - integrity sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig== +"@babel/plugin-transform-dotall-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.2.0.tgz#f0aabb93d120a8ac61e925ea0ba440812dbe0e49" + integrity sha512-sKxnyHfizweTgKZf7XsXu/CNupKhzijptfTM+bozonIuyVrLWVUvYjE2bhuSBML8VQeMxq4Mm63Q9qvcvUcciQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" regexpu-core "^4.1.3" -"@babel/plugin-transform-duplicate-keys@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0.tgz#a0601e580991e7cace080e4cf919cfd58da74e86" - integrity sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ== +"@babel/plugin-transform-duplicate-keys@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz#d952c4930f312a4dbfff18f0b2914e60c35530b3" + integrity sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-exponentiation-operator@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.1.0.tgz#9c34c2ee7fd77e02779cfa37e403a2e1003ccc73" - integrity sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ== +"@babel/plugin-transform-exponentiation-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz#a63868289e5b4007f7054d46491af51435766008" + integrity sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A== dependencies: "@babel/helper-builder-binary-assignment-operator-visitor" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-for-of@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0.tgz#f2ba4eadb83bd17dc3c7e9b30f4707365e1c3e39" - integrity sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA== +"@babel/plugin-transform-for-of@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.2.0.tgz#ab7468befa80f764bb03d3cb5eef8cc998e1cad9" + integrity sha512-Kz7Mt0SsV2tQk6jG5bBv5phVbkd0gd27SgYD4hH1aLMJRchM0dzHaXvrWhVZ+WxAlDoAKZ7Uy3jVTW2mKXQ1WQ== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-function-name@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.1.0.tgz#29c5550d5c46208e7f730516d41eeddd4affadbb" - integrity sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg== +"@babel/plugin-transform-function-name@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.2.0.tgz#f7930362829ff99a3174c39f0afcc024ef59731a" + integrity sha512-kWgksow9lHdvBC2Z4mxTsvc7YdY7w/V6B2vy9cTIPtLEE9NhwoWivaxdNM/S37elu5bqlLP/qOY906LukO9lkQ== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-literals@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0.tgz#2aec1d29cdd24c407359c930cdd89e914ee8ff86" - integrity sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA== +"@babel/plugin-transform-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz#690353e81f9267dad4fd8cfd77eafa86aba53ea1" + integrity sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-amd@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.1.0.tgz#f9e0a7072c12e296079b5a59f408ff5b97bf86a8" - integrity sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A== +"@babel/plugin-transform-modules-amd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz#82a9bce45b95441f617a24011dc89d12da7f4ee6" + integrity sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-commonjs@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.1.0.tgz#0a9d86451cbbfb29bd15186306897c67f6f9a05c" - integrity sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA== +"@babel/plugin-transform-modules-commonjs@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz#c4f1933f5991d5145e9cfad1dfd848ea1727f404" + integrity sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-simple-access" "^7.1.0" -"@babel/plugin-transform-modules-systemjs@^7.0.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.1.3.tgz#2119a3e3db612fd74a19d88652efbfe9613a5db0" - integrity sha512-PvTxgjxQAq4pvVUZF3mD5gEtVDuId8NtWkJsZLEJZMZAW3TvgQl1pmydLLN1bM8huHFVVU43lf0uvjQj9FRkKw== +"@babel/plugin-transform-modules-systemjs@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.2.0.tgz#912bfe9e5ff982924c81d0937c92d24994bb9068" + integrity sha512-aYJwpAhoK9a+1+O625WIjvMY11wkB/ok0WClVwmeo3mCjcNRjt+/8gHWrB5i+00mUju0gWsBkQnPpdvQ7PImmQ== dependencies: "@babel/helper-hoist-variables" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-modules-umd@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.1.0.tgz#a29a7d85d6f28c3561c33964442257cc6a21f2a8" - integrity sha512-enrRtn5TfRhMmbRwm7F8qOj0qEYByqUvTttPEGimcBH4CJHphjyK1Vg7sdU7JjeEmgSpM890IT/efS2nMHwYig== +"@babel/plugin-transform-modules-umd@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz#7678ce75169f0877b8eb2235538c074268dd01ae" + integrity sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw== dependencies: "@babel/helper-module-transforms" "^7.1.0" "@babel/helper-plugin-utils" "^7.0.0" @@ -459,18 +466,18 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-object-super@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.1.0.tgz#b1ae194a054b826d8d4ba7ca91486d4ada0f91bb" - integrity sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw== +"@babel/plugin-transform-object-super@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz#b35d4c10f56bab5d650047dad0f1d8e8814b6598" + integrity sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-replace-supers" "^7.1.0" -"@babel/plugin-transform-parameters@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.1.0.tgz#44f492f9d618c9124026e62301c296bf606a7aed" - integrity sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw== +"@babel/plugin-transform-parameters@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.2.0.tgz#0d5ad15dc805e2ea866df4dd6682bfe76d1408c2" + integrity sha512-kB9+hhUidIgUoBQ0MsxMewhzr8i60nMa2KgeJKQWYrqQpqcBYtnpR+JgkadZVZoaEZ/eKu9mclFaVwhRpLNSzA== dependencies: "@babel/helper-call-delegate" "^7.1.0" "@babel/helper-get-function-arity" "^7.0.0" @@ -483,107 +490,99 @@ dependencies: regenerator-transform "^0.13.3" -"@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0.tgz#85f8af592dcc07647541a0350e8c95c7bf419d15" - integrity sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw== +"@babel/plugin-transform-shorthand-properties@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz#6333aee2f8d6ee7e28615457298934a3b46198f0" + integrity sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-spread@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0.tgz#93583ce48dd8c85e53f3a46056c856e4af30b49b" - integrity sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ== +"@babel/plugin-transform-spread@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.0.tgz#0c76c12a3b5826130078ee8ec84a7a8e4afd79c4" + integrity sha512-7TtPIdwjS/i5ZBlNiQePQCovDh9pAhVbp/nGVRBZuUdBiVRThyyLend3OHobc0G+RLCPPAN70+z/MAMhsgJd/A== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-sticky-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0.tgz#30a9d64ac2ab46eec087b8530535becd90e73366" - integrity sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw== +"@babel/plugin-transform-sticky-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz#a1e454b5995560a9c1e0d537dfc15061fd2687e1" + integrity sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" -"@babel/plugin-transform-template-literals@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0.tgz#084f1952efe5b153ddae69eb8945f882c7a97c65" - integrity sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg== +"@babel/plugin-transform-template-literals@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz#d87ed01b8eaac7a92473f608c97c089de2ba1e5b" + integrity sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg== dependencies: "@babel/helper-annotate-as-pure" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-typeof-symbol@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0.tgz#4dcf1e52e943e5267b7313bff347fdbe0f81cec9" - integrity sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg== +"@babel/plugin-transform-typeof-symbol@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz#117d2bcec2fbf64b4b59d1f9819894682d29f2b2" + integrity sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw== dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-transform-unicode-regex@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0.tgz#c6780e5b1863a76fe792d90eded9fcd5b51d68fc" - integrity sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw== +"@babel/plugin-transform-unicode-regex@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b" + integrity sha512-m48Y0lMhrbXEJnVUaYly29jRXbQ3ksxPrS1Tg8t+MHqzXhtBYAvI51euOBaoAlZLPHsieY9XPVMf80a5x0cPcA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/helper-regex" "^7.0.0" regexpu-core "^4.1.3" -"@babel/preset-env@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.1.6.tgz#a0bf4b96b6bfcf6e000afc5b72b4abe7cc13ae97" - integrity sha512-YIBfpJNQMBkb6MCkjz/A9J76SNCSuGVamOVBgoUkLzpJD/z8ghHi9I42LQ4pulVX68N/MmImz6ZTixt7Azgexw== +"@babel/preset-env@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.2.0.tgz#a5030e7e4306af5a295dd5d7c78dc5464af3fee2" + integrity sha512-haGR38j5vOGVeBatrQPr3l0xHbs14505DcM57cbJy48kgMFvvHHoYEhHuRV+7vi559yyAUAVbTWzbK/B/pzJng== dependencies: "@babel/helper-module-imports" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-async-generator-functions" "^7.1.0" - "@babel/plugin-proposal-json-strings" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-proposal-optional-catch-binding" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.0.0" - "@babel/plugin-syntax-async-generators" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-async-to-generator" "^7.1.0" - "@babel/plugin-transform-block-scoped-functions" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.1.5" - "@babel/plugin-transform-classes" "^7.1.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-dotall-regex" "^7.0.0" - "@babel/plugin-transform-duplicate-keys" "^7.0.0" - "@babel/plugin-transform-exponentiation-operator" "^7.1.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.1.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-modules-amd" "^7.1.0" - "@babel/plugin-transform-modules-commonjs" "^7.1.0" - "@babel/plugin-transform-modules-systemjs" "^7.0.0" - "@babel/plugin-transform-modules-umd" "^7.1.0" + "@babel/plugin-proposal-async-generator-functions" "^7.2.0" + "@babel/plugin-proposal-json-strings" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread" "^7.2.0" + "@babel/plugin-proposal-optional-catch-binding" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.2.0" + "@babel/plugin-syntax-async-generators" "^7.2.0" + "@babel/plugin-syntax-object-rest-spread" "^7.2.0" + "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" + "@babel/plugin-transform-arrow-functions" "^7.2.0" + "@babel/plugin-transform-async-to-generator" "^7.2.0" + "@babel/plugin-transform-block-scoped-functions" "^7.2.0" + "@babel/plugin-transform-block-scoping" "^7.2.0" + "@babel/plugin-transform-classes" "^7.2.0" + "@babel/plugin-transform-computed-properties" "^7.2.0" + "@babel/plugin-transform-destructuring" "^7.2.0" + "@babel/plugin-transform-dotall-regex" "^7.2.0" + "@babel/plugin-transform-duplicate-keys" "^7.2.0" + "@babel/plugin-transform-exponentiation-operator" "^7.2.0" + "@babel/plugin-transform-for-of" "^7.2.0" + "@babel/plugin-transform-function-name" "^7.2.0" + "@babel/plugin-transform-literals" "^7.2.0" + "@babel/plugin-transform-modules-amd" "^7.2.0" + "@babel/plugin-transform-modules-commonjs" "^7.2.0" + "@babel/plugin-transform-modules-systemjs" "^7.2.0" + "@babel/plugin-transform-modules-umd" "^7.2.0" "@babel/plugin-transform-new-target" "^7.0.0" - "@babel/plugin-transform-object-super" "^7.1.0" - "@babel/plugin-transform-parameters" "^7.1.0" + "@babel/plugin-transform-object-super" "^7.2.0" + "@babel/plugin-transform-parameters" "^7.2.0" "@babel/plugin-transform-regenerator" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-sticky-regex" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - "@babel/plugin-transform-typeof-symbol" "^7.0.0" - "@babel/plugin-transform-unicode-regex" "^7.0.0" - browserslist "^4.1.0" + "@babel/plugin-transform-shorthand-properties" "^7.2.0" + "@babel/plugin-transform-spread" "^7.2.0" + "@babel/plugin-transform-sticky-regex" "^7.2.0" + "@babel/plugin-transform-template-literals" "^7.2.0" + "@babel/plugin-transform-typeof-symbol" "^7.2.0" + "@babel/plugin-transform-unicode-regex" "^7.2.0" + browserslist "^4.3.4" invariant "^2.2.2" js-levenshtein "^1.1.3" semver "^5.3.0" -"@babel/runtime-corejs2@^7.0.0-rc.1": - version "7.1.5" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.1.5.tgz#ec8341c9aec71d1139c985327314739d66b204a0" - integrity sha512-WsYRwQsFhVmxkAqwypPTZyV9GpkqMEaAr2zOItOmqSX2GBFaI+eq98CN81e13o0zaUKJOQGYyjhNVqj56nnkYg== - dependencies: - core-js "^2.5.7" - regenerator-runtime "^0.12.0" - "@babel/template@^7.1.0", "@babel/template@^7.1.2": version "7.1.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.1.2.tgz#090484a574fef5a2d2d7726a674eceda5c5b5644" @@ -593,7 +592,16 @@ "@babel/parser" "^7.1.2" "@babel/types" "^7.1.2" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.1.6": +"@babel/template@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" + integrity sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.2.2" + "@babel/types" "^7.2.2" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5": version "7.1.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.1.6.tgz#c8db9963ab4ce5b894222435482bd8ea854b7b5c" integrity sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ== @@ -608,15 +616,71 @@ globals "^11.1.0" lodash "^4.17.10" -"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.1.5", "@babel/types@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.1.6.tgz#0adb330c3a281348a190263aceb540e10f04bcce" - integrity sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w== +"@babel/traverse@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.2.tgz#961039de1f9bcb946d807efe2dba9c92e859d188" + integrity sha512-E5Bn9FSwHpSkUhthw/XEuvFZxIgrqb9M8cX8j5EUQtrUG5DQUy6bFyl7G7iQ1D1Czudor+xkmp81JbLVVM0Sjg== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/generator" "^7.2.2" + "@babel/helper-function-name" "^7.1.0" + "@babel/helper-split-export-declaration" "^7.0.0" + "@babel/parser" "^7.2.2" + "@babel/types" "^7.2.2" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.10" + +"@babel/types@^7.0.0", "@babel/types@^7.1.2", "@babel/types@^7.1.6", "@babel/types@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.0.tgz#7941c5b2d8060e06f9601d6be7c223eef906d5d8" + integrity sha512-b4v7dyfApuKDvmPb+O488UlGuR1WbwMXFsO/cyqMrnfvRAChZKJAYeeglWTjUO1b9UghKKgepAQM5tsvBJca6A== + dependencies: + esutils "^2.0.2" + lodash "^4.17.10" + to-fast-properties "^2.0.0" + +"@babel/types@^7.2.2": + version "7.2.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e" + integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg== dependencies: esutils "^2.0.2" lodash "^4.17.10" to-fast-properties "^2.0.0" +"@bugsnag/browser@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@bugsnag/browser/-/browser-5.0.2.tgz#6fcabdd622da82a586428f4b599c9a3fbc664167" + integrity sha512-uhjfoMU8MGRHdsIE8QqGclhopxcrlGgbwrZVLsWVgtiet2H/phGdwJl/6DTw+W9rTK1SmW1CpzAUCN+XfVI+JA== + +"@bugsnag/js@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@bugsnag/js/-/js-5.0.2.tgz#5bfffca3ed87a27e4ba898a9f46c4e377c3d65e1" + integrity sha512-CdilcBCrP2vdosbtoMzEjORX4/BDGTOVp1+WrYY58KbjFoSDc1fUmjPjqAmHxdZKty7oi5PAQeQv0fh3nsdtgQ== + dependencies: + "@bugsnag/browser" "^5.0.2" + "@bugsnag/node" "^5.0.2" + +"@bugsnag/node@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@bugsnag/node/-/node-5.0.2.tgz#7d762f4c5d416cdfe01df86eff8d7cc53401d0e5" + integrity sha512-ad1lR6cE8mXOzFVV9wt/pP8fxZ2gfCvR+p7BIm4c+urSaw+0UnW+/ej6uozbYmhlKTpp8KE1SXU/lr16EM2vvA== + dependencies: + byline "^5.0.0" + error-stack-parser "^2.0.2" + iserror "^0.0.2" + pump "^3.0.0" + request "^2.87.0" + stack-generator "^2.0.3" + +"@faustbrian/benchmarker@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@faustbrian/benchmarker/-/benchmarker-0.1.2.tgz#80fe90f51473eb711fb59b706d52acf4253fbad2" + integrity sha512-JQWNeYUJR0Nh9LwpvcOkQOzmSJTjr2IkL3NN33zEaw7ZEBH9nEBVdQ0q70NpVmWGYe75N9NIaLl3glIXroxE5w== + dependencies: + benchmark "^2.1.4" + "@iamstarkov/listr-update-renderer@0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@iamstarkov/listr-update-renderer/-/listr-update-renderer-0.4.1.tgz#d7c48092a2dcf90fd672b6c8b458649cb350c77e" @@ -647,53 +711,52 @@ pify "3.0.0" sqlite3 "4.0.2" -"@lerna/add@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.5.0.tgz#3518b3d4afc3743b7227b1ee3534114eb9575888" - integrity sha512-hoOqtal/ChEEtt9rxR/6xmyvTN7581XF4kWHoWPV9NbfZN9e8uTR8z4mCcJq2DiZhRuY7aA5FEROEbl12soowQ== +"@lerna/add@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.6.0.tgz#eea53efff0b3237774ddac6eaa84957140e89238" + integrity sha512-aFVekkHMno3hj1Vg3EiIpAwrZ4g34i8z4KrCx7ATY6BRuxVT4Nt/Nk3l2k6gEOq3tWUDtUctLHxIAo14FI8sng== dependencies: - "@lerna/bootstrap" "^3.5.0" - "@lerna/command" "^3.5.0" - "@lerna/filter-options" "^3.5.0" + "@lerna/bootstrap" "^3.6.0" + "@lerna/command" "^3.6.0" + "@lerna/filter-options" "^3.6.0" "@lerna/npm-conf" "^3.4.1" - "@lerna/validation-error" "^3.0.0" + "@lerna/validation-error" "^3.6.0" dedent "^0.7.0" - npm-package-arg "^6.0.0" + libnpm "^2.0.1" p-map "^1.2.0" - pacote "^9.1.0" semver "^5.5.0" -"@lerna/batch-packages@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@lerna/batch-packages/-/batch-packages-3.1.2.tgz#74b5312a01a8916204cbc71237ffbe93144b99df" - integrity sha512-HAkpptrYeUVlBYbLScXgeCgk6BsNVXxDd53HVWgzzTWpXV4MHpbpeKrByyt7viXlNhW0w73jJbipb/QlFsHIhQ== +"@lerna/batch-packages@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/batch-packages/-/batch-packages-3.6.0.tgz#66cf3ce914bbd0532071c58d5f0c913315054158" + integrity sha512-khG15B+EFLH3Oms6A6WsMAy54DrnKIhEAm6CCATN2BKnBkNgitYjLN2vKBzlR2LfQpTkgub67QKIJkMFQcK1Sg== dependencies: - "@lerna/package-graph" "^3.1.2" - "@lerna/validation-error" "^3.0.0" - npmlog "^4.1.2" + "@lerna/package-graph" "^3.6.0" + "@lerna/validation-error" "^3.6.0" + libnpm "^2.0.1" -"@lerna/bootstrap@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.5.0.tgz#4d21ef0d1e648c8121432443a7d80c9442756347" - integrity sha512-+z4kVVJFO5EGfC2ob/4C9LetqWwDtbhZgTRllr1+zOi/2clbD+WKcVI0ku+/ckzKjz783SOc83swX7RrmiLwMQ== +"@lerna/bootstrap@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/bootstrap/-/bootstrap-3.6.0.tgz#a47cd484ad60638d518a606d627b9997d5f7c960" + integrity sha512-z6rZQw/aLEN+ragWRYqIIVwA9rDv3QtmRc5VyIRrlV/JiuGpq67FcSR6BrCMc/A7UJ9Kx95+bESm/HUwheKoiQ== dependencies: - "@lerna/batch-packages" "^3.1.2" - "@lerna/command" "^3.5.0" - "@lerna/filter-options" "^3.5.0" + "@lerna/batch-packages" "^3.6.0" + "@lerna/command" "^3.6.0" + "@lerna/filter-options" "^3.6.0" "@lerna/has-npm-version" "^3.3.0" "@lerna/npm-conf" "^3.4.1" - "@lerna/npm-install" "^3.3.0" - "@lerna/rimraf-dir" "^3.3.0" - "@lerna/run-lifecycle" "^3.4.1" + "@lerna/npm-install" "^3.6.0" + "@lerna/package-graph" "^3.6.0" + "@lerna/rimraf-dir" "^3.6.0" + "@lerna/run-lifecycle" "^3.6.0" "@lerna/run-parallel-batches" "^3.0.0" - "@lerna/symlink-binary" "^3.3.0" - "@lerna/symlink-dependencies" "^3.3.0" - "@lerna/validation-error" "^3.0.0" + "@lerna/symlink-binary" "^3.6.0" + "@lerna/symlink-dependencies" "^3.6.0" + "@lerna/validation-error" "^3.6.0" dedent "^0.7.0" get-port "^3.2.0" + libnpm "^2.0.1" multimatch "^2.1.0" - npm-package-arg "^6.0.0" - npmlog "^4.1.2" p-finally "^1.0.0" p-map "^1.2.0" p-map-series "^1.0.0" @@ -701,24 +764,24 @@ read-package-tree "^5.1.6" semver "^5.5.0" -"@lerna/changed@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.5.0.tgz#c96c4bde6d78f4b2a7b3b3e2511bf78cb6aabe17" - integrity sha512-p9o7/hXwFAoet7UPeHIzIPonYxLHZe9bcNcjxKztZYAne5/OgmZiF4X1UPL2S12wtkT77WQy4Oz8NjRTczcapg== +"@lerna/changed@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/changed/-/changed-3.6.0.tgz#a6c97b9c4829d294a1d8e8a7140667bc89c996e2" + integrity sha512-L1SXTtQrsv+4F5Knw5sW/nGnMJq+bbOzhZX2srJ10WsuHuzk3cJWAi7dVEsS3RPKUw9DWOuHKy86o3v6byEiqA== dependencies: - "@lerna/collect-updates" "^3.5.0" - "@lerna/command" "^3.5.0" - "@lerna/listable" "^3.0.0" - "@lerna/output" "^3.0.0" - "@lerna/version" "^3.5.0" + "@lerna/collect-updates" "^3.6.0" + "@lerna/command" "^3.6.0" + "@lerna/listable" "^3.6.0" + "@lerna/output" "^3.6.0" + "@lerna/version" "^3.6.0" -"@lerna/check-working-tree@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-3.5.0.tgz#015f90247fa0b44a940eab0dd6a9da2ae5b8f455" - integrity sha512-aWeIputHddeZgf7/wA1e5yuv6q9S5si2y7fzO2Ah7m3KyDyl8XHP1M0VSSDzZeiloYCryAYQAoRgcrdH65Vhow== +"@lerna/check-working-tree@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/check-working-tree/-/check-working-tree-3.6.0.tgz#638ee7f5976fe607d544629b1ef4ae67887984b5" + integrity sha512-Ioy1t2aVasAwhY1Oi5kfpwbW9RDupxxVVu2t2c1EeBYYCu3jIt1A5ad34gidgsKyiG3HeBEVziI4Uaihnb96ZQ== dependencies: - "@lerna/describe-ref" "^3.5.0" - "@lerna/validation-error" "^3.0.0" + "@lerna/describe-ref" "^3.6.0" + "@lerna/validation-error" "^3.6.0" "@lerna/child-process@^3.3.0": version "3.3.0" @@ -729,95 +792,95 @@ execa "^1.0.0" strong-log-transformer "^2.0.0" -"@lerna/clean@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.5.0.tgz#38e0e58443fa43c19b3eeffcafa46cf254a328ee" - integrity sha512-bHUFF6Wv7ms81Tmwe56xk296oqU74Sg9NSkUCDG4kZLpYZx347Aw+89ZPTlaSmUwqCgEXKYLr65ZVVvKmflpcA== +"@lerna/clean@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/clean/-/clean-3.6.0.tgz#9a9d73324389cba694b19a913229c19d58e62485" + integrity sha512-4LodI/jh8IEYtqnrY/OFSpWn5YfDWoDv+5QjiJpd91EjW9vjmkvyhzQ5fG9KtltwgYVn/NJ5zlI1WfmMEXvFFQ== dependencies: - "@lerna/command" "^3.5.0" - "@lerna/filter-options" "^3.5.0" - "@lerna/prompt" "^3.3.1" - "@lerna/rimraf-dir" "^3.3.0" + "@lerna/command" "^3.6.0" + "@lerna/filter-options" "^3.6.0" + "@lerna/prompt" "^3.6.0" + "@lerna/rimraf-dir" "^3.6.0" p-map "^1.2.0" p-map-series "^1.0.0" p-waterfall "^1.0.0" -"@lerna/cli@^3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.2.0.tgz#3ed25bcbc0b8f0878bc6a102ee0296f01476cfdf" - integrity sha512-JdbLyTxHqxUlrkI+Ke+ltXbtyA+MPu9zR6kg/n8Fl6uaez/2fZWtReXzYi8MgLxfUFa7+1OHWJv4eAMZlByJ+Q== +"@lerna/cli@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/cli/-/cli-3.6.0.tgz#f86c16a095bd5506e927b9385ddefb13c605b1df" + integrity sha512-FGCx7XOLpqmU5eFOlo0Lt0hRZraxSUTEWM0bce0p+HNpOxBc91o6d2tenW1azPYFP9HzsMQey1NBtU0ofJJeog== dependencies: "@lerna/global-options" "^3.1.3" dedent "^0.7.0" - npmlog "^4.1.2" + libnpm "^2.0.1" yargs "^12.0.1" -"@lerna/collect-updates@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.5.0.tgz#e81c17f89367e71ff59e0f244a523a1dc13891c5" - integrity sha512-rFCng14K8vHyrDJSAacj6ABKKT/TxZdpL9uPEtZN7DsoJKlKPzqFeRvRGA2+ed/I6mEm4ltauEjEpKG5O6xqtw== +"@lerna/collect-updates@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/collect-updates/-/collect-updates-3.6.0.tgz#8520d64852c5059b453db53afee22539853f2bde" + integrity sha512-knliEz3phY51SGnwDhhYqx6SJN6y9qh/gZrZgQ7ogqz1UgA/MyJb27gszjsyyG6jUQshimBpjsG7OMwjt8+n9A== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/describe-ref" "^3.5.0" + "@lerna/describe-ref" "^3.6.0" + libnpm "^2.0.1" minimatch "^3.0.4" - npmlog "^4.1.2" slash "^1.0.0" -"@lerna/command@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.5.0.tgz#6b5cc530653aaa631061c1c234f3bc4408cb9613" - integrity sha512-C/0e7qPbuKZ9vEqzRePksoKDJk4TOWzsU5qaPP/ikqc6vClJbKucsIehk3za6glSjlgLCJpzBTF2lFjHfb+JNw== +"@lerna/command@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/command/-/command-3.6.0.tgz#b3289d1f72d2bebb7375d424b1778121a3d4e82c" + integrity sha512-BGpXaY2WrxPcIiZX0aATO2HQBatvYT7Qy46lqMnV9RrTePYJ1PPbX1nMzLXSxgrnnlTcTwJNEkw/TL9Xzrph7Q== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/package-graph" "^3.1.2" - "@lerna/project" "^3.5.0" - "@lerna/validation-error" "^3.0.0" - "@lerna/write-log-file" "^3.0.0" + "@lerna/package-graph" "^3.6.0" + "@lerna/project" "^3.6.0" + "@lerna/validation-error" "^3.6.0" + "@lerna/write-log-file" "^3.6.0" dedent "^0.7.0" execa "^1.0.0" is-ci "^1.0.10" + libnpm "^2.0.1" lodash "^4.17.5" - npmlog "^4.1.2" -"@lerna/conventional-commits@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.5.0.tgz#1c08013c48acdbdbf6400ccfbe1200a18e1f11ce" - integrity sha512-roKPILPYnDWiCDxOeBQ0cObJ2FbDgzJSToxr1ZwIqvJU5hGQ4RmooCf8GHcCW9maBJz7ETeestv8M2mBUgBPbg== +"@lerna/conventional-commits@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/conventional-commits/-/conventional-commits-3.6.0.tgz#b44edb60e23453d8b15dcd1accf28c421581a8c5" + integrity sha512-KkY3wd7w/tj76EEIhTMYZlSBk/5WkT2NA9Gr/EuSwKV70PYyVA55l1OGlikBUAnuqIjwyfw9x3y+OcbYI4aNEg== dependencies: - "@lerna/validation-error" "^3.0.0" + "@lerna/validation-error" "^3.6.0" conventional-changelog-angular "^5.0.2" conventional-changelog-core "^3.1.5" conventional-recommended-bump "^4.0.4" fs-extra "^7.0.0" get-stream "^4.0.0" - npm-package-arg "^6.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" semver "^5.5.0" -"@lerna/create-symlink@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-3.3.0.tgz#91de00fd576018ba4251f0c6a5b4b7f768f22a82" - integrity sha512-0lb88Nnq1c/GG+fwybuReOnw3+ah4dB81PuWwWwuqUNPE0n50qUf/M/7FfSb5JEh/93fcdbZI0La8t3iysNW1w== +"@lerna/create-symlink@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/create-symlink/-/create-symlink-3.6.0.tgz#f1815cde2fc9d8d2315dfea44ee880f2f1bc65f1" + integrity sha512-YG3lTb6zylvmGqKU+QYA3ylSnoLn+FyLH5XZmUsD0i85R884+EyJJeHx/zUk+yrL2ZwHS4RBUgJfC24fqzgPoA== dependencies: cmd-shim "^2.0.2" fs-extra "^7.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" -"@lerna/create@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.5.0.tgz#22f64a7af7a08cfbed4af2a01ff82307ee9e0b2b" - integrity sha512-ek4flHRmpMegZp9tP3RmuDhmMb9+/Hhy9B5eaZc5X5KWqDvFKJtn56sw+M9hNjiYehiimCwhaLWgE2WSikPvcQ== +"@lerna/create@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-3.6.0.tgz#4540c9ee69f63d11b3138eb5eac1942348643af1" + integrity sha512-21OunW25Y3Q/oynqWVk0znQFBvZ5tHyLPhzkJeomGmOj0il1RdOUiChu9G9AYsCaLDwBFR0ZFqvTgJ5iw/eaIg== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/command" "^3.5.0" + "@lerna/command" "^3.6.0" "@lerna/npm-conf" "^3.4.1" - "@lerna/validation-error" "^3.0.0" + "@lerna/validation-error" "^3.6.0" camelcase "^4.1.0" dedent "^0.7.0" fs-extra "^7.0.0" globby "^8.0.1" init-package-json "^1.10.3" - npm-package-arg "^6.0.0" + libnpm "^2.0.1" + p-reduce "^1.0.0" pify "^3.0.0" semver "^5.5.0" slash "^1.0.0" @@ -825,60 +888,60 @@ validate-npm-package-name "^3.0.0" whatwg-url "^7.0.0" -"@lerna/describe-ref@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-3.5.0.tgz#d59090d201a0e587798496417373c7fbc1151fc4" - integrity sha512-XvecK2PSwUv4z+otib5moWJMI+h3mtAg8nFlfo4KbivVtD/sI11jfKsr3S75HuAwhVAa8tAijoAxmuBJSsTE1g== +"@lerna/describe-ref@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/describe-ref/-/describe-ref-3.6.0.tgz#29eda334c81cd4c0a2942f309936bcb69a4543a0" + integrity sha512-hVZJ2hYVbrrNiEG+dEg/Op4pYAbROkDZdiIUabAJffr0T/frcN+5es2HfmOC//4+78Cs1M9iTyQRoyC1RXS2BQ== dependencies: "@lerna/child-process" "^3.3.0" - npmlog "^4.1.2" + libnpm "^2.0.1" -"@lerna/diff@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.5.0.tgz#4d131a1045321bcea20d743f5cc7f0d0095cf027" - integrity sha512-iyZ0ZRPqH5Y5XEhOYoKS8H/8UXC/gZ/idlToMFHhUn1oTSd8v9HVU1c2xq1ge0u36ZH/fx/YydUk0A/KSv+p3Q== +"@lerna/diff@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/diff/-/diff-3.6.0.tgz#ea8a77e712daf951c05316c81fe4065bf6b5e22c" + integrity sha512-p5+VyYKuAnw6NFVrT4s9eBubFZEYlJmiR1mdVlwNtohqS86gERjrPtI0unUK/pxFKb1U2ZNo4fhSlPd+pLwfHg== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/command" "^3.5.0" - "@lerna/validation-error" "^3.0.0" - npmlog "^4.1.2" + "@lerna/command" "^3.6.0" + "@lerna/validation-error" "^3.6.0" + libnpm "^2.0.1" -"@lerna/exec@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.5.0.tgz#98f4e8719681c07a739241fadb4cbdbd9d518982" - integrity sha512-H5jeIueDiuNsxeuGKaP7HqTcenvMsFfBFeWr0W6knHv9NrOF8il34dBqYgApZEDSQ7+2fA3ghwWbF+jUGTSh/A== +"@lerna/exec@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/exec/-/exec-3.6.0.tgz#757e96e890e436a31efc59dc72c5a7c2944d1a44" + integrity sha512-lwLYASpS8FoQpVYLBpoZlS7bpzkO9pD3D9XeDDKZBodDhdZeCEx2Md2CxZU1RKYDSVIXA8oObvlUh1FEhRQv2w== dependencies: - "@lerna/batch-packages" "^3.1.2" + "@lerna/batch-packages" "^3.6.0" "@lerna/child-process" "^3.3.0" - "@lerna/command" "^3.5.0" - "@lerna/filter-options" "^3.5.0" + "@lerna/command" "^3.6.0" + "@lerna/filter-options" "^3.6.0" "@lerna/run-parallel-batches" "^3.0.0" - "@lerna/validation-error" "^3.0.0" + "@lerna/validation-error" "^3.6.0" -"@lerna/filter-options@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.5.0.tgz#34d1719908edbb52d4963c796df565a716282165" - integrity sha512-7pEQy1i5ynYOYjcSeo+Qaps4+Ais55RRdnT6/SLLBgyyHAMziflFLX5TnoyEaaXoU90iKfQ5z/ioEp6dFAXSMg== +"@lerna/filter-options@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-options/-/filter-options-3.6.0.tgz#8100a3f2e18a9772a61138711e1fe1f14969f814" + integrity sha512-6iUMZuvvXPL5EAF7Zo9azaZ6FxOq6tGbiSX8fUXgCdN+jlRjorvkzR+E0HS4bEGTWmV446lnLwdQLZuySfLcbQ== dependencies: - "@lerna/collect-updates" "^3.5.0" - "@lerna/filter-packages" "^3.0.0" + "@lerna/collect-updates" "^3.6.0" + "@lerna/filter-packages" "^3.6.0" dedent "^0.7.0" -"@lerna/filter-packages@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-3.0.0.tgz#5eb25ad1610f3e2ab845133d1f8d7d40314e838f" - integrity sha512-zwbY1J4uRjWRZ/FgYbtVkq7I3Nduwsg2V2HwLKSzwV2vPglfGqgovYOVkND6/xqe2BHwDX4IyA2+e7OJmLaLSA== +"@lerna/filter-packages@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/filter-packages/-/filter-packages-3.6.0.tgz#4cad0bd5b32974b546b845283ac870d62ea4646a" + integrity sha512-O/nIENV3LOqp/TiUIw3Ir6L/wUGFDeYBdJsJTQDlTAyHZsgYA1OIn9FvlW8nqBu1bNLzoBVHXh3c5azx1kE+Hg== dependencies: - "@lerna/validation-error" "^3.0.0" + "@lerna/validation-error" "^3.6.0" + libnpm "^2.0.1" multimatch "^2.1.0" - npmlog "^4.1.2" -"@lerna/get-npm-exec-opts@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.0.0.tgz#8fc7866e8d8e9a2f2dc385287ba32eb44de8bdeb" - integrity sha512-arcYUm+4xS8J3Palhl+5rRJXnZnFHsLFKHBxznkPIxjwGQeAEw7df38uHdVjEQ+HNeFmHnBgSqfbxl1VIw5DHg== +"@lerna/get-npm-exec-opts@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/get-npm-exec-opts/-/get-npm-exec-opts-3.6.0.tgz#ea595eb28d1f34ba61a92ee8391f374282b4b76e" + integrity sha512-ruH6KuLlt75aCObXfUIdVJqmfVq7sgWGq5mXa05vc1MEqxTIiU23YiJdWzofQOOUOACaZkzZ4K4Nu7wXEg4Xgg== dependencies: - npmlog "^4.1.2" + libnpm "^2.0.1" "@lerna/global-options@^3.1.3": version "3.1.3" @@ -893,68 +956,69 @@ "@lerna/child-process" "^3.3.0" semver "^5.5.0" -"@lerna/import@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.5.0.tgz#b42d368378d53c664a3324c1e2fc656a23819f12" - integrity sha512-vgI6lMEzd1ODgi75cmAlfPYylaK37WY3E2fwKyO/lj6UKSGj46dVSK0KwTRHx33tu4PLvPzFi5C6nbY57o5ykQ== +"@lerna/import@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/import/-/import-3.6.0.tgz#84ef5eea61ab9a284054be32367906d092aadab5" + integrity sha512-8jxNRbAaa4mvMJr0u+sy75gMFPyWfxLHEp+pDs73x1oqMZhpS8O5901QMnpZyRyOvJRhoBJd5hBX2dpsLxC6Xw== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/command" "^3.5.0" - "@lerna/prompt" "^3.3.1" - "@lerna/validation-error" "^3.0.0" + "@lerna/command" "^3.6.0" + "@lerna/prompt" "^3.6.0" + "@lerna/validation-error" "^3.6.0" dedent "^0.7.0" fs-extra "^7.0.0" p-map-series "^1.0.0" -"@lerna/init@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.5.0.tgz#40796d70dc261907e4149df62db8acbda8dc56fc" - integrity sha512-V21/UWj34Mph+9NxIGH1kYcuJAp+uFjfG8Ku2nMy62OGL3553+YQ+Izr+R6egY8y/99UMCDpi5gkQni5eGv3MA== +"@lerna/init@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/init/-/init-3.6.0.tgz#6e23c3db632b713e23250d33519ed844a79a145e" + integrity sha512-MTLy3rmMdvpXRmDdoYiVPx7I8sXH4dquq/0MxntL5VxSVh/ZS1HsbrjyRqpdkUKWD9QguxR/w0pzOjVvCeM8CQ== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/command" "^3.5.0" + "@lerna/command" "^3.6.0" fs-extra "^7.0.0" p-map "^1.2.0" write-json-file "^2.3.0" -"@lerna/link@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.5.0.tgz#ae7fe19df1bb0555dfa5aafcaf45c7fad537d81a" - integrity sha512-KSu1mhxwNRmguqMqUTJd4c7QIk9/xmxJxbmMkA71OaJd4fwondob6DyI/B17NIWutdLbvSWQ7pRlFOPxjQVoUw== +"@lerna/link@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/link/-/link-3.6.0.tgz#490f14216b489fd66d9d3d3d0765f75dbbf52178" + integrity sha512-Xk8TTAE4EWGyhxLuPxWdyS7i7vfsM5igb6tEyhZm94XUdlA4PmMOYe25BfO7SM/9LYroFknZeDyWAebye3r+PA== dependencies: - "@lerna/command" "^3.5.0" - "@lerna/package-graph" "^3.1.2" - "@lerna/symlink-dependencies" "^3.3.0" + "@lerna/command" "^3.6.0" + "@lerna/package-graph" "^3.6.0" + "@lerna/symlink-dependencies" "^3.6.0" p-map "^1.2.0" slash "^1.0.0" -"@lerna/list@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.5.0.tgz#ae38724f1cdc588fde8495385e3b1a9aeac0c80f" - integrity sha512-T+NZBQ/l6FmZklgrtFuN7luMs3AC/BoS52APOPrM7ZmxW4nenvov0xMwQW1783w/t365YDkDlYd5gM0nX3D1Hg== +"@lerna/list@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/list/-/list-3.6.0.tgz#18ae4b1e375ef1329261c9d21be27098ca0edf63" + integrity sha512-hlQOJkg8K3XXUVXotofP71XsgkhXkkmU/EkqlNg15D78MjzhT+p1wCbG5m89K3tzvjcWVeZwU6L0elaOIXVyCw== dependencies: - "@lerna/command" "^3.5.0" - "@lerna/filter-options" "^3.5.0" - "@lerna/listable" "^3.0.0" - "@lerna/output" "^3.0.0" + "@lerna/command" "^3.6.0" + "@lerna/filter-options" "^3.6.0" + "@lerna/listable" "^3.6.0" + "@lerna/output" "^3.6.0" -"@lerna/listable@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.0.0.tgz#27209b1382c87abdbc964220e75c247d803d4199" - integrity sha512-HX/9hyx1HLg2kpiKXIUc1EimlkK1T58aKQ7ovO7rQdTx9ForpefoMzyLnHE1n4XrUtEszcSWJIICJ/F898M6Ag== +"@lerna/listable@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/listable/-/listable-3.6.0.tgz#25c9cc062ae0d3e78c53da30cdf9f011696ae48f" + integrity sha512-fz63+zlqrJ9KQxIiv0r7qtufM4DEinSayAuO8YJuooz+1ctIP7RvMEQNvYI/E9tDlUo9Q0de68b5HbKrpmA5rQ== dependencies: + "@lerna/batch-packages" "^3.6.0" chalk "^2.3.1" columnify "^1.5.4" -"@lerna/log-packed@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-3.0.4.tgz#6d1f6ce5ca68b9971f2a27f0ecf3c50684be174a" - integrity sha512-vVQHgMagE2wnbxhNY9nFkdu+Cx2TsyWalkJfkxbNzmo6gOCrDsxCBDj9vTEV8Q+4aWx0C0Bsc0sB2Eb8y/+ofA== +"@lerna/log-packed@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/log-packed/-/log-packed-3.6.0.tgz#bed96c2bdd47f076d9957d0c6069b2edc1518145" + integrity sha512-T/J41zMkzpWB5nbiTRS5PmYTFn74mJXe6RQA2qhkdLi0UqnTp97Pux1loz3jsJf2yJtiQUnyMM7KuKIAge0Vlw== dependencies: byte-size "^4.0.3" columnify "^1.5.4" has-unicode "^2.0.1" - npmlog "^4.1.2" + libnpm "^2.0.1" "@lerna/npm-conf@^3.4.1": version "3.4.1" @@ -964,159 +1028,154 @@ config-chain "^1.1.11" pify "^3.0.0" -"@lerna/npm-dist-tag@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.3.0.tgz#e1c5ab67674216d901266a16846b21cc81ff6afd" - integrity sha512-EtZJXzh3w5tqXEev+EBBPrWKWWn0WgJfxm4FihfS9VgyaAW8udIVZHGkIQ3f+tBtupcAzA9Q8cQNUkGF2efwmA== +"@lerna/npm-dist-tag@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-dist-tag/-/npm-dist-tag-3.6.0.tgz#8f8c8567810bd9ee1c1277a71b57cec1acc101f4" + integrity sha512-qX6IfQPX9Tum1LRjvjgj/yr2FYbc9dfHyeh7RI9zJ8pGncWbksBmnMcvoxF0Eu4+d7MjjIGfEnIp9LIl4MHSIA== dependencies: - "@lerna/child-process" "^3.3.0" - "@lerna/get-npm-exec-opts" "^3.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" + npm-registry-fetch "^3.8.0" -"@lerna/npm-install@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-3.3.0.tgz#16d00ffd668d11b2386b3ac68bdac2cf8320e533" - integrity sha512-WoVvKdS8ltROTGSNQwo6NDq0YKnjwhvTG4li1okcN/eHKOS3tL9bxbgPx7No0wOq5DKBpdeS9KhAfee6LFAZ5g== +"@lerna/npm-install@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-install/-/npm-install-3.6.0.tgz#314fc0d0c35429e2b5db1e7de87b3ddb1ab77606" + integrity sha512-RKV31VdrBZKjmKfq25JG4mIHJ8NAOsLKq/aYSaBs8zP+uwXH7RU39saVfv9ReKiAzhKE2ghOG2JeMdIHtYnPNA== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/get-npm-exec-opts" "^3.0.0" + "@lerna/get-npm-exec-opts" "^3.6.0" fs-extra "^7.0.0" - npm-package-arg "^6.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" signal-exit "^3.0.2" write-pkg "^3.1.0" -"@lerna/npm-publish@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.3.1.tgz#30384665d7ee387343332ece62ca231207bbabea" - integrity sha512-bVTlWIcBL6Zpyzqvr9C7rxXYcoPw+l7IPz5eqQDNREj1R39Wj18OWB2KTJq8l7LIX7Wf4C2A1uT5hJaEf9BuvA== +"@lerna/npm-publish@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-publish/-/npm-publish-3.6.0.tgz#8981a9744779c55955a8c4249fe6b44a0485f9d3" + integrity sha512-k4yF8ursajoGRlJeRh7xdeGN0HV/ALt5qImUnpTliux0213jqxA0YigiD8WSaXpvSqxSFyvh38DbJhhy9q+NuQ== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/get-npm-exec-opts" "^3.0.0" + "@lerna/get-npm-exec-opts" "^3.6.0" "@lerna/has-npm-version" "^3.3.0" - "@lerna/log-packed" "^3.0.4" + "@lerna/log-packed" "^3.6.0" fs-extra "^7.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" p-map "^1.2.0" -"@lerna/npm-run-script@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-3.3.0.tgz#3c79601c27c67121155b20e039be53130217db72" - integrity sha512-YqDguWZzp4jIomaE4aWMUP7MIAJAFvRAf6ziQLpqwoQskfWLqK5mW0CcszT1oLjhfb3cY3MMfSTFaqwbdKmICg== +"@lerna/npm-run-script@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/npm-run-script/-/npm-run-script-3.6.0.tgz#4b97e6f571ae9fdabed21d5d4fc35a2b7e9d5267" + integrity sha512-6DRNFma30ex9r1a8mMDXziSRHf1/mo//hnvW1Zc1ctBh+7PU4I8n3A2ht/+742vtoTQH93Iqs3QSJl2KOLSsYg== dependencies: "@lerna/child-process" "^3.3.0" - "@lerna/get-npm-exec-opts" "^3.0.0" - npmlog "^4.1.2" + "@lerna/get-npm-exec-opts" "^3.6.0" + libnpm "^2.0.1" -"@lerna/output@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/output/-/output-3.0.0.tgz#4ed4a30ed2f311046b714b3840a090990ba3ce35" - integrity sha512-EFxnSbO0zDEVKkTKpoCUAFcZjc3gn3DwPlyTDxbeqPU7neCfxP4rA4+0a6pcOfTlRS5kLBRMx79F2TRCaMM3DA== +"@lerna/output@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/output/-/output-3.6.0.tgz#a69384bc685cf3b21aa1bfc697eb2b9db3333d0b" + integrity sha512-9sjQouf6p7VQtVCRnzoTGlZyURd48i3ha3WBHC/UBJnHZFuXMqWVPKNuvnMf2kRXDyoQD+2mNywpmEJg5jOnRg== dependencies: - npmlog "^4.1.2" + libnpm "^2.0.1" -"@lerna/package-graph@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.1.2.tgz#b70298a3a8c82e12090da33233bf242223a38f20" - integrity sha512-9wIWb49I1IJmyjPdEVZQ13IAi9biGfH/OZHOC04U2zXGA0GLiY+B3CAx6FQvqkZ8xEGfqzmXnv3LvZ0bQfc1aQ== +"@lerna/package-graph@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/package-graph/-/package-graph-3.6.0.tgz#d13e6e80d30e2e29226d335412997b9ddf646305" + integrity sha512-Xtldh3DTiC3cPDrs6OY5URiuRXGPMIN6uFKcx59rOu3TkqYRt346jRyX+hm85996Y/pboo3+JuQlonvuEP/9QQ== dependencies: - "@lerna/validation-error" "^3.0.0" - npm-package-arg "^6.0.0" + "@lerna/validation-error" "^3.6.0" + libnpm "^2.0.1" semver "^5.5.0" -"@lerna/package@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/package/-/package-3.0.0.tgz#14afc9a6cb1f7f7b23c1d7c7aa81bdac7d44c0e5" - integrity sha512-djzEJxzn212wS8d9znBnlXkeRlPL7GqeAYBykAmsuq51YGvaQK67Umh5ejdO0uxexF/4r7yRwgrlRHpQs8Rfqg== +"@lerna/package@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/package/-/package-3.6.0.tgz#1095b91d277820b7ae8a2cfeeb73d57c6cd9b17e" + integrity sha512-XbXcjwPKA1V640mqjEicpBriO6QcNtocdfLAtEUP4uCKkRx5r9h7DdznQMCoSJYJF6Gh/PpLokPUItfMhJP3Hg== dependencies: - npm-package-arg "^6.0.0" + libnpm "^2.0.1" write-pkg "^3.1.0" -"@lerna/project@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.5.0.tgz#ac5c7b3c49318552b29ccb7a471a657fd57d3091" - integrity sha512-uFDzqwrD7a/tTohQoo0voTsRy2cgl9D1ZOU2pHZzHzow9S1M8E0x5q3hJI2HlwsZry9IUugmDUGO6UddTjwm3Q== +"@lerna/project@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/project/-/project-3.6.0.tgz#b5dd2b94fae6f58478be1c53962c2570498867ab" + integrity sha512-pEOZF1igGFqs+qWog6cJWqVyBUX21xSqrlcgeN0yzqzI36VMHozmf/u7dgclIb5MylWk5Yp87KCKswBF4hrcuQ== dependencies: - "@lerna/package" "^3.0.0" - "@lerna/validation-error" "^3.0.0" + "@lerna/package" "^3.6.0" + "@lerna/validation-error" "^3.6.0" cosmiconfig "^5.0.2" dedent "^0.7.0" dot-prop "^4.2.0" glob-parent "^3.1.0" globby "^8.0.1" + libnpm "^2.0.1" load-json-file "^4.0.0" - npmlog "^4.1.2" p-map "^1.2.0" resolve-from "^4.0.0" write-json-file "^2.3.0" -"@lerna/prompt@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.3.1.tgz#ec53f9034a7a02a671627241682947f65078ab88" - integrity sha512-eJhofrUCUaItMIH6et8kI7YqHfhjWqGZoTsE+40NRCfAraOMWx+pDzfRfeoAl3qeRAH2HhNj1bkYn70FbUOxuQ== +"@lerna/prompt@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/prompt/-/prompt-3.6.0.tgz#b17cc464dec9d830619723e879dc747367378217" + integrity sha512-nyAjPMolJ/ZRAAVcXrUH89C4n1SiWvLh4xWNvWYKLcf3PI5yges35sDFP/HYrM4+cEbkNFuJCRq6CxaET4PRsg== dependencies: inquirer "^6.2.0" - npmlog "^4.1.2" + libnpm "^2.0.1" -"@lerna/publish@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.5.0.tgz#e6543375457846557f3cb803f7795a577333a941" - integrity sha512-CoK+wzPP/0xG54Iwf9WEbB3VgwayoiVYsubhYpC072Ue7V6mGXQh97KZMm8NzCoMDg35ZGqa85Kxpeqd5hqQlw== +"@lerna/publish@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/publish/-/publish-3.6.0.tgz#7985b8c549c83114180e99a9e291e8b82db57aac" + integrity sha512-F2bT96ZS7NJfid6T4a6TSanpVUQ4VOuhjPBPX2hagt5gnocm7lluvAFR7dl/cbEgmKIg2zJQnfAPTYjrtxXMVg== dependencies: - "@lerna/batch-packages" "^3.1.2" - "@lerna/check-working-tree" "^3.5.0" + "@lerna/batch-packages" "^3.6.0" + "@lerna/check-working-tree" "^3.6.0" "@lerna/child-process" "^3.3.0" - "@lerna/collect-updates" "^3.5.0" - "@lerna/command" "^3.5.0" - "@lerna/describe-ref" "^3.5.0" - "@lerna/get-npm-exec-opts" "^3.0.0" + "@lerna/collect-updates" "^3.6.0" + "@lerna/command" "^3.6.0" + "@lerna/describe-ref" "^3.6.0" + "@lerna/get-npm-exec-opts" "^3.6.0" "@lerna/npm-conf" "^3.4.1" - "@lerna/npm-dist-tag" "^3.3.0" - "@lerna/npm-publish" "^3.3.1" - "@lerna/output" "^3.0.0" - "@lerna/prompt" "^3.3.1" - "@lerna/run-lifecycle" "^3.4.1" + "@lerna/npm-dist-tag" "^3.6.0" + "@lerna/npm-publish" "^3.6.0" + "@lerna/output" "^3.6.0" + "@lerna/prompt" "^3.6.0" + "@lerna/run-lifecycle" "^3.6.0" "@lerna/run-parallel-batches" "^3.0.0" - "@lerna/validation-error" "^3.0.0" - "@lerna/version" "^3.5.0" + "@lerna/validation-error" "^3.6.0" + "@lerna/version" "^3.6.0" fs-extra "^7.0.0" - libnpmaccess "^3.0.0" - npm-package-arg "^6.0.0" + libnpm "^2.0.1" npm-registry-fetch "^3.8.0" - npmlog "^4.1.2" p-finally "^1.0.0" p-map "^1.2.0" p-pipe "^1.2.0" p-reduce "^1.0.0" semver "^5.5.0" -"@lerna/resolve-symlink@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-3.3.0.tgz#c5d99a60cb17e2ea90b3521a0ba445478d194a44" - integrity sha512-KmoPDcFJ2aOK2inYHbrsiO9SodedUj0L1JDvDgirVNIjMUaQe2Q6Vi4Gh+VCJcyB27JtfHioV9R2NxU72Pk2hg== +"@lerna/resolve-symlink@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/resolve-symlink/-/resolve-symlink-3.6.0.tgz#985344796b704ff32afa923901e795e80741b86e" + integrity sha512-TVOAEqHJSQVhNDMFCwEUZPaOETqHDQV1TQWQfC8ZlOqyaUQ7veZUbg0yfG7RPNzlSpvF0ZaGFeR0YhYDAW03GA== dependencies: fs-extra "^7.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" read-cmd-shim "^1.0.1" -"@lerna/rimraf-dir@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-3.3.0.tgz#687e9bb3668a9e540e281302a52d9a573860f5db" - integrity sha512-vSqOcZ4kZduiSprbt+y40qziyN3VKYh+ygiCdnbBbsaxpdKB6CfrSMUtrLhVFrqUfBHIZRzHIzgjTdtQex1KLw== +"@lerna/rimraf-dir@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/rimraf-dir/-/rimraf-dir-3.6.0.tgz#a02c4ad14d9a65c005021da79d545702b7085a74" + integrity sha512-2CfyWP1lqxDET+SfwGlLUfgqGF4vz9TYDrmb7Zi//g7IFCo899uU2vWOrEcdWTgbKE3Qgwwfk9c008w5MWUhog== dependencies: "@lerna/child-process" "^3.3.0" - npmlog "^4.1.2" + libnpm "^2.0.1" path-exists "^3.0.0" rimraf "^2.6.2" -"@lerna/run-lifecycle@^3.4.1": - version "3.4.1" - resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-3.4.1.tgz#6d7e44eada31cb4ec78b18ef050da0d86f6c892b" - integrity sha512-N/hi2srM9A4BWEkXccP7vCEbf4MmIuALF00DTBMvc0A/ccItwUpl3XNuM7+ADDRK0mkwE3hDw89lJ3A7f8oUQw== +"@lerna/run-lifecycle@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/run-lifecycle/-/run-lifecycle-3.6.0.tgz#2381fd827b4a4135613e7d73d25ae76b7af5e6ef" + integrity sha512-/1+vAZnckgKwHVgWG0plVO24erNWUduz9htMOO9wuOfglTnHlMRqDc3s9B/OIKxGDkyzEvxqzfzq3c6JqEolRQ== dependencies: "@lerna/npm-conf" "^3.4.1" - npm-lifecycle "^2.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" "@lerna/run-parallel-batches@^3.0.0": version "3.0.0" @@ -1126,40 +1185,40 @@ p-map "^1.2.0" p-map-series "^1.0.0" -"@lerna/run@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.5.0.tgz#f2bf479d8ef12e6a11be5a6404bb1a83eb56146c" - integrity sha512-BnPD52tj794xG2Xsc4FvgksyFX2CLmSR28TZw/xASEuy14NuQYMZkvbaj61SEhyOEsq7pLhHE5PpfbIv2AIFJw== - dependencies: - "@lerna/batch-packages" "^3.1.2" - "@lerna/command" "^3.5.0" - "@lerna/filter-options" "^3.5.0" - "@lerna/npm-run-script" "^3.3.0" - "@lerna/output" "^3.0.0" +"@lerna/run@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/run/-/run-3.6.0.tgz#f545fcde889d7a1433b3f2cc444eeec39713ea62" + integrity sha512-OYa5pQTOiES/h9rg8vwnt0nYU/wLKUQmFYhMUxdX3lXYpoIcQ28PR7qPG1CVhex4KAU2OW42a7vnm5MAOoScDg== + dependencies: + "@lerna/batch-packages" "^3.6.0" + "@lerna/command" "^3.6.0" + "@lerna/filter-options" "^3.6.0" + "@lerna/npm-run-script" "^3.6.0" + "@lerna/output" "^3.6.0" "@lerna/run-parallel-batches" "^3.0.0" "@lerna/timer" "^3.5.0" - "@lerna/validation-error" "^3.0.0" + "@lerna/validation-error" "^3.6.0" p-map "^1.2.0" -"@lerna/symlink-binary@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-3.3.0.tgz#99ea570b21baabd61ecab27582eeb1d7b2c5f9cf" - integrity sha512-zRo6CimhvH/VJqCFl9T4IC6syjpWyQIxEfO2sBhrapEcfwjtwbhoGgKwucsvt4rIpFazCw63jQ/AXMT27KUIHg== +"@lerna/symlink-binary@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/symlink-binary/-/symlink-binary-3.6.0.tgz#935a5b98908578da7f9eed20818899f728b9f3d9" + integrity sha512-h69AQBBWgZOEzQ1RJEYQ7Ou6llrJNhNNkpqT6k8qSWZ93iXyFmLE4hWoxMXXHFmxmQ0CqjEYKmeLV1Dr5DKT4g== dependencies: - "@lerna/create-symlink" "^3.3.0" - "@lerna/package" "^3.0.0" + "@lerna/create-symlink" "^3.6.0" + "@lerna/package" "^3.6.0" fs-extra "^7.0.0" p-map "^1.2.0" read-pkg "^3.0.0" -"@lerna/symlink-dependencies@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-3.3.0.tgz#13bcaed3e37986ab01b13498a459c7f609397dc3" - integrity sha512-IRngSNCmuD5uBKVv23tHMvr7Mplti0lKHilFKcvhbvhAfu6m/Vclxhkfs/uLyHzG+DeRpl/9o86SQET3h4XDhg== +"@lerna/symlink-dependencies@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/symlink-dependencies/-/symlink-dependencies-3.6.0.tgz#76e9d54f6fd3af3e24221cce3ee546e5657ea2d8" + integrity sha512-mLpbWLidAU5Xi7bc9Fj8Yt/9XvDczzWocnS/yEe0E6RqWXh2KK+4VR9H24rLywBAWTv2s4GEXrb/ofbPb8gwBQ== dependencies: - "@lerna/create-symlink" "^3.3.0" - "@lerna/resolve-symlink" "^3.3.0" - "@lerna/symlink-binary" "^3.3.0" + "@lerna/create-symlink" "^3.6.0" + "@lerna/resolve-symlink" "^3.6.0" + "@lerna/symlink-binary" "^3.6.0" fs-extra "^7.0.0" p-finally "^1.0.0" p-map "^1.2.0" @@ -1170,32 +1229,32 @@ resolved "https://registry.yarnpkg.com/@lerna/timer/-/timer-3.5.0.tgz#8dee6acf002c55de64678c66ef37ca52143f1b9b" integrity sha512-TAb99hqQN6E3JBGtG9iyZNPq1/DbmqgBOeNrKtdJsGvIeX/NGLgUDWMrj2h04V4O+jpBFmSf6HIld6triKmxCA== -"@lerna/validation-error@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-3.0.0.tgz#a27e90051c3ba71995e2a800a43d94ad04b3e3f4" - integrity sha512-5wjkd2PszV0kWvH+EOKZJWlHEqCTTKrWsvfHnHhcUaKBe/NagPZFWs+0xlsDPZ3DJt5FNfbAPAnEBQ05zLirFA== +"@lerna/validation-error@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/validation-error/-/validation-error-3.6.0.tgz#550cf66bb2ef88edc02e36017b575a7a9100d5d8" + integrity sha512-MWltncGO5VgMS0QedTlZCjFUMF/evRjDMMHrtVorkIB2Cp5xy0rkKa8iDBG43qpUWeG1giwi58yUlETBcWfILw== dependencies: - npmlog "^4.1.2" + libnpm "^2.0.1" -"@lerna/version@^3.5.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.5.0.tgz#df6d4d8ef077b51962126c0f52c800d98c53cc26" - integrity sha512-vxuGkUSfjJuvOIgPG7SDXVmk4GPwJF9F+uhDW9T/wJzTk4UaxL37GpBeJDo43eutQ7mwluP+t88Luwf8S3WXlA== +"@lerna/version@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/version/-/version-3.6.0.tgz#7360d8a93b1cc5fe6a7588d7266812b916a281f7" + integrity sha512-V1f3fNM5ELGHmF824Wc8ah505SMpfiBqOHAIiW+u9soH/3W/t256c1P9UeaDh5blWAk3HeZMzbpRZ9Nlpf6aQA== dependencies: - "@lerna/batch-packages" "^3.1.2" - "@lerna/check-working-tree" "^3.5.0" + "@lerna/batch-packages" "^3.6.0" + "@lerna/check-working-tree" "^3.6.0" "@lerna/child-process" "^3.3.0" - "@lerna/collect-updates" "^3.5.0" - "@lerna/command" "^3.5.0" - "@lerna/conventional-commits" "^3.5.0" - "@lerna/output" "^3.0.0" - "@lerna/prompt" "^3.3.1" - "@lerna/run-lifecycle" "^3.4.1" - "@lerna/validation-error" "^3.0.0" + "@lerna/collect-updates" "^3.6.0" + "@lerna/command" "^3.6.0" + "@lerna/conventional-commits" "^3.6.0" + "@lerna/output" "^3.6.0" + "@lerna/prompt" "^3.6.0" + "@lerna/run-lifecycle" "^3.6.0" + "@lerna/validation-error" "^3.6.0" chalk "^2.3.1" dedent "^0.7.0" + libnpm "^2.0.1" minimatch "^3.0.4" - npmlog "^4.1.2" p-map "^1.2.0" p-pipe "^1.2.0" p-reduce "^1.0.0" @@ -1204,198 +1263,1061 @@ slash "^1.0.0" temp-write "^3.4.0" -"@lerna/write-log-file@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-3.0.0.tgz#2f95fee80c6821fe1ee6ccf8173d2b4079debbd2" - integrity sha512-SfbPp29lMeEVOb/M16lJwn4nnx5y+TwCdd7Uom9umd7KcZP0NOvpnX0PHehdonl7TyHZ1Xx2maklYuCLbQrd/A== +"@lerna/write-log-file@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@lerna/write-log-file/-/write-log-file-3.6.0.tgz#b8d5a7efc84fa93cbd67d724d11120343b2a849a" + integrity sha512-OkLK99V6sYXsJsYg+O9wtiFS3z6eUPaiz2e6cXJt80mfIIdI1t2dnmyua0Ib5cZWExQvx2z6Y32Wlf0MnsoNsA== + dependencies: + libnpm "^2.0.1" + write-file-atomic "^2.3.0" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@oclif/color@^0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@oclif/color/-/color-0.0.0.tgz#54939bbd16d1387511bf1a48ccda1a417248e6a9" + integrity sha512-KKd3W7eNwfNF061tr663oUNdt8EMnfuyf5Xv55SGWA1a0rjhWqS/32P7OeB7CbXcJUBdfVrPyR//1afaW12AWw== + dependencies: + ansi-styles "^3.2.1" + supports-color "^5.4.0" + tslib "^1" + +"@oclif/command@^1.5.3", "@oclif/command@^1.5.8": + version "1.5.8" + resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.5.8.tgz#cd09d4f3183123548cb25d1b12b92e41277ac3e9" + integrity sha512-+Xuqp7by9jmB+GvR2r450wUXkCpZVdeOXQD0mLSEm3h+Mxhp0NPHuhzXZQvLI0/2fXR+cmJLv1CfpaCYaflL/g== + dependencies: + "@oclif/errors" "^1.2.2" + "@oclif/parser" "^3.7.2" + debug "^4.1.0" + semver "^5.6.0" + +"@oclif/config@^1.12.4": + version "1.12.4" + resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.12.4.tgz#664f7f43c79a5b6674c35d1fd4a98c596c8a8630" + integrity sha512-lZR5Qs9NEbQ9PsOZN0nALMBisTTa53ajRsyuhMwSQtDP8B7jISqWS9czYRfn9eYA+/xWgcjwRILCHuJFtdEuTA== + dependencies: + debug "^4.1.1" + tslib "^1.9.3" + +"@oclif/errors@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@oclif/errors/-/errors-1.2.2.tgz#9d8f269b15f13d70aa93316fed7bebc24688edc2" + integrity sha512-Eq8BFuJUQcbAPVofDxwdE0bL14inIiwt5EaKRVY9ZDIG11jwdXZqiQEECJx0VfnLyUZdYfRd/znDI/MytdJoKg== + dependencies: + clean-stack "^1.3.0" + fs-extra "^7.0.0" + indent-string "^3.2.0" + strip-ansi "^5.0.0" + wrap-ansi "^4.0.0" + +"@oclif/linewrap@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@oclif/linewrap/-/linewrap-1.0.0.tgz#aedcb64b479d4db7be24196384897b5000901d91" + integrity sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw== + +"@oclif/parser@^3.7.2": + version "3.7.2" + resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.7.2.tgz#b06c73377a1f027f10444109a8a4a6cc31ffd8ba" + integrity sha512-ssYXztaf9TuOGCJQOYMg62L1Q4y2lB4wZORWng+Iy0ckP2A6IUnQy97V8YjAJkkohYZOu3Mga8LGfQcf+xdIIw== + dependencies: + "@oclif/linewrap" "^1.0.0" + chalk "^2.4.1" + tslib "^1.9.3" + +"@oclif/plugin-help@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-2.1.6.tgz#ae14cbe2c5cd3eaf5f796d205ffbe66d3e369173" + integrity sha512-M4kTERpPWNSM1Mga7K/zo9DWHLCVf2FRaIeXPoytmTPd+0kSvG3TR0Vc1bwx9/cxXoYyYGgEejwNlrfayr8FZw== + dependencies: + "@oclif/command" "^1.5.8" + chalk "^2.4.1" + indent-string "^3.2.0" + lodash.template "^4.4.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + widest-line "^2.0.1" + wrap-ansi "^4.0.0" + +"@oclif/plugin-not-found@^1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@oclif/plugin-not-found/-/plugin-not-found-1.2.2.tgz#3e601f6e4264d7a0268cd03c152d90aa9c0cec6d" + integrity sha512-SPlmiJFmTFltQT/owdzQwKgq6eq5AEKVwVK31JqbzK48bRWvEL1Ye60cgztXyZ4bpPn2Fl+KeL3FWFQX41qJuA== + dependencies: + "@oclif/color" "^0.0.0" + "@oclif/command" "^1.5.3" + cli-ux "^4.9.0" + fast-levenshtein "^2.0.6" + lodash "^4.17.11" + +"@oclif/screen@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-1.0.4.tgz#b740f68609dfae8aa71c3a6cab15d816407ba493" + integrity sha512-60CHpq+eqnTxLZQ4PGHYNwUX572hgpMHGPtTWMjdTMsAvlm69lZV/4ly6O3sAYkomo4NggGcomrDpBe34rxUqw== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + +"@samverschueren/stream-to-observable@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" + integrity sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg== + dependencies: + any-observable "^0.3.0" + +"@sentry/core@4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-4.4.2.tgz#562526bc634c087f04bbca68b09cedc4b41cc64d" + integrity sha512-hJyAodTCf4sZfVdf41Rtuzj4EsyzYq5rdMZ+zc2Vinwdf8D0/brHe91fHeO0CKXEb2P0wJsrjwMidG/ccq/M8A== + dependencies: + "@sentry/hub" "4.4.2" + "@sentry/minimal" "4.4.2" + "@sentry/types" "4.4.2" + "@sentry/utils" "4.4.2" + tslib "^1.9.3" + +"@sentry/hub@4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-4.4.2.tgz#1399556fda06fb83c4f186c4aa842725f520159c" + integrity sha512-oe9ytXkTWyD+QmOpVzHAqTbRV4Hc0ee2Nt6HvrDtRmlXzQxfvTWG2F8KYT6w8kzqg5klnuRpnsmgTTV3KuNBVQ== + dependencies: + "@sentry/types" "4.4.2" + "@sentry/utils" "4.4.2" + tslib "^1.9.3" + +"@sentry/minimal@4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-4.4.2.tgz#13fffc6b17a2401b6a79947838a637626ab80b10" + integrity sha512-GEZZiNvVgqFAESZhAe3vjwTInn13lI2bSI3ItQN4RUWKL/W4n/fwVoDJbkb1U8aWxanuMnRDEpKwyQv6zYTZfw== + dependencies: + "@sentry/hub" "4.4.2" + "@sentry/types" "4.4.2" + tslib "^1.9.3" + +"@sentry/node@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-4.4.2.tgz#549921d2df3cbf58ebcfb525c3005c3fec4739a3" + integrity sha512-8/KlSdfVhledZ6PS6muxZY5r2pqhw8MNSXP7AODR2qRrHwsbnirVgV21WIAYAjKXEfYQGbm69lyoaTJGazlQ3Q== + dependencies: + "@sentry/core" "4.4.2" + "@sentry/hub" "4.4.2" + "@sentry/types" "4.4.2" + "@sentry/utils" "4.4.2" + "@types/stack-trace" "0.0.29" + cookie "0.3.1" + https-proxy-agent "^2.2.1" + lsmod "1.0.0" + stack-trace "0.0.10" + tslib "^1.9.3" + +"@sentry/types@4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-4.4.2.tgz#f38dd3bc671cd2f5983a85553aebeac9c2286b17" + integrity sha512-QyQd6PKKIyjJgaq/RQjsxPJEWbXcuiWZ9RvSnhBjS5jj53HEzkM1qkbAFqlYHJ1DTJJ1EuOM4+aTmGzHe93zuA== + +"@sentry/utils@4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-4.4.2.tgz#e05a47e135ecef29e63a996f59aee8c8f792c222" + integrity sha512-j/Ad8G1abHlJdD2q7aWWbSOSeWB5M5v1R1VKL8YPlwEbSvvmEQWePhBKFI0qlnKd2ObdUQsj86pHEXJRSFNfCw== + dependencies: + "@sentry/types" "4.4.2" + tslib "^1.9.3" + +"@sindresorhus/is@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.12.0.tgz#55c37409c809e802efea25911a579731adfc6e07" + integrity sha512-9ve22cGrAKlSRvi8Vb2JIjzcaaQg79531yQHnF+hi/kOpsSj3Om8AyR1wcHrgl0u7U3vYQ7gmF5erZzOp4+51Q== + dependencies: + symbol-observable "^1.2.0" + +"@sindresorhus/tsconfig@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@sindresorhus/tsconfig/-/tsconfig-0.1.1.tgz#c13d7d79ab5a7b02c2374487d0822695a1baa496" + integrity sha512-n2KZlZ5tD2oc0CUNYtKSRSJr2PC6pqSG9n1JWvY12rU6UjkQ563czDUxCSEGyOEhhogvu8Ad5SonjH7DoBNa7w== + +"@snyk/dep-graph@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.1.2.tgz#a0377fbb29dd42bc12d1c2493b51a7b7fe0d334a" + integrity sha512-mCoAFKtmezBL61JOzLMzqqd/sXXxp0iektEwf4zw+sM3zuG4Tnmhf8OqNO6Wscn84bMIfLlI/nvECdxvSS7MTw== + dependencies: + graphlib "^2.1.5" + lodash "^4" + source-map-support "^0.5.9" + tslib "^1.9.3" + +"@snyk/gemfile@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.1.0.tgz#8c254dfc7739b2e8513c44c976fc41872d5f6af0" + integrity sha512-mLwF+ccuvRZMS0SxUAxA3dAp8mB3m2FxIsBIUWFTYvzxl+E4XTZb8uFrUqXHbcxhZH1Z8taHohNTbzXZn3M8ag== + +"@szmarczak/http-timer@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.1.tgz#6402258dfe467532b26649ef076b4d11f74fb612" + integrity sha512-WljfOGkmSJe8SUkl+4TPvN2ec0dpUGVyfTBQLoXJUiILs+wBSc4Kvp2N3aAWE4VwwDSLGdmD3/bufS5BgZpVSQ== + dependencies: + defer-to-connect "^1.0.1" + +"@types/anymatch@*": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.0.tgz#d1d55958d1fccc5527d4aba29fc9c4b942f563ff" + integrity sha512-7WcbyctkE8GTzogDb0ulRAEw7v8oIS54ft9mQTU7PfM0hp5e+8kpa+HeQ7IQrFbKtJXBKcZ4bh+Em9dTw5L6AQ== + +"@types/async@^2.0.50": + version "2.0.50" + resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.50.tgz#117540e026d64e1846093abbd5adc7e27fda7bcb" + integrity sha512-VMhZMMQgV1zsR+lX/0IBfAk+8Eb7dPVMWiQGFAt3qjo5x7Ml6b77jUo0e1C3ToD+XRDXqtrfw+6AB0uUsPEr3Q== + +"@types/babel__core@^7.0.4": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.0.4.tgz#14b30c11113bad353cabfaea73e327b48edb0f0e" + integrity sha512-2Y2RK1BN5BRFfhneGfQA8mmFmTANbzGgS5uQPluoRqGNWb6uAcefqxzNbqgxPpmPkLqKapQfmYcyyl5iAQV+fA== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/better-sqlite3@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-5.2.0.tgz#bb3c48ea4c9480ba719c72f64c13386581895e4c" + integrity sha512-9zvM1wtE/il/CE6+eIlczoqdRJlydJl0Li3kcloUhCKwgAPk+laRDyJGdy2Gw89tCE6TM++4CA8hy9MfU0Acvg== + dependencies: + "@types/integer" "*" + +"@types/bip32@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/bip32/-/bip32-1.0.0.tgz#792b26521267effbee4cb048f3425d114ad1e4f8" + integrity sha512-BXLhOIsl6vzm+OOHmb5aHMs/eCt3xY7sO9xDfMtNoQl5UyrUMR+QREFLZ1zDl0jt0mkFBhhp5mtEefUOQDTuRA== + dependencies: + "@types/node" "*" + +"@types/bip38@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/bip38/-/bip38-2.0.0.tgz#2c8bed75ac77aefead77580147fe6cac443c133b" + integrity sha512-R8/rsp9fbqrxTg6FelmEuiNR39/VIrmNtbyG487Q9ObhPnxf+HZ/Sc0FvqcqLq+Mbcj5bjXSDu+wB3dNeZS1zQ== + dependencies: + "@types/node" "*" + +"@types/bip39@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/bip39/-/bip39-2.4.1.tgz#1a47b453b59a50d7b5856819b834c74798915eb3" + integrity sha512-QHx0qI6JaTIW/S3zxE/bXrwOWu6Boos+LZ4438xmFAHY5k+qHkExMdAnb/DENEt2RBnOdZ6c5J+SHrnLEhUohQ== + dependencies: + "@types/node" "*" + +"@types/bluebird@*", "@types/bluebird@^3.5.25": + version "3.5.25" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.25.tgz#59188b871208092e37767e4b3d80c3b3eaae43bd" + integrity sha512-yfhIBix+AIFTmYGtkC0Bi+XGjSkOINykqKvO/Wqdz/DuXlAKK7HmhLAXdPIGsV4xzKcL3ev/zYc4yLNo+OvGaw== + +"@types/body-parser@*", "@types/body-parser@^1.17.0": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" + integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/boom@*", "@types/boom@^7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@types/boom/-/boom-7.2.1.tgz#a21e21ba08cc49d17b26baef98e1a77ee4d6cdb0" + integrity sha512-kOiap+kSa4DPoookJXQGQyKy1rjZ55tgfKAh9F0m1NUdukkcwVzpSnXPMH42a5L+U++ugdQlh/xFJu/WAdr1aw== + +"@types/braces@*": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@types/braces/-/braces-2.3.0.tgz#d00ec0a76562b2acb6f29330be33a093e33ed25c" + integrity sha512-A3MV5EsLHgShHoJ/XES/fQAnwNISKLrFuH9eNBZY5OkTQB7JPIwbRoExvRpDsNABvkMojnKqKWS8x0m2rLYi+A== + +"@types/bson@*": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@types/bson/-/bson-1.0.11.tgz#c95ad69bb0b3f5c33b4bb6cc86d86cafb273335c" + integrity sha512-j+UcCWI+FsbI5/FQP/Kj2CXyplWAz39ktHFkXk84h7dNblKRSoNJs95PZFRd96NQGqsPEPgeclqnznWZr14ZDA== + dependencies: + "@types/node" "*" + +"@types/bytebuffer@^5.0.37": + version "5.0.37" + resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.37.tgz#795c0082cfb8c43a4cf3a8fc97d108ec76e59579" + integrity sha512-ktTBUX8hHsosJbt3Pxx5/pUpEFkLlimJLeddGFz7illfm/gTk43Ln7lAdr0rEPi8QIJjuFuV0bvVSa1sKcYszg== + dependencies: + "@types/long" "*" + "@types/node" "*" + +"@types/capture-console@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/capture-console/-/capture-console-1.0.0.tgz#8730d90248d862c4ccdaf5a623bfee0272d9a934" + integrity sha512-v6XDGk++qgUNmL5oqjmrK/tYIRmekq9FO8vgVv4xAx9frJYTwWUI+iOa+2z48zw+ZHfVVTNxL1qT9wuZZUrDVg== + dependencies: + "@types/node" "*" + +"@types/caseless@*": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.1.tgz#9794c69c8385d0192acc471a540d1f8e0d16218a" + integrity sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A== + +"@types/catbox@*": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/catbox/-/catbox-10.0.3.tgz#6cbf87ddd35108b8d3945b0838794de8488a1cae" + integrity sha512-f77b7+fhDLVD9d0dbprmCcAJ7wxipuQwN+8UNws33ZDlf+7G7NDgaBnrPUfXF9DGsfKQWdlzU3ZPLIrNEzqBqA== + +"@types/cli-progress@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-1.8.0.tgz#d7cc20191efa0374b9b919531a6bee90ee223e99" + integrity sha512-anbR1K9PtAzzB7Uc577Nq5KzgLhax1AuiJp7SC029wEZTmgpOT68uKOs+LMXpvLn0aVjwOiU6ijEwkcNWT0/JA== + dependencies: + "@types/node" "*" + +"@types/clipboardy@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/clipboardy/-/clipboardy-1.1.0.tgz#316fe1a3ed71b1a51becb925e7e0497986c6a85c" + integrity sha512-KOxf4ah9diZWmREM5jCupx2+pZaBPwKk5d5jeNK2+TY6IgEO35uhG55NnDT4cdXeRX8irDSHQPtdRrr0JOTQIw== + +"@types/commander@^2.12.2": + version "2.12.2" + resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae" + integrity sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q== + dependencies: + commander "*" + +"@types/connect@*": + version "3.4.32" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" + integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== + dependencies: + "@types/node" "*" + +"@types/continuation-local-storage@*": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#a33e0df9dce9b424d1c98fc4fdebd8578dceec7e" + integrity sha1-oz4N+dzptCTRyY/E/evYV43O7H4= + dependencies: + "@types/node" "*" + +"@types/create-hash@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/create-hash/-/create-hash-1.2.0.tgz#a30e091ce5904652dd6cee76d1b5e4ce6e883a68" + integrity sha512-tvo2dQ4TRKi0GYsblpWnhpJKR7Dvyyu+JdWhu4K5J8MKKONQfD9imAI/RIZn9brZXJ7n5DHxjwMpB4XOIVvGaw== + dependencies: + "@types/node" "*" + +"@types/deepmerge@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/deepmerge/-/deepmerge-2.2.0.tgz#6f63896c217f3164782f52d858d9f3a927139f64" + integrity sha512-FEQYDHh6+Q+QXKSrIY46m+/lAmAj/bk4KpLaam+hArmzaVpMBHLcfwOH2Q2UOkWM7XsdY9PmZpGyPAjh/JRGhQ== + dependencies: + deepmerge "*" + +"@types/elasticsearch@^5.0.30": + version "5.0.30" + resolved "https://registry.yarnpkg.com/@types/elasticsearch/-/elasticsearch-5.0.30.tgz#3c52f7119e3a20a47e2feb8e2b4cc54030a54e23" + integrity sha512-swxiNcLOtnHhJhAE5HcUL3WsKLHr8rEQ+fwpaJ0x4dfEE3oK2kGUoyz4wCcQfvulcMm2lShyxZ+2E4BQJzsAlg== + +"@types/env-paths@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/env-paths/-/env-paths-1.0.2.tgz#8b2b2b89d959591de2bf74ea448a7db2289299aa" + integrity sha512-EVOU+P4CSugzatd2GD8tVEYYR/OMcwzkneaJLx0R091gTqW2wZO1vpiWoM+BKZAEYdzif0SfwRuBvegIZBcZnw== + +"@types/events@*": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" + integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== + +"@types/express-serve-static-core@*": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz#fdfe777594ddc1fe8eb8eccce52e261b496e43e7" + integrity sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w== + dependencies: + "@types/events" "*" + "@types/node" "*" + "@types/range-parser" "*" + +"@types/express@^4.16.0": + version "4.16.0" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.0.tgz#6d8bc42ccaa6f35cf29a2b7c3333cb47b5a32a19" + integrity sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + +"@types/form-data@*": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" + integrity sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ== + dependencies: + "@types/node" "*" + +"@types/fs-extra@^5.0.3", "@types/fs-extra@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599" + integrity sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g== + dependencies: + "@types/node" "*" + +"@types/geojson@^1.0.0": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.6.tgz#3e02972728c69248c2af08d60a48cbb8680fffdf" + integrity sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w== + +"@types/glob@*": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/handlebars@^4.0.38", "@types/handlebars@^4.0.39": + version "4.0.39" + resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.0.39.tgz#961fb54db68030890942e6aeffe9f93a957807bd" + integrity sha512-vjaS7Q0dVqFp85QhyPSZqDKnTTCemcSHNHFvDdalO1s0Ifz5KuE64jQD5xoUkfdWwF4WpqdJEl7LsWH8rzhKJA== + +"@types/hapi@*", "@types/hapi@^17.8.2": + version "17.8.2" + resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-17.8.2.tgz#667fbeff250c338dca9e6cc1779f5696c7c148ed" + integrity sha512-UP+Z+NN5c55hu96j68kx04uIsUWxElc3H2JSoFm4I7ltmOlIqtn/tZIuSVeeRABKz3NWNG6X7wHyL2N+tgm6lA== + dependencies: + "@types/boom" "*" + "@types/catbox" "*" + "@types/iron" "*" + "@types/joi" "*" + "@types/mimos" "*" + "@types/node" "*" + "@types/podium" "*" + "@types/shot" "*" + +"@types/highlight.js@^9.12.3": + version "9.12.3" + resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca" + integrity sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ== + +"@types/hoek@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/hoek/-/hoek-4.1.3.tgz#d1982d48fb0d2a0e5d7e9d91838264d8e428d337" + integrity sha1-0ZgtSPsNKg5dfp2Rg4Jk2OQo0zc= + +"@types/inert@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/inert/-/inert-5.1.2.tgz#2bb8bef3b2462f904c960654c9edfa39285a85c6" + integrity sha512-3IoSFLQWvhLfZ85kHas/F3iD/TyZPfeJbTsDjrwYljK1MgBGCB2OywAsyeA/YiJ62VbNXfXBwpD1/VbJPIZSGA== + dependencies: + "@types/hapi" "*" + +"@types/integer@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/integer/-/integer-1.0.0.tgz#f5b313876012fad0afeb5318f03cb871064eb33e" + integrity sha512-3viiRKLoSP2Qr78nMoQjkDc0fan4BgmpOyV1+1gKjE8wWXo3QQ78WItO6f9WuBf3qe3ymDYhM65oqHTOZ0rFxw== + +"@types/ip@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/ip/-/ip-1.1.0.tgz#aec4f5bfd49e4a4c53b590d88c36eb078827a7c0" + integrity sha512-dwNe8gOoF70VdL6WJBwVHtQmAX4RMd62M+mAB9HQFjG1/qiCLM/meRy95Pd14FYBbEDwCq7jgJs89cHpLBu4HQ== + dependencies: + "@types/node" "*" + +"@types/iron@*": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/iron/-/iron-5.0.1.tgz#5420bbda8623c48ee51b9a78ebad05d7305b4b24" + integrity sha512-Ng5BkVGPt7Tw9k1OJ6qYwuD9+dmnWgActmsnnrdvs4075N8V2go1f6Pz8omG3q5rbHjXN6yzzZDYo3JOgAE/Ug== + dependencies: + "@types/node" "*" + +"@types/is-reachable@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/is-reachable/-/is-reachable-3.0.0.tgz#3203aa585cd5d3a4c1339bc857f4de06c4a18aff" + integrity sha512-rCl0PxFRPqwnKnWOO+6YdfnIIEsqahrEQjBfKR31C6E5oC4MY2U9W6VvXl090ZBYoCEY2wtqrBQaZn0UBGY85g== + +"@types/jest@^23.3.10": + version "23.3.10" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.10.tgz#4897974cc317bf99d4fe6af1efa15957fa9c94de" + integrity sha512-DC8xTuW/6TYgvEg3HEXS7cu9OijFqprVDXXiOcdOKZCU/5PJNLZU37VVvmZHdtMiGOa8wAA/We+JzbdxFzQTRQ== + +"@types/joi@*", "@types/joi@^14.0.0", "@types/joi@^14.0.1": + version "14.0.1" + resolved "https://registry.yarnpkg.com/@types/joi/-/joi-14.0.1.tgz#739be8a8899a75631a3c9f15611e54bbab06c024" + integrity sha512-0uZZ+nffpr480zwwUXsk0Z5O0szllffNW1EbkI+dDzKhNKhiX4QOwpwK37WpKIpaPLk9V8U9y2We/VOeD6zyhQ== + +"@types/js-yaml@^3.11.4": + version "3.11.4" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-3.11.4.tgz#c4323304673278b8406bccb63bbf577342dbbdba" + integrity sha512-a42nbZzOlxbH3ORDg/brU+zlRQS8mSvZCEY4Xery66/NIA7yX4T9qk6yL9qpw4AhgwzEgc1XIhHRnivQW+zlkw== + +"@types/keyv@*": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.0.tgz#1961f73b3bf1084c044f79a070b45a5bfa6578b9" + integrity sha512-OxT2IEeRdwvoUyp8n1v1hTIFzATb3NQYN8OHv/XbXRHiF2DXwKyzoI4UUaQgwZkRflLaSgyttat+RfWgsKIMIQ== + dependencies: + "@types/node" "*" + +"@types/keyv@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.0.1.tgz#360353aba0fcc8db2c857685d3b31b42d4602b5c" + integrity sha512-Nn9TebKwLyY17j7arUL1yKYS5Mx+I1h45bejs/C9g8LW1Km7CrJbMHmm96cSsxslOAk1CnQj614gF9ekMnH1Lg== + dependencies: + "@types/node" "*" + +"@types/keyv__sqlite@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/keyv__sqlite/-/keyv__sqlite-2.0.0.tgz#629e663b8cc8570fb15360103f7f2859f2d617e7" + integrity sha512-3IAuqZTnyXMQlwv2ObmQOe/ZhGETpBqXyqfm1H1k9J3/irmW2Og2YmDhStB6dv4lGvJrG079NLIAtSuYT0F8LA== + dependencies: + "@types/keyv" "*" + "@types/node" "*" + +"@types/lodash.camelcase@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@types/lodash.camelcase/-/lodash.camelcase-4.3.4.tgz#bdc60ff98f7727787d9ea593e398a3e9bf9f6180" + integrity sha512-yx+TmSP3QnhUGdcxkvwV3O++eI6kXKf5E89yJutHR8ebdr5f7KF5XmTBIWrbXFBLo2JIcaBz2axdpe7/WiOT2g== + dependencies: + "@types/lodash" "*" + +"@types/lodash.chunk@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@types/lodash.chunk/-/lodash.chunk-4.2.4.tgz#d0301e7ed435f7eb60c570e715fb05047256d536" + integrity sha512-8/M4C4g2xIKCZb6B66J3pQrcdGgAEb9O8gZrYULJ7dI3BDOFLm5bzrg+K4u5MogGqx3K19rJoy1BnJ0KNQvMBA== + dependencies: + "@types/lodash" "*" + +"@types/lodash.clonedeep@^4.5.4": + version "4.5.4" + resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.4.tgz#2515c5f08bc95afebfb597711871b0497f5d7da7" + integrity sha512-+rCVPIZOJaub++wU/lmyp/SxiKlqXQaXI5LryzjuHBKFj51ApVt38Xxk9psLWNGMuR/obEQNTH0l/yDfG4ANNQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.compact@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.compact/-/lodash.compact-3.0.4.tgz#f5eb5b34fa19521029febf27b8eca0a6689c2384" + integrity sha512-oHZH8dWjSJHIstoGL7ZKtZ87UP7Diz9SXKAFLh19JZaEkdxhld89vg+XykjMzdR0mfcZWF5mWOrco+SKG+uXtg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.fill@^3.4.4": + version "3.4.4" + resolved "https://registry.yarnpkg.com/@types/lodash.fill/-/lodash.fill-3.4.4.tgz#c54608d7da691142bf281134149b6ecf0d1f701b" + integrity sha512-D2c164uS5YG3OYmalDFW3yXlhq3DmFE8y1EdQ9MqQ9VPFBD9+73GMzZxRAdG4/G8O3ZNeERkRGXMJCgsWi7c6A== + dependencies: + "@types/lodash" "*" + +"@types/lodash.first@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.first/-/lodash.first-3.0.4.tgz#39c95a74f4a00a97fa3afb4d1655b6996eac79e5" + integrity sha512-ApNt+FXI8YNZr11ae/dfl1C121SflvsT2uc602E76ZfZDyQJg5Uiy7oS+t+m1Zf36VghWxsMzjYor11qDdq/cg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.flatten@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@types/lodash.flatten/-/lodash.flatten-4.4.4.tgz#7f28009ef57c8d2b1d8463c3e53fdccf780120a5" + integrity sha512-F106FV2hmztEtMHozFMfS41u+58vjMEv2SJljMlXmPCn13yWS+/B1r0KjQuaZpsPE857req0BunDwzgpqQ2Ydg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.get@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@types/lodash.get/-/lodash.get-4.4.4.tgz#34b67841594e4ddc8853341d65e971a38cb4e2f0" + integrity sha512-6igkhtKoWAEvTWCgd5uubiuxXLUY/kljQOQZV1G5Y7SrivpmCU+NWG5tGLgRBkccobrDljbJYzBM2vgCG4Oc8Q== + dependencies: + "@types/lodash" "*" + +"@types/lodash.groupby@^4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@types/lodash.groupby/-/lodash.groupby-4.6.4.tgz#4dbb730b0a8ad46915b7406d1e247cfd7a79b288" + integrity sha512-WvjZ0XtKdNUNGwY0udB3DiP3PC4mW6L3ESkJudA+LsgB2+LCUXUu3Pa+idzenLqBbMOcbBeAWPmEWSvyH0VWjw== + dependencies: + "@types/lodash" "*" + +"@types/lodash.head@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.head/-/lodash.head-4.0.4.tgz#1e788bad0435d31c06cbefe8781dc368be756d49" + integrity sha512-Tngbn0PblpxwEljuFWJ+CK1hp4GMDjtoJtQjpz0JLJ/7u4eNA3Wemu0e+mNPPqtlrFMoY8eFKEPQ5OX6/K5q6g== + dependencies: + "@types/lodash" "*" + +"@types/lodash.isempty@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@types/lodash.isempty/-/lodash.isempty-4.4.4.tgz#0748feefeb1d639017bb76f155d7e402ca3e3cdf" + integrity sha512-d0pn1toi559K94bpy1/Huv82xZWhEHpump1nrhToZ+CDZizEZ9hBx8J+pT4aUbnBtyeGdEMyWqknQOIIie83NA== + dependencies: + "@types/lodash" "*" + +"@types/lodash.isequal@^4.5.3": + version "4.5.3" + resolved "https://registry.yarnpkg.com/@types/lodash.isequal/-/lodash.isequal-4.5.3.tgz#b0d2747d3e76cc94da47ebd932c69a98c0ba61b4" + integrity sha512-tpTUmHksO2H9RF98Y2w7v06ZeEKAxHPo2ysL0bV5qv5UBweiZl33NFu5QYmYOSxSnHMqBt/vsVsBVeQAcJiokg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.isstring@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.isstring/-/lodash.isstring-4.0.4.tgz#d049735cd098c39227a9974068b61c874ac107bc" + integrity sha512-kdDz6h18L4H8Stbrm0S3uKADdtMJsq+7+AmqsMtZ5h0fi+6gSpphe8qQHJBeeGtz8EFzz+8pyIBLAxuvmCInww== + dependencies: + "@types/lodash" "*" + +"@types/lodash.last@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/lodash.last/-/lodash.last-3.0.4.tgz#7c3dcd6be9de2a5517614e8cca604d0ef2a6c73a" + integrity sha512-G/5M4Uek3v41lYgq8qLuvfWVbm8iwsHgFLw+jFVOyRF3PT5GqSwtQSMviONiNqT1CymdZV8HSh2tw8wfv0eVmA== + dependencies: + "@types/lodash" "*" + +"@types/lodash.orderby@^4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@types/lodash.orderby/-/lodash.orderby-4.6.4.tgz#adea1ffe3d5c33ae13b137ca1685f267a6b50f14" + integrity sha512-zOdkK4xTzEfwXH2ffwIMAJbZ2CeCKs6egg+xr/TWJttHAEccvEH/qX/mbRnTHWTqBZsXPHUpOHfXo2l2lMDKUQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.partition@^4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@types/lodash.partition/-/lodash.partition-4.6.4.tgz#55858a53778724cfc8a0feff05fa0b28e0757cd3" + integrity sha512-znM//f4B0PcPk13GZJB66kzZnw5MewcX3DIM4e1cQ+TmJbFUV/GUbDD/e8m08nbdUDiZlCOLm/1oOVe6s2SthQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.pick@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@types/lodash.pick/-/lodash.pick-4.4.4.tgz#381ac6c0a92f50405e2a6f9caeff07b0e40a9f75" + integrity sha512-54nf0ndJlN3ULz9fLceKhYJZfwf50jAqqPJyI3/UbU/qxyuMSgNC3niWhFqmVAnGPoxMwAwNQCYTXG0Gv2lfPg== + dependencies: + "@types/lodash" "*" + +"@types/lodash.sample@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@types/lodash.sample/-/lodash.sample-4.2.4.tgz#d979476e1b5b05781adad5185027cd286bf4e963" + integrity sha512-6TFpHgqLDu8Dmpn6xfzzjKC/25leqA1sV3CJt7LbXrLCkbnaSGaax3WKacvllaS3FOdBocVLgXU/nVqdKERl9A== + dependencies: + "@types/lodash" "*" + +"@types/lodash.set@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@types/lodash.set/-/lodash.set-4.3.4.tgz#ef11a971c7d3858e74fa6f745b4b69b2256f6c07" + integrity sha512-oY+y8V6Bg69q4U4eDhR7K177gE76I2Zb40OMHb+epTwo6RMGXeJpY7sKN7xrzvr1aXxPsfS50pvKVlcRq34JPQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.shuffle@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@types/lodash.shuffle/-/lodash.shuffle-4.2.4.tgz#3931aeafe65770c132e3a4061c833eaf5936c2b2" + integrity sha512-GnqZmVNNRDbDTzaFOf5TaumjlN+Nq83+kTSnU1EsTo6NtKlifBKU0oNM2wsL3BAxMWk4E6WVtzoBu+2Vg7RIjw== + dependencies: + "@types/lodash" "*" + +"@types/lodash.snakecase@^4.1.4": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/lodash.snakecase/-/lodash.snakecase-4.1.4.tgz#58729021e111db6a4ff814d3ff16ac13ef7d0132" + integrity sha512-cC7ebPwSRw3hvBBfB6AV2Aja/XsIxL1HkwKjgDoQPZWjQlNtkkpFCGF7wxGaHMYsEaoUrnX1RE0FZW5Zzacr9Q== + dependencies: + "@types/lodash" "*" + +"@types/lodash.sortby@^4.7.4": + version "4.7.4" + resolved "https://registry.yarnpkg.com/@types/lodash.sortby/-/lodash.sortby-4.7.4.tgz#14f9d45b6214b32cbe2f6332990b386d4b2dd09a" + integrity sha512-Byy/JXUl7VCKOjqk2XyOEa4kRp2UBuPPkdQpIwSi+54t3KDa1vkIRU+qFEoWZMLcMUbBq8+Iy8Ybri8AqFYLTA== + dependencies: + "@types/lodash" "*" + +"@types/lodash.sumby@^4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@types/lodash.sumby/-/lodash.sumby-4.6.4.tgz#169974c2e54a24ce3f27ee785f1969abe6d8e385" + integrity sha512-CY7N49UIPO7CdArz5Kj3IyQKpZbXcnP4tVqQgL6+qDsd9jmcukqEmyD4weyxBUxXH3EvEmIYoBQjA8loAi266Q== + dependencies: + "@types/lodash" "*" + +"@types/lodash.take@^4.1.4": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/lodash.take/-/lodash.take-4.1.4.tgz#07e5670ac15501fcfccb4d0a4dd81ef87b239bb3" + integrity sha512-kGCIqpisGQs8x0dB5Usd6+7lL1pGgUThD9HlpJXS+xdTAE075HpqHBdl9YAezBKEWyx7F9qFR076eclld9QRhQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash.uniq@^4.5.4": + version "4.5.4" + resolved "https://registry.yarnpkg.com/@types/lodash.uniq/-/lodash.uniq-4.5.4.tgz#8dd571e4a68adddcd1bac810538e68f440e87403" + integrity sha512-q0FI7RCY99bUPBR7sJyfefWDa/KSD21pMWM1hi+2O+rJTzY2N4eRs+A6BwLotPNy/JOySfcZJYamZ8Owcs3SkQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*", "@types/lodash@^4.14.110": + version "4.14.119" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.119.tgz#be847e5f4bc3e35e46d041c394ead8b603ad8b39" + integrity sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw== + +"@types/long@*", "@types/long@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" + integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== + +"@types/marked@^0.4.0": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.4.2.tgz#64a89e53ea37f61cc0f3ee1732c555c2dbf6452f" + integrity sha512-cDB930/7MbzaGF6U3IwSQp6XBru8xWajF5PV2YZZeV8DyiliTuld11afVztGI9+yJZ29il5E+NpGA6ooV/Cjkg== + +"@types/micromatch@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-3.1.0.tgz#514c8a3d24b2680a9b838eeb80e6d7d724545433" + integrity sha512-06uA9V7v68RTOzA3ky1Oi0HmCPa+YJ050vM+sTECwkxnHUQnO17TAcNCGX400QT6bldUiPb7ux5oKy0j8ccEDw== + dependencies: + "@types/braces" "*" + +"@types/mime-db@*": + version "1.27.0" + resolved "https://registry.yarnpkg.com/@types/mime-db/-/mime-db-1.27.0.tgz#9bc014a1fd1fdf47649c1a54c6dd7966b8284792" + integrity sha1-m8AUof0f30dknBpUxt15ZrgoR5I= + +"@types/mime@*": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" + integrity sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA== + +"@types/mimos@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mimos/-/mimos-3.0.1.tgz#59d96abe1c9e487e7463fe41e8d86d76b57a441a" + integrity sha512-MATIRH4VMIJki8lcYUZdNQEHuAG7iQ1FWwoLgxV+4fUOly2xZYdhHtGgvQyWiTeJqq2tZbE0nOOgZD6pR0FpNQ== + dependencies: + "@types/mime-db" "*" + +"@types/minimatch@*", "@types/minimatch@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + +"@types/mongodb@*": + version "3.1.17" + resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.1.17.tgz#11351b147b68e7674cff9055ea82072521bc6fe3" + integrity sha512-u6tSIpfdsgK74aE0TuyqZYhHscw+gHs6dQNSsFUTFXubhhxCqovmV3nJRS0YKSw0sfqbzUgGzbG5+yorUPRnFg== + dependencies: + "@types/bson" "*" + "@types/node" "*" + +"@types/msgpack-lite@^0.1.6": + version "0.1.6" + resolved "https://registry.yarnpkg.com/@types/msgpack-lite/-/msgpack-lite-0.1.6.tgz#27e2a7eea4514f084ed4f9b53f8f63e6db4d6d50" + integrity sha512-Y9GFyM8qt5p7p+HtM9rsSAnle4ZmcpNAhbaFQyj1XzaHgEVCYgRNJiCeGxf5Jf81/a/G1o+LuGDE3mvLosU2MA== + dependencies: + "@types/node" "*" + +"@types/node-emoji@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@types/node-emoji/-/node-emoji-1.8.0.tgz#6bc88a880cdcb3c165e2828d056db242e8829a8b" + integrity sha512-+LiY4f3HMAgQGjte8Lg4K6xpTR+glwZolVrreU+ShACr6H/IzYN1VQAitHVeQrNKgZeuOegxE3IACh7Jo2qk0w== + +"@types/node-forge@^0.7.11": + version "0.7.11" + resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.7.11.tgz#6b25860d54a0e2e37a53b97e2554f99fbcc7e8bb" + integrity sha512-ZxPN7QZxwU1/hLY/gclSI66T5ziL/meVaQPs7V4070e9ptuTVWsLLPv6fAH268JR6Dqm35tu27MeJW5aJpiWIA== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "10.12.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.12.tgz#e15a9d034d9210f00320ef718a50c4a799417c47" + integrity sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A== + +"@types/node@^10.1.0": + version "10.12.15" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.15.tgz#20e85651b62fd86656e57c9c9bc771ab1570bc59" + integrity sha512-9kROxduaN98QghwwHmxXO2Xz3MaWf+I1sLVAA6KJDF5xix+IyXVhds0MAfdNwtcpSrzhaTsNB0/jnL86fgUhqA== + +"@types/node@^10.12.17": + version "10.12.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.17.tgz#7040565b2c93d59325a68fa69073e754a7eda93a" + integrity sha512-umSCRkjWH70uNzFiOof5yxCqrMXIBJ9UJJUzbEsmtWt8apURQh06pylGMqnhdjHGJSeoBrhzk+mibu6NgL1oBA== + +"@types/otplib@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@types/otplib/-/otplib-7.0.0.tgz#bc608c8771cba0f4417478eef79ef3f79c47e9f6" + integrity sha512-OZFn1eVNRGpaCfVZhTCIeSlHfxXM1oe1qtu9w07hWfH4nHiDo+tI6b6pIrOCNKQN9gYOP2M4Q43YvkT1R50deA== + +"@types/pg-query-stream@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/pg-query-stream/-/pg-query-stream-1.0.2.tgz#88ba9d7634d12e2a2a9ab31829970b3bdcc9d9ca" + integrity sha512-aZKYcd3p5xRQ97tD5AkeWD+ZIY9HobFUsPeuGNSdZZnVAi20Bh0nTyUZkEGh3vPJA1TNmCAG94Ou9vv8i0blww== + dependencies: + "@types/node" "*" + "@types/pg" "*" + +"@types/pg-types@*": + version "1.11.4" + resolved "https://registry.yarnpkg.com/@types/pg-types/-/pg-types-1.11.4.tgz#8d7c59fb509ce3dca3f8bae589252051c639a9a8" + integrity sha512-WdIiQmE347LGc1Vq3Ki8sk3iyCuLgnccqVzgxek6gEHp2H0p3MQ3jniIHt+bRODXKju4kNQ+mp53lmP5+/9moQ== dependencies: - npmlog "^4.1.2" - write-file-atomic "^2.3.0" + moment ">=2.14.0" -"@mrmlnc/readdir-enhanced@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" - integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== +"@types/pg@*": + version "7.4.11" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-7.4.11.tgz#dcc560e89c17d859c83c91ad3faf60bfeacaad6c" + integrity sha512-Eksj2yOBNHnNqLuU1AqwF1qXgdaYDcWVH1ZQlxS7k1i34+JZd/ZNd15ugIpTVQxmBBMqjliJstmrnusjXy08Tg== dependencies: - call-me-maybe "^1.0.1" - glob-to-regexp "^0.3.0" + "@types/node" "*" + "@types/pg-types" "*" -"@nodelib/fs.stat@^1.1.2": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" - integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== +"@types/pino@^5.8.3": + version "5.8.3" + resolved "https://registry.yarnpkg.com/@types/pino/-/pino-5.8.3.tgz#cd355c97a92d57927fe67ee5c7d1fa9349280805" + integrity sha512-dpHLhkuV1QNHC6SOnjSn75MeA0L9CGIWWp+axhm2JwgrYiViomUJF5tELWvGopKPrGXhggHAQv1Q5vkBapy6/w== + dependencies: + "@types/node" "*" + "@types/sonic-boom" "*" -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= +"@types/pluralize@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/pluralize/-/pluralize-0.0.29.tgz#6ffa33ed1fc8813c469b859681d09707eb40d03c" + integrity sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA== -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== +"@types/podium@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/podium/-/podium-1.0.0.tgz#bfaa2151be2b1d6109cc69f7faa9dac2cba3bb20" + integrity sha1-v6ohUb4rHWEJzGn3+qnawsujuyA= -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== +"@types/prettier@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.15.2.tgz#91594ea7cb6f3b1f7ea69f32621246654c7cc231" + integrity sha512-XIB0ZCaFZmWUHAa9dBqP5UKXXHwuukmVlP+XcyU94dui2k+l2lG+CHAbt2ffenHPUqoIs5Beh8Pdf2YEq/CZ7A== -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= +"@types/pretty-ms@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/pretty-ms/-/pretty-ms-4.0.0.tgz#5e5177b9e447fbc8446e6fd2b09aea045971f11d" + integrity sha512-l5aIszYIm3s4jIL4wLDwajEml1w3E4zqGDDUgvnroX1ux6YAxsCrgRHLUafgrHFGTHlx7TpwiwSwcHV8IBvccw== -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= +"@types/random-seed@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@types/random-seed/-/random-seed-0.3.3.tgz#7741f7b0a4513198a9396ce4ad25832f799a6727" + integrity sha512-kHsCbIRHNXJo6EN5W8EA5b4i1hdT6jaZke5crBPLUcLqaLdZ0QBq8QVMbafHzhjFF83Cl9qlee2dChD18d/kPg== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/request-promise@^4.1.42": + version "4.1.42" + resolved "https://registry.yarnpkg.com/@types/request-promise/-/request-promise-4.1.42.tgz#a70a6777429531e60ed09faa077ead9b995204cd" + integrity sha512-b8li55sEZ00BXZstZ3d8WOi48dnapTqB1VufEG9Qox0nVI2JVnTVT1Mw4JbBa1j+1sGVX/qJ0R4WDv4v2GjT0w== dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" + "@types/bluebird" "*" + "@types/request" "*" -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= +"@types/request@*": + version "2.48.1" + resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.1.tgz#e402d691aa6670fbbff1957b15f1270230ab42fa" + integrity sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg== + dependencies: + "@types/caseless" "*" + "@types/form-data" "*" + "@types/node" "*" + "@types/tough-cookie" "*" -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= +"@types/rimraf@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@types/rimraf/-/rimraf-2.0.2.tgz#7f0fc3cf0ff0ad2a99bb723ae1764f30acaf8b6e" + integrity sha512-Hm/bnWq0TCy7jmjeN5bKYij9vw5GrDFWME4IuxV08278NtU/VdGbzsBohcCUJ7+QMqmUq5hpRKB39HeQWJjztQ== + dependencies: + "@types/glob" "*" + "@types/node" "*" -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= +"@types/secp256k1@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-3.5.0.tgz#0f3baf16b07488c6da2633a63b4160bcf8d0fd5b" + integrity sha512-ZE39QhkIaNK6xbKIp1VLN5O36r97LuslLmRnjAcT0sVDxcfvrk3zqp/VnIfmGza7J6jDxR8dIai3hsCxPYglPA== + dependencies: + "@types/node" "*" -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= +"@types/semver@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" + integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= +"@types/sequelize@*", "@types/sequelize@^4.27.33": + version "4.27.33" + resolved "https://registry.yarnpkg.com/@types/sequelize/-/sequelize-4.27.33.tgz#1e906565f371c7efbda0bf2bfa322bea4b5bb1a2" + integrity sha512-4w36T30hQKtsVj3BWktRauAls+amAkJsjn6EdQBQ5Pq5iiMm2qRgdFsQj/OHYM1LCYtH7eR4/g4ffbmFux1q2A== + dependencies: + "@types/bluebird" "*" + "@types/continuation-local-storage" "*" + "@types/lodash" "*" + "@types/validator" "*" -"@samverschueren/stream-to-observable@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" - integrity sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg== +"@types/serve-static@*": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" + integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q== dependencies: - any-observable "^0.3.0" + "@types/express-serve-static-core" "*" + "@types/mime" "*" -"@sentry/core@4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-4.4.0.tgz#32caf17751cbd50345214d7bb61d498d51bb13c5" - integrity sha512-t/qKC6LhAaAp3/bgfNaZ2Ed5de6FpJT0xEvB9dpILYLy083tg4evmBZA/DspuGn0XZek9GKDhcxbO7BA5/QBpA== +"@types/shelljs@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.1.tgz#133e874b5fb816a2e1c8647839c82d76760b1191" + integrity sha512-1lQw+48BuVgp6c1+z8EMipp18IdnV2dLh6KQGwOm+kJy9nPjEkaqRKmwbDNEYf//EKBvKcwOC6V2cDrNxVoQeQ== dependencies: - "@sentry/hub" "4.4.0" - "@sentry/minimal" "4.4.0" - "@sentry/types" "4.4.0" - "@sentry/utils" "4.4.0" - tslib "^1.9.3" + "@types/glob" "*" + "@types/node" "*" -"@sentry/hub@4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-4.4.0.tgz#9aa47ce9143cbd44de6ac1aeddd2a6532517673c" - integrity sha512-nsKNKD9hcj5b95KBqVH7sCPXezI7kngyP36Cst4GXUKU9StQ6F4ZUG2fUflvREUo+Rj1lz3y2UB8+WrcnLejag== +"@types/shot@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/shot/-/shot-4.0.0.tgz#7545500c489b65c69b5bc5446ba4fef3bd26af92" + integrity sha512-Xv+n8yfccuicMlwBY58K5PVVNtXRm7uDzcwwmCarBxMP+XxGfnh1BI06YiVAsPbTAzcnYsrzpoS5QHeyV7LS8A== dependencies: - "@sentry/types" "4.4.0" - "@sentry/utils" "4.4.0" - tslib "^1.9.3" + "@types/node" "*" -"@sentry/minimal@4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-4.4.0.tgz#e2c19296312311851704bb5d61f6892253ae52e6" - integrity sha512-BBI/dL+imR8mAh4gHxnsfAgCjvOGtLWalIOV29QsVXM6OgXX+se/14zAbSlAk8a/u9bFQq+/235P/OCMVb9mqA== +"@types/sonic-boom@*": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@types/sonic-boom/-/sonic-boom-0.6.1.tgz#530d17e0b971c8f41cdfd78206171155aee58795" + integrity sha512-I0LVjE/VPehYvvMgmLZ8kSutqCaGzwDbyf74C5zoNwsb64KCppCJ7GkrLC4Sic3SzfEsGhcAVFpxR7UEpDi+bg== dependencies: - "@sentry/hub" "4.4.0" - "@sentry/types" "4.4.0" - tslib "^1.9.3" + "@types/node" "*" -"@sentry/node@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-4.4.0.tgz#56962f718811ce6f55e04189d3ebb4acdc50d79d" - integrity sha512-EvHfDURVaiFFa2vDS/3FjgytOaSaJlJqbTYaHxoJYQBs5WJmlw6tqnMNAzpGu2I2iV5hjo13SspU3mN4SwNY5Q== +"@types/sqlite3@^3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/sqlite3/-/sqlite3-3.1.3.tgz#580d547203b8ad6e11aa6a6769c8ca5d7e197d13" + integrity sha512-BgGToABnI/8/HnZtZz2Qac6DieU2Dm/j3rtbMmUlDVo4T/uLu8cuVfU/n2UkHowiiwXb6/7h/CmSqBIVKgcTMA== dependencies: - "@sentry/core" "4.4.0" - "@sentry/hub" "4.4.0" - "@sentry/types" "4.4.0" - "@sentry/utils" "4.4.0" - "@types/stack-trace" "0.0.29" - cookie "0.3.1" - https-proxy-agent "^2.2.1" - lsmod "1.0.0" - stack-trace "0.0.10" - tslib "^1.9.3" + "@types/events" "*" + "@types/node" "*" -"@sentry/types@4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-4.4.0.tgz#99e662c1b9955f293e2673433a1c84b50db1ed64" - integrity sha512-db0Vw8EofVszUOTj86cE4YeM0ZGQIp/YYi/isu3SV28+tLUJlDPlU11xhXCSsoyvqk0L0Ld6QFOfIKJeMCtl5Q== +"@types/stack-trace@0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/stack-trace/-/stack-trace-0.0.29.tgz#eb7a7c60098edb35630ed900742a5ecb20cfcb4d" + integrity sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g== -"@sentry/utils@4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-4.4.0.tgz#1e81f8849763477bb32707d6eba60897c9de9044" - integrity sha512-n3uqSiAhKjMS24NS2RLOl3OFsODr82D6aSHwIByXH83O+tHdLeyin3yEuosfiKkvvurELPI1tSh9QPCgW35dJw== +"@types/tapable@*": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" + integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ== + +"@types/tough-cookie@*": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.4.tgz#821878b81bfab971b93a265a561d54ea61f9059f" + integrity sha512-Set5ZdrAaKI/qHdFlVMgm/GsAv/wkXhSTuZFkJ+JI7HK+wIkIlOaUXSXieIvJ0+OvGIqtREFoE+NHJtEq0gtEw== + +"@types/uglify-js@*": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.0.4.tgz#96beae23df6f561862a830b4288a49e86baac082" + integrity sha512-SudIN9TRJ+v8g5pTG8RRCqfqTMNqgWCKKd3vtynhGzkIIjxaicNAMuY5TRadJ6tzDu3Dotf3ngaMILtmOdmWEQ== dependencies: - "@sentry/types" "4.4.0" - tslib "^1.9.3" + source-map "^0.6.1" -"@sindresorhus/is@^0.12.0": - version "0.12.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.12.0.tgz#55c37409c809e802efea25911a579731adfc6e07" - integrity sha512-9ve22cGrAKlSRvi8Vb2JIjzcaaQg79531yQHnF+hi/kOpsSj3Om8AyR1wcHrgl0u7U3vYQ7gmF5erZzOp4+51Q== +"@types/umzug@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@types/umzug/-/umzug-2.2.0.tgz#0cfc694dd70cf20c0386bddc76d53fe225ae1c67" + integrity sha512-p9yaOdoKRYT8MuLOGKigzOFKaIrd6v6OwcfUEEGNiLYWldf1dLgoZ74e0vuo9/tpIuww9LuoqfJFEEslj4Z7Ng== dependencies: - symbol-observable "^1.2.0" + "@types/events" "*" + "@types/mongodb" "*" + "@types/sequelize" "*" -"@snyk/dep-graph@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@snyk/dep-graph/-/dep-graph-1.1.2.tgz#a0377fbb29dd42bc12d1c2493b51a7b7fe0d334a" - integrity sha512-mCoAFKtmezBL61JOzLMzqqd/sXXxp0iektEwf4zw+sM3zuG4Tnmhf8OqNO6Wscn84bMIfLlI/nvECdxvSS7MTw== +"@types/uuid@^3.4.4": + version "3.4.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.4.tgz#7af69360fa65ef0decb41fd150bf4ca5c0cefdf5" + integrity sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw== dependencies: - graphlib "^2.1.5" - lodash "^4" - source-map-support "^0.5.9" - tslib "^1.9.3" + "@types/node" "*" -"@snyk/gemfile@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@snyk/gemfile/-/gemfile-1.1.0.tgz#8c254dfc7739b2e8513c44c976fc41872d5f6af0" - integrity sha512-mLwF+ccuvRZMS0SxUAxA3dAp8mB3m2FxIsBIUWFTYvzxl+E4XTZb8uFrUqXHbcxhZH1Z8taHohNTbzXZn3M8ag== +"@types/validator@*": + version "9.4.4" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-9.4.4.tgz#67c745e988f721ea2a1e4cc5b4cd76e6bb3a76b1" + integrity sha512-7bWNKQ3lDMhRS2lxe1aHGTBijZ/a6wQfZmCtKJDefpb81sYd+FrfNqj6Gda1Tcw8bYK0gG1CVuNLWV2JS7K8Dw== -"@szmarczak/http-timer@^1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.1.tgz#6402258dfe467532b26649ef076b4d11f74fb612" - integrity sha512-WljfOGkmSJe8SUkl+4TPvN2ec0dpUGVyfTBQLoXJUiILs+wBSc4Kvp2N3aAWE4VwwDSLGdmD3/bufS5BgZpVSQ== +"@types/vision@^5.3.5": + version "5.3.5" + resolved "https://registry.yarnpkg.com/@types/vision/-/vision-5.3.5.tgz#10cd8a155f58d6ceb65c8b889b5aca8ca80ea57b" + integrity sha512-CbGBNPBUSjyLW1bsC42mgvDffGtkHXzKIB69hGgCGwkoE90YS5DH1N8E4v+LXf/0LDepj/oc0IT3IDdnTQOOGQ== dependencies: - defer-to-connect "^1.0.1" - -"@types/events@*": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" - integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== + "@types/hapi" "*" -"@types/geojson@^1.0.0": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-1.0.6.tgz#3e02972728c69248c2af08d60a48cbb8680fffdf" - integrity sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w== +"@types/webpack-merge@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/webpack-merge/-/webpack-merge-4.1.3.tgz#e6af0f2a0f20a86ac83d7da84a2e454063121ad4" + integrity sha512-VdmNuYIvIouYlCI73NLKOE1pOVAxv5m5eupvTemojZz9dqghoQXmeEveI6CqeuWpCH6x6FLp6+tXM2sls20/MA== + dependencies: + "@types/webpack" "*" -"@types/long@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" - integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== +"@types/webpack-node-externals@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@types/webpack-node-externals/-/webpack-node-externals-1.6.3.tgz#a70ecab38339ab219d0090680c98c4973a90af01" + integrity sha512-sIYCMSV4Z1FbxqSKKvhmEnjd1UoZnTBT0VIhoZ0iT8gck7FB1MzJ1eH+yFyOgY65WU4LJBTxj3ySMKKfzfEiCw== + dependencies: + "@types/webpack" "*" -"@types/node@*", "@types/node@^10.1.0": - version "10.12.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.10.tgz#4fa76e6598b7de3f0cb6ec3abacc4f59e5b3a2ce" - integrity sha512-8xZEYckCbUVgK8Eg7lf5Iy4COKJ5uXlnIOnePN0WUwSQggy9tolM+tDJf7wMOnT/JT/W9xDYIaYggt3mRV2O5w== +"@types/webpack@*", "@types/webpack@^4.4.23": + version "4.4.23" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.4.23.tgz#059d6f4598cfd65ddee0e2db38317ef989696712" + integrity sha512-WswyG+2mRg0ul/ytPpCSWo+kOlVVPW/fKCBEVwqmPVC/2ffWEwhsCEQgnFbWDf8EWId2qGcpL623EjLfNTRk9A== + dependencies: + "@types/anymatch" "*" + "@types/node" "*" + "@types/tapable" "*" + "@types/uglify-js" "*" + source-map "^0.6.0" -"@types/stack-trace@0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/stack-trace/-/stack-trace-0.0.29.tgz#eb7a7c60098edb35630ed900742a5ecb20cfcb4d" - integrity sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g== +"@types/wif@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/wif/-/wif-2.0.1.tgz#bcab48b201403cb759cd7659aff4610cfd4888f4" + integrity sha512-FQKvE4EncC8C4qxW9y0psAOs2XVkxSAGIofB8tqNDPSeOsR4OueEf9TED3PHf8xlOquI+m++AXTWjJIS07FNHw== + dependencies: + "@types/node" "*" "@types/ws@^6.0.0": version "6.0.1" @@ -1617,11 +2539,6 @@ acorn-globals@^4.1.0: acorn "^6.0.1" acorn-walk "^6.0.1" -acorn-jsx@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" - integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== - acorn-walk@^6.0.1: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" @@ -1632,7 +2549,7 @@ acorn@^5.0.0, acorn@^5.5.3, acorn@^5.6.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1, acorn@^6.0.2: +acorn@^6.0.1: version "6.0.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.4.tgz#77377e7353b72ec5104550aa2d2097a2fd40b754" integrity sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg== @@ -1660,19 +2577,29 @@ aggregate-error@^1.0.0: indent-string "^3.0.0" ajv-errors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59" - integrity sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk= + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== ajv-keywords@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= -ajv@^6.1.0, ajv@^6.5.3, ajv@^6.5.5: - version "6.5.5" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.5.tgz#cf97cdade71c6399a92c6d6c4177381291b781a1" - integrity sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg== +ajv@^6.1.0, ajv@^6.5.5: + version "6.6.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.1.tgz#6360f5ed0d80f232cc2b294c362d5dc2e538dd61" + integrity sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.6.2.tgz#caceccf474bf3fc3ce3b147443711a24063cc30d" + integrity sha512-FBHEW6Jf5TB9MGBgUUA9XHkTbjXYfAUjY43ACMfmdMRHniyoMHjHjzD50OK8LGDWQwp4rWEsIq5kEqq7rvIM1g== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -1716,6 +2643,11 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +ansi-regex@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" + integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -1751,13 +2683,13 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -apollo-cache-control@0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.3.3.tgz#ad71d8f786e06f0275b2432004c15c2d37c48484" - integrity sha512-X6JhKfIaMLfl2jpsK/880BflXA+2lmm2sAsOZL4Bn2VrMsDtOssI1Ij9vNRbch9k9cA4WJvKed7Sql/wUIa1Eg== +apollo-cache-control@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.4.0.tgz#fec343e6ec95aa4f1b88e07e62f067bee0c48397" + integrity sha512-WuriaNQIugTE8gYwfBWWCbbQTSKul/cV4JMi5UgqNIUvjHvnKZQLKbt5uYWow6QQNMkLT9hey8QPYkWpogkeSA== dependencies: apollo-server-env "2.2.0" - graphql-extensions "0.3.3" + graphql-extensions "0.4.0" apollo-datasource@0.2.1: version "0.2.1" @@ -1767,22 +2699,22 @@ apollo-datasource@0.2.1: apollo-server-caching "0.2.1" apollo-server-env "2.2.0" -apollo-engine-reporting-protobuf@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.1.0.tgz#fbc220cac2a3b7800ffc155d7e54c21c56b7848e" - integrity sha512-GReJtAYTmpwg0drb9VgFtqObYYTCHkJhlHEYCeXY8bJV4fOgXsAZ7CIXR9nPKO0mBaoHIHaGYvXGcyCLrZ36VA== +apollo-engine-reporting-protobuf@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.2.0.tgz#2aaf4d2eddefe7924d469cf1135267bc0deadf73" + integrity sha512-qI+GJKN78UMJ9Aq/ORdiM2qymZ5yswem+/VDdVFocq+/e1QqxjnpKjQWISkswci5+WtpJl9SpHBNxG98uHDKkA== dependencies: protobufjs "^6.8.6" -apollo-engine-reporting@0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-0.1.3.tgz#85ad6ffd71db8f877202ce8b3d7dbfa7cabfbcf9" - integrity sha512-VkjiifHMHIAxydXecT+ck0WtqpFIsMlylKnKeuNAXfIfAXHX/JYtLhbArTTyhDunLrphMiUewfFv9P0K+aX2jw== +apollo-engine-reporting@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-0.2.0.tgz#e71816b1f46e782f8538c5a118148d4c0e628e25" + integrity sha512-Q6FfVb10v/nrv8FaFsPjIYlWh62jaYav3LuMgM9PsHWGK/zRQFXOEwLxcY2UCvG7O1moxF3XGmfBhMgo54py+Q== dependencies: - apollo-engine-reporting-protobuf "0.1.0" + apollo-engine-reporting-protobuf "0.2.0" apollo-server-env "2.2.0" async-retry "^1.2.1" - graphql-extensions "0.3.3" + graphql-extensions "0.4.0" lodash "^4.17.10" apollo-env@0.2.5: @@ -1794,12 +2726,12 @@ apollo-env@0.2.5: node-fetch "^2.2.0" apollo-link@^1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.4.tgz#ab4d21d2e428db848e88b5e8f4adc717b19c954b" - integrity sha512-B1z+9H2nTyWEhMXRFSnoZ1vSuAYP+V/EdUJvRx9uZ8yuIBZMm6reyVtr1n0BWlKeSFyPieKJy2RLzmITAAQAMQ== + version "1.2.6" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.6.tgz#d9b5676d79c01eb4e424b95c7171697f6ad2b8da" + integrity sha512-sUNlA20nqIF3gG3F8eyMD+mO80fmf3dPZX+GUOs3MI9oZR8ug09H3F0UsWJMcpEg6h55Yy5wZ+BMmAjrbenF/Q== dependencies: apollo-utilities "^1.0.0" - zen-observable-ts "^0.8.11" + zen-observable-ts "^0.8.13" apollo-server-caching@0.2.1: version "0.2.1" @@ -1808,27 +2740,27 @@ apollo-server-caching@0.2.1: dependencies: lru-cache "^5.0.0" -apollo-server-core@2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.2.4.tgz#2dc0752067c10588a4d3b8445cdfd00591a7175b" - integrity sha512-HHienzcp4KbVatUahH22xepk58szaXU3B14dPlJdvE58V6fApdWneEg+2iCXPOiTs5dR5uxrayuqwQdzjZhK2g== +apollo-server-core@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.3.1.tgz#cbdc0020a0dfecf2220cf5062dbb304fdf56edf2" + integrity sha512-8jMWYOQIZi9mDJlHe2rXg8Cp4xKYogeRu23jkcNy+k5UjZL+eO+kHXbNFiTaP4HLYYEpe2XE3asxp6q5YUEQeQ== dependencies: "@apollographql/apollo-tools" "^0.2.6" - "@apollographql/apollo-upload-server" "^5.0.3" "@apollographql/graphql-playground-html" "^1.6.6" "@types/ws" "^6.0.0" - apollo-cache-control "0.3.3" + apollo-cache-control "0.4.0" apollo-datasource "0.2.1" - apollo-engine-reporting "0.1.3" + apollo-engine-reporting "0.2.0" apollo-server-caching "0.2.1" apollo-server-env "2.2.0" apollo-server-errors "2.2.0" - apollo-server-plugin-base "0.1.4" - apollo-tracing "0.3.3" - graphql-extensions "0.3.4" + apollo-server-plugin-base "0.2.1" + apollo-tracing "0.4.0" + graphql-extensions "0.4.1" graphql-subscriptions "^1.0.0" graphql-tag "^2.9.2" graphql-tools "^4.0.0" + graphql-upload "^8.0.2" json-stable-stringify "^1.0.1" lodash "^4.17.10" subscriptions-transport-ws "^0.9.11" @@ -1847,31 +2779,30 @@ apollo-server-errors@2.2.0: resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.2.0.tgz#5b452a1d6ff76440eb0f127511dc58031a8f3cb5" integrity sha512-gV9EZG2tovFtT1cLuCTavnJu2DaKxnXPRNGSTo+SDI6IAk6cdzyW0Gje5N2+3LybI0Wq5KAbW6VLei31S4MWmg== -apollo-server-hapi@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/apollo-server-hapi/-/apollo-server-hapi-2.2.4.tgz#57c09c2d4e024ba56ce5a439ebe6e25d1b8d8ec8" - integrity sha512-Z8q/4CD5tWko/DnFd0wal68M6XaLoBxRKxQHxmZ67s73HKf01Wc5IYiVfdIsBGVzAgHpEXDBlN+1fgwgJxgi4Q== +apollo-server-hapi@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/apollo-server-hapi/-/apollo-server-hapi-2.3.1.tgz#f0614884a06c5fecd436b16e7472ae31063cf399" + integrity sha512-FGkKrm27L2l4DyNX2FLaedsZeK2u2WE8IS66FTI00HdOXeTeq0W4z2pq1H2+kDMQmcxuALHVv3J4VbExIYCa2A== dependencies: - "@apollographql/apollo-upload-server" "^5.0.3" "@apollographql/graphql-playground-html" "^1.6.6" accept "^3.0.2" - apollo-server-core "2.2.4" + apollo-server-core "2.3.1" boom "^7.1.0" graphql-subscriptions "^1.0.0" graphql-tools "^4.0.0" -apollo-server-plugin-base@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.1.4.tgz#b30eeb033c315e591cbf8b816708536c1c4f920e" - integrity sha512-hsRfTEvtCZkpgad3sXHPCWMO+FWugMgzFEmOOrSWzBbqzWTIRYPc9y0VgjvArl70dHcbgx+WBSc5HTLwFOF2fw== +apollo-server-plugin-base@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.2.1.tgz#d08c9576f7f11ab6e212f352d482faaa4059a31e" + integrity sha512-497NIY9VWRYCrMSkgR11IrIUO4Fsy6aGgnpOJoTdLQAnkDD9SJDSRzwKj4gypUoTT2unfKDng4eMxXVZlHvjOw== -apollo-tracing@0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.3.3.tgz#b819942180480c1c4d89e613cf2eff8f6d8b595a" - integrity sha512-gsTYgDVjtMlnomPq46aky7yk8XshCQfj9rxalCCismLlMomVW44fq+8GKQnZIkFOwiAsazRy4dzZ0cBbygA9sA== +apollo-tracing@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.4.0.tgz#4b939063f4292422ac5a3564b76d1d88dec0a916" + integrity sha512-BlM8iQUQva4fm0xD/pLwkcz0degfB9a/aAn4k4cK36eLVD8XUkl7ptEB0c+cwcj7tOYpV1r5QX1XwdayBzlHSg== dependencies: apollo-server-env "2.2.0" - graphql-extensions "0.3.3" + graphql-extensions "0.4.0" apollo-utilities@^1.0.0, apollo-utilities@^1.0.1: version "1.0.26" @@ -1887,12 +2818,12 @@ append-transform@^0.4.0: dependencies: default-require-extensions "^1.0.0" -aproba@^1.0.3, aproba@^1.1.1: +aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -aproba@^2.0.0: +"aproba@^1.1.2 || 2", aproba@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== @@ -2174,7 +3105,7 @@ b64@4.x.x: dependencies: hoek "6.x.x" -babel-code-frame@^6.26.0: +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= @@ -2350,11 +3281,6 @@ backo2@^1.0.2: resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= -backo@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/backo/-/backo-1.1.0.tgz#a36c4468923f2d265c9e8a709ea56ecdaff807e6" - integrity sha1-o2xEaJI/LSZcnopwnqVuza/4B+Y= - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -2392,10 +3318,18 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -better-sqlite3@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-5.0.1.tgz#5addb5dcf18c9374c570a1eab7694e67fc9437a3" - integrity sha512-dyZk+gDYNPw14maYX5LG/2SCUTiB7jCvETd+bBYqhFyji3oG+UQFN452sUWSjCHCmfg1JtMbLT7WmqB8GLq8Gw== +benchmark@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629" + integrity sha1-CfPeMckWQl1JjMLuVloOvzwqVik= + dependencies: + lodash "^4.17.4" + platform "^1.3.3" + +better-sqlite3@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-5.2.1.tgz#30a6e3290da0f6f17482231bdb59f934dd8dd456" + integrity sha512-FAqPi+jUl6VBBDZpIZPtva+VcYAxw3sbXKjImNBPvK1TmU6bCuaPn42XuNk6VL6Vhjl5KBZpeIesxJlDqTFBqA== dependencies: integer "^2.1.0" tar "^4.4.6" @@ -2410,25 +3344,31 @@ big.js@^3.1.3: resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== -bigi@^1.1.0, bigi@^1.2.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825" - integrity sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU= - bignumber.js@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.0.1.tgz#5d419191370fb558c64e3e5f70d68e5947138832" integrity sha512-zAySveTJXkgLYCBi0b14xzfnOs+f3G6x36I8w2a1+PFQpWk/dp0mI0F+ZZK2bu+3ELewDcSyP+Cfq++NcHX7sg== +bin-links@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/bin-links/-/bin-links-1.1.2.tgz#fb74bd54bae6b7befc6c6221f25322ac830d9757" + integrity sha512-8eEHVgYP03nILphilltWjeIjMbKyJo3wvp9K816pHbhP301ismzw15mxAAEVQ/USUwcP++1uNrbERbp8lOA6Fg== + dependencies: + bluebird "^3.5.0" + cmd-shim "^2.0.2" + gentle-fs "^2.0.0" + graceful-fs "^4.1.11" + write-file-atomic "^2.3.0" + binary-extensions@^1.0.0: version "1.12.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg== bindings@^1.2.1, bindings@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" - integrity sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5" + integrity sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew== bip32@^1.0.2: version "1.0.2" @@ -2442,19 +3382,6 @@ bip32@^1.0.2: typeforce "^1.11.5" wif "^2.0.6" -bip38@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/bip38/-/bip38-2.0.2.tgz#6f7762bc90b0bdf63b489ff95349354aecf9baee" - integrity sha512-22KDak0RDyghFbR0Si7wyq9IgY423YzGYzWLpGeofH3DaolOQqjD3mNN08eFoubKlbyclOQKFwtONMv2SD9V3A== - dependencies: - bigi "^1.2.0" - browserify-aes "^1.0.1" - bs58check "<3.0.0" - buffer-xor "^1.0.2" - create-hash "^1.1.1" - ecurve "^1.0.0" - scryptsy "^2.0.0" - bip39@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/bip39/-/bip39-2.5.0.tgz#51cbd5179460504a63ea3c000db3f787ca051235" @@ -2520,10 +3447,10 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boom@7.x.x, boom@^7.1.0, boom@^7.1.1, boom@^7.2.0: - version "7.2.2" - resolved "https://registry.yarnpkg.com/boom/-/boom-7.2.2.tgz#ac92101451aa5cea901aed07d881dd32b4f08345" - integrity sha512-IFUbOa8PS7xqmhIjpeStwT3d09hGkNYQ6aj2iELSTxcVs2u0aKn1NzhkdUQSzsRg1FVkj3uit3I6mXQCBixw+A== +boom@7.x.x, boom@^7.1.0, boom@^7.1.1, boom@^7.2.0, boom@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9" + integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A== dependencies: hoek "6.x.x" @@ -2534,13 +3461,6 @@ boom@^5.2.0: dependencies: hoek "4.x.x" -boom@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9" - integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A== - dependencies: - hoek "6.x.x" - bounce@1.x.x: version "1.2.3" resolved "https://registry.yarnpkg.com/bounce/-/bounce-1.2.3.tgz#2b286d36eb21d5f08fe672dd8cd37a109baad121" @@ -2600,11 +3520,6 @@ brorand@^1.0.1: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-fingerprint@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/browser-fingerprint/-/browser-fingerprint-0.0.1.tgz#8df3cdca25bf7d5b3542d61545d730053fce604a" - integrity sha1-jfPNyiW/fVs1QtYVRdcwBT/OYEo= - browser-process-hrtime@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" @@ -2617,7 +3532,7 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" -browserify-aes@^1.0.0, browserify-aes@^1.0.1, browserify-aes@^1.0.4, browserify-aes@^1.0.6: +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== @@ -2676,14 +3591,21 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.3.4.tgz#4477b737db6a1b07077275b24791e680d4300425" - integrity sha512-u5iz+ijIMUlmV8blX82VGFrB9ecnUg5qEt55CMZ/YJEhha+d8qpBfOFuutJ6F/VKRXjZoD33b6uvarpPxcl3RA== +browserslist@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.3.5.tgz#1a917678acc07b55606748ea1adf9846ea8920f7" + integrity sha512-z9ZhGc3d9e/sJ9dIx5NFXkKoaiQTnrvrMsN3R1fGb1tkWWNSz12UewJn9TNxGo1l7J23h0MRaPmk7jfeTZYs1w== dependencies: - caniuse-lite "^1.0.30000899" - electron-to-chromium "^1.3.82" - node-releases "^1.0.1" + caniuse-lite "^1.0.30000912" + electron-to-chromium "^1.3.86" + node-releases "^1.0.5" + +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" bs58@^4.0.0: version "4.0.1" @@ -2708,7 +3630,7 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" -buffer-from@^1.0.0: +buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -2723,7 +3645,7 @@ buffer-writer@2.0.0: resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== -buffer-xor@^1.0.2, buffer-xor@^1.0.3: +buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= @@ -2737,19 +3659,7 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -bugsnag@^2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/bugsnag/-/bugsnag-2.4.3.tgz#c63129d6a95b9719774ac941537412300fc79044" - integrity sha512-7gjpRE+J0BBbwYvmZeYo2ZyX3nCDX+ITqHd5wNb+t6KBXwhvuEbyJrmsDE/U32ndsV441jwaGtJ1o2ppLoQXTg== - dependencies: - backo "^1.1.0" - cuid "^1.3.8" - json-stringify-safe "~5.0.1" - promise "7.x" - request "^2.81.0" - stack-trace "~0.0.9" - -builtin-modules@^1.0.0: +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= @@ -2867,13 +3777,6 @@ caller-callsite@^2.0.0: dependencies: callsites "^2.0.0" -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= - dependencies: - callsites "^0.2.0" - caller-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" @@ -2881,11 +3784,6 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= - callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -2931,10 +3829,10 @@ camelcase@^4.0.0, camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -caniuse-lite@^1.0.30000899: - version "1.0.30000910" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000910.tgz#755d5181d4b006e5a2b59b1ffa05d0a0470039f5" - integrity sha512-u/nxtHGAzCGZzIxt3dA/tpSPOcirBZFWKwz1EPz4aaupnBI2XR0Rbr74g0zc6Hzy41OEM4uMoZ38k56TpYAWjQ== +caniuse-lite@^1.0.30000912: + version "1.0.30000918" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000918.tgz#6288f79da3c5c8b45e502f47ad8f3eb91f1379a9" + integrity sha512-CAZ9QXGViBvhHnmIHhsTPSWFBujDaelKnUj7wwImbyQRxmXynYqKGi3UaZTSz9MoVh+1EVxOS/DFIkrJYgR3aw== capture-console@^1.0.1: version "1.0.1" @@ -2957,6 +3855,14 @@ capture-stack-trace@^1.0.0: resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== +cardinal@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" + integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= + dependencies: + ansicolors "~0.3.2" + redeyed "~2.1.0" + caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" @@ -2985,7 +3891,7 @@ catbox@10.x.x: hoek "6.x.x" joi "14.x.x" -chalk@2.4.1, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1: +chalk@2.4.1, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== @@ -3070,11 +3976,6 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -3085,11 +3986,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-stack@^1.0.0: +clean-stack@^1.0.0, clean-stack@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" integrity sha1-noIVAa6XmYbEax1m0tQy2y/UrjE= +clean-stack@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.0.0.tgz#301bfa9e8dd2d3d984c0e542f7aa67b996f63e0a" + integrity sha512-VEoL9Qh7I8s8iHnV53DaeWSt8NJ0g3khMfK6NiCPB7H657juhro+cSw2O88uo3bo0c0X5usamtXk0/Of0wXa5A== + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -3102,10 +4008,10 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-progress@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-2.1.0.tgz#a212e7e6b685687644ca703904ea49c34ee302c8" - integrity sha512-mY0GFIADTooScUe8ERTiQjJoOvXv1z0SzA8gzKO8imLqB7tBwEnNN10gWHcKoltDL4gLdi1GGoPEbxxbvJtR4A== +cli-progress@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-2.1.1.tgz#45ee1b143487c19043a3262131ccb4676f87f032" + integrity sha512-TSJw3LY9ZRSis7yYzQ7flIdtQMbacd9oYoiFphJhI4SzgmqF0zErO+uNv0lbUjk1L4AGfHQJ4OVYYzW+JV66KA== dependencies: colors "^1.1.2" string-width "^2.1.1" @@ -3135,6 +4041,33 @@ cli-truncate@^0.2.1: slice-ansi "0.0.4" string-width "^1.0.1" +cli-ux@^4.9.0: + version "4.9.3" + resolved "https://registry.yarnpkg.com/cli-ux/-/cli-ux-4.9.3.tgz#4c3e070c1ea23eef010bbdb041192e0661be84ce" + integrity sha512-/1owvF0SZ5Gn54cgrikJ0QskgTzeg30HGjkmjFoaHDJzAqFpuX1DBpFR8aLvsE1J5s9MgeYRENQK4BFwOag5VA== + dependencies: + "@oclif/errors" "^1.2.2" + "@oclif/linewrap" "^1.0.0" + "@oclif/screen" "^1.0.3" + ansi-escapes "^3.1.0" + ansi-styles "^3.2.1" + cardinal "^2.1.1" + chalk "^2.4.1" + clean-stack "^2.0.0" + extract-stack "^1.0.0" + fs-extra "^7.0.0" + hyperlinker "^1.0.0" + indent-string "^3.2.0" + is-wsl "^1.1.0" + lodash "^4.17.11" + password-prompt "^1.0.7" + semver "^5.6.0" + strip-ansi "^5.0.0" + supports-color "^5.5.0" + supports-hyperlinks "^1.0.1" + treeify "^1.1.0" + tslib "^1.9.3" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -3276,10 +4209,10 @@ colors@1.0.3: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.1.2, colors@^1.2.1, colors@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.2.tgz#2df8ff573dfbf255af562f8ce7181d6b971a359b" - integrity sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ== +colors@^1.1.2, colors@^1.2.1, colors@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" + integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== colorspace@1.1.x: version "1.1.1" @@ -3304,7 +4237,7 @@ combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@~1.0.5, combined dependencies: delayed-stream "~1.0.0" -commander@2.19.0, commander@^2.14.1, commander@^2.19.0, commander@^2.9.0: +commander@*, commander@2.19.0, commander@^2.12.1, commander@^2.14.1, commander@^2.19.0, commander@^2.9.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== @@ -3384,11 +4317,6 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= - content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" @@ -3523,20 +4451,15 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js@^1.1.1: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= - -core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: - version "2.5.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" - integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw== +core-js@^2.4.0, core-js@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.0.tgz#1e30793e9ee5782b307e37ffa22da0eacddd84d4" + integrity sha512-kLRC6ncVpuEW/1kwrOXYX6KQASCVtrh1gQr/UiaVgFlf9WE5Vp+lNe5+h3LuMr5PAucWnnEXwH0nQHRH/gpGtw== core-js@^3.0.0-beta.3: - version "3.0.0-beta.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.3.tgz#b0f22009972b8c6c04550ebf38513ca4b3cc9559" - integrity sha512-kM/OfrnMThP5PwGAj5HhQLdjUqzjrllqN2EVnk/X9qrLsfYjR2hzZ+E/8CzH0xuosexZtqMTLQrk//BULrBj9w== + version "3.0.0-beta.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.5.tgz#0d09e0cbb45eedef427e72cc1d7725602805f909" + integrity sha512-lv/UPXe8QIvAX4XEgz3u9gpSbYr0Et6gaVhwMEH6SN9Uk+aIhk9IMwQUa35pymUiA4t2THPOaqysDJtX4jcm3w== core-js@~2.3.0: version "2.3.0" @@ -3557,7 +4480,7 @@ cosmiconfig@5.0.6: js-yaml "^3.9.0" parse-json "^4.0.0" -cosmiconfig@^5.0.2, cosmiconfig@^5.0.6: +cosmiconfig@^5.0.2, cosmiconfig@^5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA== @@ -3567,6 +4490,35 @@ cosmiconfig@^5.0.2, cosmiconfig@^5.0.6: js-yaml "^3.9.0" parse-json "^4.0.0" +cp-file@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-6.0.0.tgz#f38477ece100b403fcf780fd34d030486beb693e" + integrity sha512-OtHMgPugkgwHlbph25wlMKd358lZNhX1Y2viUpPoFmlBPlEiPIRhztYWha11grbGPnlM+urp5saVmwsChCIOEg== + dependencies: + graceful-fs "^4.1.2" + make-dir "^1.0.0" + nested-error-stacks "^2.0.0" + pify "^3.0.0" + safe-buffer "^5.0.1" + +cpy-cli@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cpy-cli/-/cpy-cli-2.0.0.tgz#13f1528a231605c52ee7b7f74848e4be82253274" + integrity sha512-LzrtY3lBWvFZcw4lXgkEbbDUd7y78juC3C5l7gj3UyezMEZF0Be9fjCVLN1HoZAzdMDeC3KHehWpHBJvgVAPkw== + dependencies: + cpy "^7.0.0" + meow "^5.0.0" + +cpy@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cpy/-/cpy-7.0.1.tgz#d817e4d81bd7f0f25ff812796c5f1392dc0fb485" + integrity sha512-Zo52tXKLJcgy/baacn6KaNoRAakkl2wb+R4u6qJ4wlD0uchncwRQcIk66PlGlkzuToCJO6A6PWX27Tdwc8LU2g== + dependencies: + arrify "^1.0.1" + cp-file "^6.0.0" + globby "^8.0.1" + nested-error-stacks "^2.0.0" + create-ecdh@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" @@ -3582,7 +4534,7 @@ create-error-class@^3.0.0: dependencies: capture-stack-trace "^1.0.0" -create-hash@^1.1.0, create-hash@^1.1.1, create-hash@^1.1.2, create-hash@^1.2.0: +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== @@ -3681,15 +4633,6 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" -cuid@^1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/cuid/-/cuid-1.3.8.tgz#4b875e0969bad764f7ec0706cf44f5fb0831f6b7" - integrity sha1-S4deCWm612T37AcGz0T1+wgx9rc= - dependencies: - browser-fingerprint "0.0.1" - core-js "^1.1.1" - node-fingerprint "0.0.2" - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -3743,9 +4686,9 @@ data-urls@^1.0.0: whatwg-url "^7.0.0" date-fns@^1.27.2: - version "1.29.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" - integrity sha512-lbTXWZ6M20cWH8N9S6afb0SBm6tMk+uUg6z3MqHPKE9atmsY3kJkTm8vKe93izJ2B2+q5MV990sM2CHgtAZaOw== + version "1.30.1" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" + integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== date-now@^0.1.4: version "0.1.4" @@ -3793,6 +4736,13 @@ debug@^4.0.1, debug@^4.1.0: dependencies: ms "^2.1.1" +debug@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -3843,7 +4793,7 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepmerge@^3.0.0: +deepmerge@*, deepmerge@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.0.0.tgz#ca7903b34bfa1f8c2eab6779280775a411bfc6ba" integrity sha512-a8z8bkgHsAML+uHLqmMS83HHlpy3PvZOOuiTQqaa3wu8ZVg3h0hqHk6aCsGdOnZV2XMM/FRimNGjUh0KCcmHBw== @@ -3905,6 +4855,15 @@ degenerator@^1.0.4: escodegen "1.x.x" esprima "3.x.x" +del-cli@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/del-cli/-/del-cli-1.1.0.tgz#27557d69a0b7df99dcbaa1e34a09e6ac6591d2c4" + integrity sha1-J1V9aaC335ncuqHjSgnmrGWR0sQ= + dependencies: + del "^3.0.0" + meow "^3.6.0" + update-notifier "^2.1.0" + del@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" @@ -4024,15 +4983,10 @@ dir-glob@^2.0.0: arrify "^1.0.1" path-type "^3.0.0" -directory-tree@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/directory-tree/-/directory-tree-2.1.1.tgz#db42ab3bda37b0e7de51654ce9709b49cea2ba55" - integrity sha512-9uaftXCRHRntOsmeSxTMLNp52N0tb5eSXkLTIdbDnuC85lleB6/Xub6CG4swSrkS5XXHA56OpTiRj5ntRlsjCg== - -docdash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/docdash/-/docdash-1.0.0.tgz#5b7df10fed3d341fc4416a8978c65ad561869d18" - integrity sha512-HhK72PT4z55og8FDqskO/tTYXxU+LovRz+9pCDHLnUoPchkxjdIJidS+96LqW3CLrRdBmnkDRrcVrDFGLIluTw== +docdash@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/docdash/-/docdash-1.0.1.tgz#33d8d50c619f137d3db6948d56d9f9dcb5c8d000" + integrity sha512-m6qqQ3zQZnGv+P/GGpKWja9Zrf50X1NJa9HsPe0DYeaZYUbW+afcSAoG2MUY4bQGeOpq0dkuLhsz2QbefXu95g== dockerfile-ast@0.0.12: version "0.0.12" @@ -4041,21 +4995,6 @@ dockerfile-ast@0.0.12: dependencies: vscode-languageserver-types "^3.5.0" -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -4137,20 +5076,12 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -ecurve@^1.0.0: - version "1.0.6" - resolved "https://registry.yarnpkg.com/ecurve/-/ecurve-1.0.6.tgz#dfdabbb7149f8d8b78816be5a7d5b83fcf6de797" - integrity sha512-/BzEjNfiSuB7jIWKcS/z8FK9jNjmEWvUV2YZ4RLSmcDtP7Lq0m6FvDuSnJpBlDpGRpfRQeTLGLBI8H+kEv0r+w== - dependencies: - bigi "^1.1.0" - safe-buffer "^5.0.1" - editions@^1.1.1, editions@^1.3.3, editions@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg== -editions@^2.0.2: +editions@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/editions/-/editions-2.1.0.tgz#5c6f6341ef19ee362a3bcbb907fe68e696dbc69e" integrity sha512-yKrimWcvOXcYXtqsOeebbMLynm9qbYVd0005wveGU2biPxJaJoxA0jtaZrxiMe3mAanLr5lxoYFVz5zjv9JdnA== @@ -4177,10 +5108,10 @@ elasticsearch@^15.2.0: chalk "^1.0.0" lodash "^4.17.10" -electron-to-chromium@^1.3.82: - version "1.3.84" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.84.tgz#2e55df59e818f150a9f61b53471ebf4f0feecc65" - integrity sha512-IYhbzJYOopiTaNWMBp7RjbecUBsbnbDneOP86f3qvS0G0xfzwNSvMJpTrvi5/Y1gU7tg2NAgeg8a8rCYvW9Whw== +electron-to-chromium@^1.3.86: + version "1.3.90" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.90.tgz#b4c51b8303beff18f2b74817402bf4898e09558a" + integrity sha512-IjJZKRhFbWSOX1w0sdIXgp4CMRguu6UYcTckyFF/Gjtemsu/25eZ+RXwFlV+UWcIueHyQA1UnRJxocTpH5NdGA== elegant-spinner@^1.0.1: version "1.0.1" @@ -4205,6 +5136,11 @@ email-validator@^2.0.4: resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -4236,7 +5172,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^4.1.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -4245,6 +5181,11 @@ enhanced-resolve@^4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" +env-paths@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.0.0.tgz#5a71723f3df7ca98113541f6fa972184f2c9611d" + integrity sha512-13VpSqOO91W3MskXxWJ8x+Y33RKaPT53/HviPp8QcMmEbAJaPFEm8BmMpxCHroJ5rGADqr34Zl6zosBt3F+xAA== + env-variable@0.0.x: version "0.0.5" resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88" @@ -4286,7 +5227,14 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.5.1, es-abstract@^1.6.1: +error-stack-parser@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.2.tgz#4ae8dbaa2bf90a8b450707b9149dcabca135520d" + integrity sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw== + dependencies: + stackframe "^1.0.4" + +es-abstract@^1.5.1: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" integrity sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA== @@ -4371,89 +5319,6 @@ escodegen@1.x.x, escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -eslint-config-airbnb-base@^13.1.0: - version "13.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz#b5a1b480b80dfad16433d6c4ad84e6605052c05c" - integrity sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw== - dependencies: - eslint-restricted-globals "^0.1.1" - object.assign "^4.1.0" - object.entries "^1.0.4" - -eslint-config-prettier@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-3.3.0.tgz#41afc8d3b852e757f06274ed6c44ca16f939a57d" - integrity sha512-Bc3bh5bAcKNvs3HOpSi6EfGA2IIp7EzWcg2tS4vP7stnXu/J1opihHDM7jI9JCIckyIDTgZLSWn7J3HY0j2JfA== - dependencies: - get-stdin "^6.0.0" - -eslint-import-resolver-node@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" - integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== - dependencies: - debug "^2.6.9" - resolve "^1.5.0" - -eslint-module-utils@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" - integrity sha1-snA2LNiLGkitMIl2zn+lTphBF0Y= - dependencies: - debug "^2.6.8" - pkg-dir "^1.0.0" - -eslint-plugin-es@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz#475f65bb20c993fc10e8c8fe77d1d60068072da6" - integrity sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw== - dependencies: - eslint-utils "^1.3.0" - regexpp "^2.0.1" - -eslint-plugin-import@^2.14.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8" - integrity sha512-FpuRtniD/AY6sXByma2Wr0TXvXJ4nA/2/04VPlfpmUDPOpOY264x+ILiwnrk/k4RINgDAyFZByxqPUbSQ5YE7g== - dependencies: - contains-path "^0.1.0" - debug "^2.6.8" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.2.0" - has "^1.0.1" - lodash "^4.17.4" - minimatch "^3.0.3" - read-pkg-up "^2.0.0" - resolve "^1.6.0" - -eslint-plugin-jest@^22.1.0: - version "22.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.1.0.tgz#9a4dfa3367563e8301560a7fb92ec309096dbca3" - integrity sha512-WcQd5LxEoAS20zuWEAd8CX0pVC+gGInZPcsoYvK5w7BrEJNmltyTxYYh1r0ct4GsahD2GvNySlcTcLtK2amFZA== - -eslint-plugin-node@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-8.0.0.tgz#fb9e8911f4543514f154bb6a5924b599aa645568" - integrity sha512-Y+ln8iQ52scz9+rSPnSWRaAxeWaoJZ4wIveDR0vLHkuSZGe44Vk1J4HX7WvEP5Cm+iXPE8ixo7OM7gAO3/OKpQ== - dependencies: - eslint-plugin-es "^1.3.1" - eslint-utils "^1.3.1" - ignore "^5.0.2" - minimatch "^3.0.4" - resolve "^1.8.1" - semver "^5.5.0" - -eslint-plugin-promise@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.0.1.tgz#2d074b653f35a23d1ba89d8e976a985117d1c6a2" - integrity sha512-Si16O0+Hqz1gDHsys6RtFRrW7cCTB6P7p3OJmKp3Y3dxpQE2qwOA7d3xnV+0mBmrPoi0RBnxlCKvqu70te6wjg== - -eslint-restricted-globals@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" - integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc= - eslint-scope@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" @@ -4462,86 +5327,16 @@ eslint-scope@^4.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.0, eslint-utils@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" - integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== - -eslint-visitor-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" - integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== - -eslint@^5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.9.0.tgz#b234b6d15ef84b5849c6de2af43195a2d59d408e" - integrity sha512-g4KWpPdqN0nth+goDNICNXGfJF7nNnepthp46CAlJoJtC5K/cLu3NgCM3AHu1CkJ5Hzt9V0Y0PBAO6Ay/gGb+w== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.5.3" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^2.1.0" - eslint-scope "^4.0.0" - eslint-utils "^1.3.1" - eslint-visitor-keys "^1.0.0" - espree "^4.0.0" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.7.0" - ignore "^4.0.6" - imurmurhash "^0.1.4" - inquirer "^6.1.0" - is-resolvable "^1.1.0" - js-yaml "^3.12.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.5" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - regexpp "^2.0.1" - require-uncached "^1.0.3" - semver "^5.5.1" - strip-ansi "^4.0.0" - strip-json-comments "^2.0.1" - table "^5.0.2" - text-table "^0.2.0" - -espree@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-4.1.0.tgz#728d5451e0fd156c04384a7ad89ed51ff54eb25f" - integrity sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w== - dependencies: - acorn "^6.0.2" - acorn-jsx "^5.0.0" - eslint-visitor-keys "^1.0.0" - esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= -esprima@^4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== - dependencies: - estraverse "^4.0.0" - esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -4549,7 +5344,7 @@ esrecurse@^4.1.0: dependencies: estraverse "^4.1.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= @@ -4671,7 +5466,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -expand-home-dir@0.0.3, expand-home-dir@^0.0.3: +expand-home-dir@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/expand-home-dir/-/expand-home-dir-0.0.3.tgz#72de8a0486cc28a3bbd704635398825b5b62827d" integrity sha1-ct6KBIbMKKO71wRjU5iCW1tign0= @@ -4790,6 +5585,11 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-stack@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-1.0.0.tgz#b97acaf9441eea2332529624b732fc5a1c8165fa" + integrity sha1-uXrK+UQe6iMyUpYktzL8WhyBZfo= + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -4827,12 +5627,12 @@ fast-json-parse@^1.0.3: resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= -fast-levenshtein@~2.0.4: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -4842,10 +5642,10 @@ fast-plural-rules@^0.0.1: resolved "https://registry.yarnpkg.com/fast-plural-rules/-/fast-plural-rules-0.0.1.tgz#7032c8afa979e6dc65a452f0ef5b4ef31eca763b" integrity sha512-0Cxx7LaH7+dNJEBozlisCxqaN5g68VTFT9PyLeFGBHmkPnQ3e46zss+r8pRC94KpzPlitL6m33GVdbMIDiUgqg== -fast-redact@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-1.4.0.tgz#6123d2a23e6bdf76e82b3176d6f11698f2001797" - integrity sha512-WsYhPdWJY+6d/pFJbBNWGUd5ENrBAJ6e7yDWcYNoFZoIQUUbKxnIRGS4d0kZkQlMPB4cLK3r4A0BZXpFxdoGhg== +fast-redact@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-1.4.2.tgz#14989f452ee07f0723fbb483ee778d363135b7ad" + integrity sha512-ttC8IgelNvYqb9RBC+rirgUCVPtPVonfdeRdsHBcBx3kzQat1DafbUKAEhLo5GnvuBqda+Xe1BvblecPpQkZ2Q== fast-safe-stringify@2.0.x, fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.6: version "2.0.6" @@ -4884,14 +5684,6 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - file-stream-rotator@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.4.1.tgz#09f67b86d6ea589d20b7852c51c59de55d916d6d" @@ -4969,6 +5761,11 @@ find-cache-dir@^2.0.0: make-dir "^1.0.0" pkg-dir "^3.0.0" +find-npm-prefix@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/find-npm-prefix/-/find-npm-prefix-1.0.2.tgz#8d8ce2c78b3b4b9e66c8acc6a37c231eb841cfdf" + integrity sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA== + find-parent-dir@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.0.tgz#33c44b429ab2b2f0646299c5f9f718f376ff8d54" @@ -4996,20 +5793,10 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== - dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" - -flatstr@^1.0.5, flatstr@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.8.tgz#0e849229751f2b9f6a0919f8e81e1229e84ba901" - integrity sha512-YXblbv/vc1zuVVUtnKl1hPqqk7TalZCppnKE7Pr8FI/Rp48vzckS/4SJ4Y9O9RNiI82Vcw/FydmtqdQOg1Dpqw== +flatstr@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/flatstr/-/flatstr-1.0.9.tgz#0950d56fec02de1030c1311847ecd58c25690eb9" + integrity sha512-qFlJnOBWDfIaunF54/lBqNKmXOI0HqNhu+mHkLmbaBXlS71PUd9OjFOdyevHt/aHoHB1+eW7eKHgRKOG5aHSpw== flush-write-stream@^1.0.0: version "1.0.3" @@ -5098,6 +5885,11 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-capacitor@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-1.0.1.tgz#ff9dbfa14dfaf4472537720f19c3088ed9278df0" + integrity sha512-XdZK0Q78WP29Vm3FGgJRhRhrBm51PagovzWtW2kJ3Q6cYJbGtZqWSGTSPwvtEkyjIirFd7b8Yes/dpOYjt4RRQ== + fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -5114,7 +5906,7 @@ fs-minipass@^1.2.5: dependencies: minipass "^2.2.1" -fs-vacuum@~1.2.9: +fs-vacuum@^1.2.10, fs-vacuum@~1.2.9: version "1.2.10" resolved "https://registry.yarnpkg.com/fs-vacuum/-/fs-vacuum-1.2.10.tgz#b7629bec07a4031a2548fdf99f5ecf1cc8b31e36" integrity sha1-t2Kb7AekAxolSP35n17PHMizHjY= @@ -5181,16 +5973,11 @@ ftp@~0.3.10: readable-stream "1.1.x" xregexp "2.0.0" -function-bind@^1.1.0, function-bind@^1.1.1: +function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - g-status@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/g-status/-/g-status-2.0.2.tgz#270fd32119e8fc9496f066fe5fe88e0a6bc78b97" @@ -5253,6 +6040,20 @@ genfun@^5.0.0: resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== +gentle-fs@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/gentle-fs/-/gentle-fs-2.0.1.tgz#585cfd612bfc5cd52471fdb42537f016a5ce3687" + integrity sha512-cEng5+3fuARewXktTEGbwsktcldA+YsnUEaXZwcK/3pjSE1X9ObnTs+/8rYf8s+RnIcQm2D5x3rwpN7Zom8Bew== + dependencies: + aproba "^1.1.2" + fs-vacuum "^1.2.10" + graceful-fs "^4.1.11" + iferr "^0.1.5" + mkdirp "^0.5.1" + path-is-inside "^1.0.2" + read-cmd-shim "^1.0.1" + slide "^1.1.6" + get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" @@ -5392,7 +6193,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.0: +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.0: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== @@ -5412,11 +6213,11 @@ global-dirs@^0.1.0: ini "^1.3.4" global-modules-path@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" - integrity sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag== + version "2.3.1" + resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.1.tgz#e541f4c800a1a8514a990477b267ac67525b9931" + integrity sha512-y+shkf4InI7mPRHSo2b/k6ix6+NLDtyccYv86whhxrSGX9wjPX1VMITmrDbE1eh7zkzhiWtW2sHklJYoQ62Cxg== -globals@^11.1.0, globals@^11.7.0: +globals@^11.1.0: version "11.9.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.9.0.tgz#bde236808e987f290768a93d065060d78e6ab249" integrity sha512-5cJVtyXWH8PiJPVLZzzoIizXx944O4OmRro5MWKx5fT4MgcN7OfaMutPeaTdJCCURwbWdhhcCWcKIffPnmTzBg== @@ -5506,9 +6307,9 @@ got@^6.7.1: url-parse-lax "^1.0.0" got@^9.3.2: - version "9.3.2" - resolved "https://registry.yarnpkg.com/got/-/got-9.3.2.tgz#f6e3bd063aa8f461ccd924afa2ba2b61deab3989" - integrity sha512-OyKOUg71IKvwb8Uj0KP6EN3+qVVvXmYsFznU1fnwUnKtDbZnwSlAi7muNlu4HhBfN9dImtlgg9e7H0g5qVdaeQ== + version "9.4.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.4.0.tgz#3b52a54306514b0404b869e1ba572b594772f2b1" + integrity sha512-k15lhRXITxW0eURHfEGzV+8pBYBHtTrYterFlMzP5rXQcQMPikDC2wvZkgivcJwGH4bv1JzMUTPlHfYGhuXJnw== dependencies: "@sindresorhus/is" "^0.12.0" "@szmarczak/http-timer" "^1.1.0" @@ -5528,23 +6329,23 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@~4.1.9: integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== graphlib@^2.1.1, graphlib@^2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.5.tgz#6afe1afcc5148555ec799e499056795bd6938c87" - integrity sha512-XvtbqCcw+EM5SqQrIetIKKD+uZVNQtDPD1goIg7K73RuRZtVI5rYMdcCVSHm/AS1sCBZ7vt0p5WgXouucHQaOA== + version "2.1.7" + resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.7.tgz#b6a69f9f44bd9de3963ce6804a2fc9e73d86aecc" + integrity sha512-TyI9jIy2J4j0qgPmOOrHTCtpPqJGN/aurBwc6ZT+bRii+di1I+Wv3obRhVrmBEXet+qkMaEX67dXrwsd3QQM6w== dependencies: - lodash "^4.11.1" + lodash "^4.17.5" -graphql-extensions@0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.3.3.tgz#277efe11976bbdfd59915551606a2d550247bb45" - integrity sha512-pudOaHq7Ok+rh1ElzlqFaoYZWGefUNsqn/jX6eKns7rl0VHuB4qZBfhpVLTpquJpM6Y19/hsCYZNPfnUVMFIiA== +graphql-extensions@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.4.0.tgz#5857c7b7b9f20dbccbfd88730fffa5963b3c61ee" + integrity sha512-8TUgIIUVpXWOcqq9RdmTSHUrhc3a/s+saKv9cCl8TYWHK9vyJIdea7ZaSKHGDthZNcsN+C3LulZYRL3Ah8ukoA== dependencies: "@apollographql/apollo-tools" "^0.2.6" -graphql-extensions@0.3.4: - version "0.3.4" - resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.3.4.tgz#a6636ba8b41d5f9ea917085e5d4518adbba0e21f" - integrity sha512-fOI+0p2+1xnwLQx3DvInSuPyGjwsW4RWxTI8caZ91NTFfsa0K3EBFBD71BNO3Ziw2P1nIa8Ip+WRT/Dx3GML0g== +graphql-extensions@0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.4.1.tgz#92c49a8409ffbfb24559d7661ab60cc90d6086e4" + integrity sha512-Xei4rBxbsTHU6dYiq9y1xxbpRMU3+Os7yD3vXV5W4HbTaxRMizDmu6LAvV4oBEi0ttwICHARQjYTjDTDhHnxrQ== dependencies: "@apollographql/apollo-tools" "^0.2.6" @@ -5581,6 +6382,16 @@ graphql-tools@^4.0.0: iterall "^1.1.3" uuid "^3.1.0" +graphql-upload@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-8.0.2.tgz#1c1f116f15b7f8485cf40ff593a21368f0f58856" + integrity sha512-u8a5tKPfJ0rU4MY+B3skabL8pEjMkm3tUzq25KBx6nT0yEWmqUO7Z5tdwvwYLFpkLwew94Gue0ARbZtar3gLTw== + dependencies: + busboy "^0.2.14" + fs-capacitor "^1.0.0" + http-errors "^1.7.1" + object-path "^0.11.4" + graphql@0.13.2: version "0.13.2" resolved "https://registry.yarnpkg.com/graphql/-/graphql-0.13.2.tgz#4c740ae3c222823e7004096f832e7b93b2108270" @@ -5593,7 +6404,7 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@4.x.x, handlebars@^4.0.12, handlebars@^4.0.2, handlebars@^4.0.3: +handlebars@4.x.x, handlebars@^4.0.12, handlebars@^4.0.2, handlebars@^4.0.3, handlebars@^4.0.6: version "4.0.12" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5" integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA== @@ -5713,6 +6524,11 @@ has-flag@^1.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -5782,9 +6598,9 @@ hash-base@^3.0.0: safe-buffer "^5.0.1" hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" - integrity sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA== + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.1" @@ -5808,6 +6624,11 @@ heavy@6.x.x: hoek "6.x.x" joi "14.x.x" +highlight.js@^9.0.0: + version "9.13.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.13.1.tgz#054586d53a6863311168488a0f58d6c505ce641e" + integrity sha512-Sc28JNQNDzaH6PORtRLMvif9RSn1mYuOoX3omVjnb0+HbpPygU2ALBI0R/wsiqCb4/fcp07Gdo8g+fhtFrQl6A== + hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -5832,15 +6653,10 @@ hoek@5.x.x, hoek@^5.0.2: resolved "https://registry.yarnpkg.com/hoek/-/hoek-5.0.4.tgz#0f7fa270a1cafeb364a4b2ddfaa33f864e4157da" integrity sha512-Alr4ZQgoMlnere5FZJsIyfIjORBqZll5POhDsF4q64dPuJR6rNxXdDxtHSQq8OXRurhmx+PWYEE8bXRROY8h0w== -hoek@6.x.x: - version "6.0.4" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.0.4.tgz#8db638130825534575e8e4e80f97ca66108e6382" - integrity sha512-9D47elppcwrTx2x9B6TrovxnUtlTBYFcHGgo0+LRA1+YfUkCecT//41ovdh6zbl7whB9Hc2whRO1c6lzPoTgww== - -hoek@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.1.tgz#dae8ca1c97b091b123281d87d4eba38d71580b7d" - integrity sha512-q60PigXXRtRFSe1+Eal3y/wlIq5weFsYPiyulkg1EAObgWhkDqSwj4xqgtd7qT3IpS6e4eLigyMWH6duPRI7QA== +hoek@6.x.x, hoek@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.2.tgz#99e6d070561839de74ee427b61aa476bd6bddfd6" + integrity sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q== home-or-tmp@^2.0.0: version "2.0.0" @@ -5887,6 +6703,17 @@ http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-errors@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.1.tgz#6a4ffe5d35188e1c39f872534690585852e1f027" + integrity sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -5933,12 +6760,12 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -husky@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/husky/-/husky-1.2.0.tgz#d631dda1e4a9ee8ba69a10b0c51a0e2c66e711e5" - integrity sha512-/ib3+iycykXC0tYIxsyqierikVa9DA2DrT32UEirqNEFVqOj1bFMTgP3jAz8HM7FgC/C8pc/BTUa9MV2GEkZaA== +husky@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-1.3.0.tgz#fbb97b5e52739d945fd86c3bf92def0ea60175c4" + integrity sha512-0Yfh4PkWr8Nrume/lEMgSgK5zg8Q19jGM7jDmN7H92VrqgcYSbn+CM1iLGmAvQSNWuaPx9iq/rq/4Y8LPX6dLA== dependencies: - cosmiconfig "^5.0.6" + cosmiconfig "^5.0.7" execa "^1.0.0" find-up "^3.0.0" get-stdin "^6.0.0" @@ -5949,6 +6776,11 @@ husky@^1.2.0: run-node "^1.0.0" slash "^2.0.0" +hyperlinker@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" + integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== + iconv-lite@0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" @@ -5985,16 +6817,6 @@ ignore@^3.3.5: resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -ignore@^5.0.2: - version "5.0.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.0.4.tgz#33168af4a21e99b00c5d41cbadb6a6cb49903a45" - integrity sha512-WLsTMEhsQuXpCiG173+f3aymI43SXa+fB1rSfbzyP4GkPP+ZFVuO0/3sFUGNBtifisPeDcl/uD/Y2NxZ7xFq4g== - immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" @@ -6046,7 +6868,7 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" -indent-string@^3.0.0: +indent-string@^3.0.0, indent-string@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= @@ -6091,7 +6913,7 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= -ini@^1.3.0, ini@^1.3.2, ini@^1.3.4, ini@~1.3.0, ini@~1.3.4: +ini@^1.3.0, ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0, ini@~1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== @@ -6144,10 +6966,10 @@ inquirer@^3.0.0: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^6.1.0, inquirer@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" - integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== +inquirer@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.1.tgz#9943fc4882161bdb0b0c9276769c75b32dbfcd52" + integrity sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg== dependencies: ansi-escapes "^3.0.0" chalk "^2.0.0" @@ -6160,7 +6982,7 @@ inquirer@^6.1.0, inquirer@^6.2.0: run-async "^2.2.0" rxjs "^6.1.0" string-width "^2.1.0" - strip-ansi "^4.0.0" + strip-ansi "^5.0.0" through "^2.3.6" int64-buffer@^0.1.9: @@ -6173,7 +6995,7 @@ integer@^2.1.0: resolved "https://registry.yarnpkg.com/integer/-/integer-2.1.0.tgz#29134ea2f7ba3362ed4dbe6bcca992b1f18ff276" integrity sha512-vBtiSgrEiNocWvvZX1RVfeOKa2mCHLZQ2p9nkQkQZ/BvEiY+6CcUz0eyjvIiewjJoeNidzg2I+tpPJvpyspL1w== -interpret@^1.1.0: +interpret@^1.0.0, interpret@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" integrity sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ= @@ -6544,11 +7366,6 @@ is-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= -is-resolvable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" @@ -6620,6 +7437,11 @@ isemail@3.x.x: dependencies: punycode "2.x.x" +iserror@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/iserror/-/iserror-0.0.2.tgz#bd53451fe2f668b9f2402c1966787aaa2c7c0bf5" + integrity sha1-vVNFH+L2aLnyQCwZZnh6qix8C/U= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7213,11 +8035,6 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -7230,6 +8047,13 @@ json-stringify-safe@5.0.x, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0. resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@2.x, json5@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" + integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== + dependencies: + minimist "^1.2.0" + json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -7242,13 +8066,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" - integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ== - dependencies: - minimist "^1.2.0" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -7335,6 +8152,11 @@ kleur@^2.0.1: resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300" integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ== +kleur@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.1.tgz#4f5b313f5fa315432a400f19a24db78d451ede62" + integrity sha512-P3kRv+B+Ra070ng2VKQqW4qW7gd/v3iD8sy/zOdcYRsfiD+QBokQNOps/AfP6Hr48cBhIIBFWckB9aO+IZhrWg== + kuler@1.0.x: version "1.0.1" resolved "https://registry.yarnpkg.com/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6" @@ -7373,50 +8195,139 @@ left-pad@^1.3.0: resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== -lerna@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.5.0.tgz#fd989b8992701e90e10aa5b3970d6f7a3512ba64" - integrity sha512-12kZb2yCJtx3rhLycepzHTbR6suqLaqky2hSldGQh9+B7FXFgvbQJXwzoi+Y7+1cstmKHHyockdRYFLiLmiEYA== - dependencies: - "@lerna/add" "^3.5.0" - "@lerna/bootstrap" "^3.5.0" - "@lerna/changed" "^3.5.0" - "@lerna/clean" "^3.5.0" - "@lerna/cli" "^3.2.0" - "@lerna/create" "^3.5.0" - "@lerna/diff" "^3.5.0" - "@lerna/exec" "^3.5.0" - "@lerna/import" "^3.5.0" - "@lerna/init" "^3.5.0" - "@lerna/link" "^3.5.0" - "@lerna/list" "^3.5.0" - "@lerna/publish" "^3.5.0" - "@lerna/run" "^3.5.0" - "@lerna/version" "^3.5.0" +lerna@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.6.0.tgz#b6616873fa038ee1dae514e04322c191ff71a369" + integrity sha512-iQFAgrgtv18SI5LtQBBca0WVeYvk2r8eYgiEQtcZBT63T5R9RVv+snsviIiOp0z6gD43tcyiWXiLvBdp1IY/Rg== + dependencies: + "@lerna/add" "^3.6.0" + "@lerna/bootstrap" "^3.6.0" + "@lerna/changed" "^3.6.0" + "@lerna/clean" "^3.6.0" + "@lerna/cli" "^3.6.0" + "@lerna/create" "^3.6.0" + "@lerna/diff" "^3.6.0" + "@lerna/exec" "^3.6.0" + "@lerna/import" "^3.6.0" + "@lerna/init" "^3.6.0" + "@lerna/link" "^3.6.0" + "@lerna/list" "^3.6.0" + "@lerna/publish" "^3.6.0" + "@lerna/run" "^3.6.0" + "@lerna/version" "^3.6.0" import-local "^1.0.0" - npmlog "^4.1.2" + libnpm "^2.0.1" leven@2.1.0, leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +libnpm@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/libnpm/-/libnpm-2.0.1.tgz#a48fcdee3c25e13c77eb7c60a0efe561d7fb0d8f" + integrity sha512-qTKoxyJvpBxHZQB6k0AhSLajyXq9ZE/lUsZzuHAplr2Bpv9G+k4YuYlExYdUCeVRRGqcJt8hvkPh4tBwKoV98w== + dependencies: + bin-links "^1.1.2" + bluebird "^3.5.3" + find-npm-prefix "^1.0.2" + libnpmaccess "^3.0.1" + libnpmconfig "^1.2.1" + libnpmhook "^5.0.2" + libnpmorg "^1.0.0" + libnpmpublish "^1.1.0" + libnpmsearch "^2.0.0" + libnpmteam "^1.0.1" + lock-verify "^2.0.2" + npm-lifecycle "^2.1.0" + npm-logical-tree "^1.2.1" + npm-package-arg "^6.1.0" + npm-profile "^4.0.1" + npm-registry-fetch "^3.8.0" + npmlog "^4.1.2" + pacote "^9.2.3" + read-package-json "^2.0.13" + stringify-package "^1.0.0" + +libnpmaccess@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8" + integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA== + dependencies: + aproba "^2.0.0" + get-stream "^4.0.0" + npm-package-arg "^6.1.0" + npm-registry-fetch "^3.8.0" + +libnpmconfig@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/libnpmconfig/-/libnpmconfig-1.2.1.tgz#c0c2f793a74e67d4825e5039e7a02a0044dfcbc0" + integrity sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA== + dependencies: + figgy-pudding "^3.5.1" + find-up "^3.0.0" + ini "^1.3.5" + +libnpmhook@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/libnpmhook/-/libnpmhook-5.0.2.tgz#d12817b0fb893f36f1d5be20017f2aea25825d94" + integrity sha512-vLenmdFWhRfnnZiNFPNMog6CK7Ujofy2TWiM2CrpZUjBRIhHkJeDaAbJdYCT6W4lcHtyrJR8yXW8KFyq6UAp1g== + dependencies: + aproba "^2.0.0" + figgy-pudding "^3.4.1" + get-stream "^4.0.0" + npm-registry-fetch "^3.8.0" + +libnpmorg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/libnpmorg/-/libnpmorg-1.0.0.tgz#979b868c48ba28c5820e3bb9d9e73c883c16a232" + integrity sha512-o+4eVJBoDGMgRwh2lJY0a8pRV2c/tQM/SxlqXezjcAg26Qe9jigYVs+Xk0vvlYDWCDhP0g74J8UwWeAgsB7gGw== + dependencies: + aproba "^2.0.0" + figgy-pudding "^3.4.1" + get-stream "^4.0.0" + npm-registry-fetch "^3.8.0" + +libnpmpublish@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/libnpmpublish/-/libnpmpublish-1.1.0.tgz#773bd6fc9ed247e4a41a68ebd69fdc096ea630a3" + integrity sha512-mQ3LT2EWlpJ6Q8mgHTNqarQVCgcY32l6xadPVPMcjWLtVLz7II4WlWkzlbYg1nHGAf+xyABDwS+3aNUiRLkyaA== + dependencies: + aproba "^2.0.0" + figgy-pudding "^3.5.1" + get-stream "^4.0.0" + lodash.clonedeep "^4.5.0" + normalize-package-data "^2.4.0" + npm-package-arg "^6.1.0" + npm-registry-fetch "^3.8.0" + semver "^5.5.1" + ssri "^6.0.1" + +libnpmsearch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/libnpmsearch/-/libnpmsearch-2.0.0.tgz#de05af47ada81554a5f64276a69599070d4a5685" + integrity sha512-vd+JWbTGzOSfiOc+72MU6y7WqmBXn49egCCrIXp27iE/88bX8EpG64ST1blWQI1bSMUr9l1AKPMVsqa2tS5KWA== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + figgy-pudding "^3.5.1" + get-stream "^4.0.0" + npm-registry-fetch "^3.8.0" -libnpmaccess@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-3.0.1.tgz#5b3a9de621f293d425191aa2e779102f84167fa8" - integrity sha512-RlZ7PNarCBt+XbnP7R6PoVgOq9t+kou5rvhaInoNibhPO7eMlRfS0B8yjatgn2yaHIwWNyoJDolC/6Lc5L/IQA== +libnpmteam@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/libnpmteam/-/libnpmteam-1.0.1.tgz#ff704b1b6c06ea674b3b1101ac3e305f5114f213" + integrity sha512-gDdrflKFCX7TNwOMX1snWojCoDE5LoRWcfOC0C/fqF7mBq8Uz9zWAX4B2RllYETNO7pBupBaSyBDkTAC15cAMg== dependencies: aproba "^2.0.0" + figgy-pudding "^3.4.1" get-stream "^4.0.0" - npm-package-arg "^6.1.0" npm-registry-fetch "^3.8.0" lie@~3.1.0: @@ -7512,16 +8423,6 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -7562,6 +8463,14 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" +lock-verify@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lock-verify/-/lock-verify-2.0.2.tgz#148e4f85974915c9e3c34d694b7de9ecb18ee7a8" + integrity sha512-QNVwK0EGZBS4R3YQ7F1Ox8p41Po9VGl2QG/2GsuvTbkJZYSsPeWHKMbbH6iZMCHWSMww5nrJroZYnGzI4cePuw== + dependencies: + npm-package-arg "^5.1.2 || 6" + semver "^5.4.1" + lockfile@~1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609" @@ -7622,11 +8531,6 @@ lodash.clonedeep@^4.3.0, lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0: resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= -lodash.clonedeepwith@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz#6ee30573a03a1a60d670a62ef33c10cf1afdbdd4" - integrity sha1-buMFc6A6GmDWcKYu8zwQzxr9vdQ= - lodash.compact@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash.compact/-/lodash.compact-3.0.1.tgz#540ce3837745975807471e16b4a2ba21e7256ca5" @@ -7702,6 +8606,11 @@ lodash.orderby@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.orderby/-/lodash.orderby-4.6.0.tgz#e697f04ce5d78522f54d9338b32b81a3393e4eb3" integrity sha1-5pfwTOXXhSL1TZM4syuBozk+TrM= +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + lodash.pick@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" @@ -7742,7 +8651,7 @@ lodash.take@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.take/-/lodash.take-4.1.1.tgz#0b4146dcb7a70c6153495187fc10b12b71fefadf" integrity sha1-C0FG3LenDGFTSVGH/BCxK3H++t8= -lodash.template@^4.0.2: +lodash.template@^4.0.2, lodash.template@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" integrity sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A= @@ -7772,11 +8681,6 @@ lodash.uniq@^4.5.0, lodash.uniq@~4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash.uniqby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" - integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= - lodash.without@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" @@ -7792,7 +8696,7 @@ lodash@4.17.10: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" integrity sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg== -lodash@^4, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: +lodash@^4, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -7876,12 +8780,12 @@ lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lru-cache@4.1.x, lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.2, lru-cache@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.4.tgz#51cc46e8e6d9530771c857e24ccc720ecdbcc031" - integrity sha512-EPstzZ23znHUVLKj+lcXO1KvZkrlw+ZirdwvOmnAnA/1PB4ggyXJ77LRkCqkff+ShQ+cqoxCxLQOh4cKITO5iA== + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== dependencies: pseudomap "^1.0.2" - yallist "^3.0.2" + yallist "^2.1.2" lru-cache@^5.0.0: version "5.1.1" @@ -7907,6 +8811,11 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +make-error@1.x: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + make-fetch-happen@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" @@ -7965,6 +8874,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +marked@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.4.0.tgz#9ad2c2a7a1791f10a852e0112f77b571dce10c66" + integrity sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw== + matcher@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/matcher/-/matcher-1.1.1.tgz#51d8301e138f840982b338b116bb0c09af62c1c2" @@ -8020,7 +8934,7 @@ memory-fs@^0.4.0, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" -meow@^3.3.0: +meow@^3.3.0, meow@^3.6.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= @@ -8051,6 +8965,21 @@ meow@^4.0.0: redent "^2.0.0" trim-newlines "^2.0.0" +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" + integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -8208,9 +9137,9 @@ minipass@^2.2.1, minipass@^2.3.4, minipass@^2.3.5: yallist "^3.0.0" minizlib@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.1.tgz#6734acc045a46e61d596a43bb9d9cd326e19cc42" - integrity sha512-TrfjCjk4jLhcJyGMYymBH6oTXcWjYbUAXTHDbtnWHjZC25h0cdajHuPE1zxb4DVmu8crfh+HwH/WMuyLG0nHBg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== dependencies: minipass "^2.2.1" @@ -8246,7 +9175,7 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -8270,10 +9199,10 @@ moment@2.20.x: resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" integrity sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg== -"moment@>= 2.9.0", moment@^2.11.2, moment@^2.20.0: - version "2.22.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" - integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y= +"moment@>= 2.9.0", moment@>=2.14.0, moment@^2.11.2, moment@^2.20.0: + version "2.23.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.23.0.tgz#759ea491ac97d54bac5ad776996e2a58cc1bc225" + integrity sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA== move-concurrently@^1.0.1: version "1.0.1" @@ -8388,6 +9317,11 @@ neo-async@^2.5.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== +nested-error-stacks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" + integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== + netmask@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" @@ -8447,11 +9381,6 @@ node-fetch@^2.1.2, node-fetch@^2.2.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== -node-fingerprint@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/node-fingerprint/-/node-fingerprint-0.0.2.tgz#31cbabeb71a67ae7dd5a7dc042e51c3c75868501" - integrity sha1-Mcur63GmeufdWn3AQuUcPHWGhQE= - node-forge@^0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" @@ -8555,10 +9484,10 @@ node-pre-gyp@^0.10.0, node-pre-gyp@^0.10.3: semver "^5.3.0" tar "^4" -node-releases@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.4.tgz#2d585de8c6c81d00017e063e7810a63889aa6756" - integrity sha512-GqRV9GcHw8JCRDaP/JoeNMNzEGzHAknMvIHqMb2VeTOmg1Cf9+ej8bkV12tHfzWHQMCkQ5zUFgwFUkfraynNCw== +node-releases@^1.0.5: + version "1.1.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.1.tgz#8fff8aea1cfcad1fb4205f805149054fbf73cafd" + integrity sha512-2UXrBr6gvaebo5TNF84C66qyJJ6r0kxBObgZIDX3D3/mt1ADKiHux3NJPWisq0wxvJJdkjECH+9IIKYViKj71Q== dependencies: semver "^5.3.0" @@ -8660,7 +9589,7 @@ npm-install-checks@~3.0.0: dependencies: semver "^2.3.0 || 3.x || 4 || 5" -npm-lifecycle@^2.0.0: +npm-lifecycle@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/npm-lifecycle/-/npm-lifecycle-2.1.0.tgz#1eda2eedb82db929e3a0c50341ab0aad140ed569" integrity sha512-QbBfLlGBKsktwBZLj6AviHC6Q9Y3R/AY4a2PYSIRhSKSS0/CxRyD/PfxEX6tPeOCXQgMSNdwGeECacstgptc+g== @@ -8674,6 +9603,11 @@ npm-lifecycle@^2.0.0: umask "^1.1.0" which "^1.3.1" +npm-logical-tree@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/npm-logical-tree/-/npm-logical-tree-1.2.1.tgz#44610141ca24664cad35d1e607176193fd8f5b88" + integrity sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg== + "npm-package-arg@^3.0.0 || ^4.0.0", npm-package-arg@^4.1.1, npm-package-arg@~4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-4.2.1.tgz#593303fdea85f7c422775f17f9eb7670f680e3ec" @@ -8692,7 +9626,7 @@ npm-lifecycle@^2.0.0: semver "^5.1.0" validate-npm-package-name "^3.0.0" -"npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: +"npm-package-arg@^4.0.0 || ^5.0.0 || ^6.0.0", "npm-package-arg@^5.1.2 || 6", npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.0.tgz#15ae1e2758a5027efb4c250554b85a737db7fcc1" integrity sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA== @@ -8726,6 +9660,15 @@ npm-pick-manifest@^2.2.3: npm-package-arg "^6.0.0" semver "^5.4.1" +npm-profile@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-profile/-/npm-profile-4.0.1.tgz#d350f7a5e6b60691c7168fbb8392c3603583f5aa" + integrity sha512-NQ1I/1Q7YRtHZXkcuU1/IyHeLy6pd+ScKg4+DQHdfsm769TGq6HPrkbuNJVJS4zwE+0mvvmeULzQdWn2L2EsVA== + dependencies: + aproba "^1.1.2 || 2" + figgy-pudding "^3.4.1" + npm-registry-fetch "^3.8.0" + npm-registry-client@~7.2.1: version "7.2.1" resolved "https://registry.yarnpkg.com/npm-registry-client/-/npm-registry-client-7.2.1.tgz#c792266b088cc313f8525e7e35248626c723db75" @@ -8930,7 +9873,7 @@ object-hash@^1.3.0: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== -object-keys@^1.0.11, object-keys@^1.0.12: +object-keys@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" integrity sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag== @@ -8947,26 +9890,6 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.entries@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f" - integrity sha1-G/mk3SKI9bM/Opk9JXZh8F0WGl8= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.6.1" - function-bind "^1.1.0" - has "^1.0.1" - object.getownpropertydescriptors@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" @@ -9043,7 +9966,7 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.1, optionator@^0.8.2: +optionator@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= @@ -9118,11 +10041,6 @@ otplib@^10.0.1: dependencies: thirty-two "1.0.2" -ow@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/ow/-/ow-0.8.0.tgz#d360d779c996f4132941a596c87f86ce8e812e62" - integrity sha512-hYgYZNcRfIZ2JppSTqh6mxdU1zkUXsGlwy4eBsRG91R6CiZk7cB+AfHl+SVKBdynQvAnNHNfu0ZrtJN1jj7Mow== - p-any@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-any/-/p-any-1.1.0.tgz#1d03835c7eed1e34b8e539c47b7b60d0d015d4e1" @@ -9276,7 +10194,7 @@ packet-reader@0.3.1: resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-0.3.1.tgz#cd62e60af8d7fea8a705ec4ff990871c46871f27" integrity sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc= -pacote@^9.1.0: +pacote@^9.2.3: version "9.2.3" resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.2.3.tgz#48cfe87beb9177acd6594355a584a538835424b3" integrity sha512-Y3+yY3nBRAxMlZWvr62XLJxOwCmG9UmkGZkFurWHoCjqF0cZL72cTOCRJTvWw8T4OhJS2RTg13x4oYYriauvEw== @@ -9310,9 +10228,9 @@ pacote@^9.1.0: which "^1.3.1" pako@~1.0.2, pako@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" - integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== + version "1.0.7" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.7.tgz#2473439021b57f1516c82f58be7275ad8ef1bb27" + integrity sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ== parallel-transform@^1.1.0: version "1.1.0" @@ -9384,6 +10302,14 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= +password-prompt@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.0.7.tgz#8e27748d3400bc9c9140d5ade705dfb7aeb7d91a" + integrity sha1-jid0jTQAvJyRQNWt5wXft6632Ro= + dependencies: + ansi-escapes "^3.1.0" + cross-spawn "^6.0.5" + path-array@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-array/-/path-array-1.0.1.tgz#7e2f0f35f07a2015122b868b7eac0eb2c4fec271" @@ -9447,13 +10373,6 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= - dependencies: - pify "^2.0.0" - path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -9511,18 +10430,18 @@ pg-minify@0.5.5: resolved "https://registry.yarnpkg.com/pg-minify/-/pg-minify-0.5.5.tgz#6c961a61aa19f469d8edfe5a3c0da58760f3c339" integrity sha512-7Pf9h6nV1RFqED1hkRosePqvpPwNUUtW06TT4+lHwzesxa5gffxkShTjYH6JXV5sSSfh5+2yHOTTWEkCyCQ0Eg== -pg-pool@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.4.tgz#05ad0f2d9437d89c94ccc4f4d0a44ac65ade865b" - integrity sha512-Mi2AsmlFkVMpI28NreaDkz5DkfxLOG16C/HNwi091LDlOiDiQACtAroLxSd1vIS2imBqxdjjO9cQZg2CwsOPbw== +pg-pool@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-2.0.5.tgz#f00556fab23f1bbb14b0650ba8d0e447434a1b04" + integrity sha512-T4W9qzP2LjItXuXbW6jgAF2AY0jHp6IoTxRhM3GB7yzfBxzDnA3GCm0sAduzmmiCybMqD0+V1HiqIG5X2YWqlQ== -pg-promise@^8.5.2: - version "8.5.2" - resolved "https://registry.yarnpkg.com/pg-promise/-/pg-promise-8.5.2.tgz#ea78fb7989ce05427cd064079e5539384b8e3654" - integrity sha512-7vMkZcKR9byVb9p4T/Hwsy3BliOAD7ieFcizd1eMeXhWxoU8DgF+MtQIpNLTD44dF6ojR1NYMgTnYDh1qSPgsQ== +pg-promise@^8.5.4: + version "8.5.4" + resolved "https://registry.yarnpkg.com/pg-promise/-/pg-promise-8.5.4.tgz#3c1c656d340b1d8e21bbd8017a650c795b375aca" + integrity sha512-GjGEL5WuRJOjTdTiI/v8x4SUd+nLh/fnAgKa8GvMi6xNKLaxvXOC6PiVj53n/oppUN6QjewGh06RBod4VrXhGQ== dependencies: manakin "0.5.2" - pg "7.6.1" + pg "7.7.1" pg-minify "0.5.5" spex "2.1.0" @@ -9543,15 +10462,15 @@ pg-types@~1.12.1: postgres-date "~1.0.0" postgres-interval "^1.1.0" -pg@7.6.1: - version "7.6.1" - resolved "https://registry.yarnpkg.com/pg/-/pg-7.6.1.tgz#42c68aed37bf38b813616e3d21f4338f350c1b79" - integrity sha512-rAItIkYrRaNGinZN/Hs8F9R5mQjQSPlnzxPF+eCimSl92qnuNGR42gkpOQKP1bnvTwkSjRTBL+VNC5EcFhtCuQ== +pg@7.7.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-7.7.1.tgz#546b192ff484322b69689391f885de3ba91a30d4" + integrity sha512-p3I0mXOmUvCoVlCMFW6iYSrnguPol6q8He15NGgSIdM3sPGjFc+8JGCeKclw8ZR4ETd+Jxy2KNiaPUcocHZeMw== dependencies: buffer-writer "2.0.0" packet-reader "0.3.1" pg-connection-string "0.1.3" - pg-pool "~2.0.3" + pg-pool "^2.0.4" pg-types "~1.12.1" pgpass "1.x" semver "4.3.2" @@ -9585,15 +10504,16 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pino-pretty@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-2.3.0.tgz#b412d216f6261e354d69f5b1b12f22447fc45f05" - integrity sha512-Fj5L0hxYvBF0QfszXpaIdUHfedKGUmDP7hhQBdXjLlQLqphrRDUXRwEVYdonXaQpd5JZb22gjTHVMR8roIDBVA== +pino-pretty@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-2.5.0.tgz#fade5b6d2acbdbf2c7e77adf220e7b7d89d04437" + integrity sha512-odR4SKdyubhe4aFts0/mBau2/mJLG23Ghyo86a+GZ2/Cev3CRr5nYv2+82V7v1hQL93yRSO004ASrrF7278TNQ== dependencies: args "^5.0.0" chalk "^2.3.2" dateformat "^3.0.3" fast-json-parse "^1.0.3" + fast-safe-stringify "^2.0.6" jmespath "^0.15.0" pump "^3.0.0" readable-stream "^3.0.6" @@ -9604,26 +10524,19 @@ pino-std-serializers@^2.3.0: resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-2.3.0.tgz#34eeaab97c055c28e22c0542ae55978e7e427786" integrity sha512-klfGoOsP6sJH7ON796G4xoUSx2fkpFgKHO4YVVO2zmz31jR+etzc/QzGJILaOIiCD6HTCFgkPx+XN8nk+ruqPw== -pino@^5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-5.9.0.tgz#4a436289ea90cefd0fad1e3ca5726b6d58922c71" - integrity sha512-6sHy38gWsZbrmYq6vk343VCThy93ZdVfmLsHDVzbl/j621SjSaxCcS/ySmxK/hRmq8jpQb3n44dNRIeqbbQw6A== +pino@^5.10.1: + version "5.10.1" + resolved "https://registry.yarnpkg.com/pino/-/pino-5.10.1.tgz#24831a60681622f96f16df723b48eb6802fcd993" + integrity sha512-a2pXIoYfEpQaGKR87/iY97jRjAqSVCyULyNsYJLTXCXbout4qGe2tvAz9jHlm1kMVwOw3IYVZ2baKP8GvLMeiw== dependencies: fast-json-parse "^1.0.3" - fast-redact "^1.4.0" + fast-redact "^1.4.2" fast-safe-stringify "^2.0.6" - flatstr "^1.0.5" + flatstr "^1.0.9" pino-std-serializers "^2.3.0" pump "^3.0.0" quick-format-unescaped "^3.0.0" - sonic-boom "^0.6.3" - -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q= - dependencies: - find-up "^1.0.0" + sonic-boom "^0.7.1" pkg-dir@^2.0.0: version "2.0.0" @@ -9639,6 +10552,11 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +platform@^1.3.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.5.tgz#fb6958c696e07e2918d2eeda0f0bc9448d733444" + integrity sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q== + please-upgrade-node@^3.0.2, please-upgrade-node@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz#ed320051dfcc5024fae696712c8288993595e8ac" @@ -9716,10 +10634,10 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= -prettier@^1.15.2: - version "1.15.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.2.tgz#d31abe22afa4351efa14c7f8b94b58bb7452205e" - integrity sha512-YgPLFFA0CdKL4Eg2IHtUSjzj/BWgszDHiNQAe0VAIBse34148whfdzLagRL+QiKS+YfK5ftB6X4v/MBw8yCoug== +prettier@^1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.15.3.tgz#1feaac5bdd181237b54dbe65d874e02a1472786a" + integrity sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg== pretty-format@^22.4.3: version "22.4.3" @@ -9765,9 +10683,9 @@ process@^0.11.1, process@^0.11.10: integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= progress@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.1.tgz#c9242169342b1c29d275889c95734621b1952e31" - integrity sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg== + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== promise-inflight@^1.0.1: version "1.0.1" @@ -9782,7 +10700,7 @@ promise-retry@^1.1.1: err-code "^1.0.0" retry "^0.10.0" -promise@7.x, "promise@>=3.2 <8": +"promise@>=3.2 <8": version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== @@ -9797,6 +10715,14 @@ prompts@^0.1.9: kleur "^2.0.1" sisteransi "^0.1.1" +prompts@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.0.1.tgz#201b3718b4276fb407f037db48c0029d6465245c" + integrity sha512-8lnEOSIGQbgbnO47+13S+H204L8ISogGulyi0/NNEFAQ9D1VMNTrJ9SBX2Ra03V4iPn/zt36HQMndRYkaPoWiQ== + dependencies: + kleur "^3.0.0" + sisteransi "^1.0.0" + promzard@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" @@ -9872,7 +10798,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24: +psl@^1.1.24, psl@^1.1.28: version "1.1.29" resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" integrity sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ== @@ -9928,7 +10854,7 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@2.x.x, punycode@^2.1.0: +punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== @@ -10080,7 +11006,7 @@ read-installed@~4.0.3: optionalDependencies: graceful-fs "^4.1.2" -"read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@~2.0.4: +"read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@^2.0.13, read-package-json@~2.0.4: version "2.0.13" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.13.tgz#2e82ebd9f613baa6d2ebe3aa72cefe3f68e41f4a" integrity sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg== @@ -10122,14 +11048,6 @@ read-pkg-up@^1.0.1: find-up "^1.0.0" read-pkg "^1.0.0" -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - read-pkg-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" @@ -10147,15 +11065,6 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -10272,6 +11181,13 @@ realpath-native@^1.0.0: dependencies: util.promisify "^1.0.0" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= + dependencies: + resolve "^1.1.6" + recursive-readdir@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" @@ -10295,6 +11211,13 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +redeyed@~2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" + integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= + dependencies: + esprima "~4.0.0" + regenerate-unicode-properties@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c" @@ -10312,11 +11235,6 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.12.0: - version "0.12.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" - integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== - regenerator-runtime@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.1.tgz#522ea2aafd9200a00eee143dc14219a35a0f3991" @@ -10344,20 +11262,15 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexpp@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" - integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== - regexpu-core@^4.1.3, regexpu-core@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d" - integrity sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw== + version "4.4.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.4.0.tgz#8d43e0d1266883969720345e70c275ee0aec0d32" + integrity sha512-eDDWElbwwI3K0Lo6CqbQbA6FwgtCz4kYTarrri1okfkRLZAqstU+B3voZBCjg8Fl6iq0gXrJG6MvRgLthfvgOA== dependencies: regenerate "^1.4.0" regenerate-unicode-properties "^7.0.0" - regjsgen "^0.4.0" - regjsparser "^0.3.0" + regjsgen "^0.5.0" + regjsparser "^0.6.0" unicode-match-property-ecmascript "^1.0.4" unicode-match-property-value-ecmascript "^1.0.2" @@ -10376,15 +11289,15 @@ registry-url@^3.0.3: dependencies: rc "^1.0.1" -regjsgen@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.4.0.tgz#c1eb4c89a209263f8717c782591523913ede2561" - integrity sha512-X51Lte1gCYUdlwhF28+2YMO0U6WeN0GLpgpA7LK7mbdDnkQYiwvEpmpe0F/cv5L14EbxgrdayAG3JETBv0dbXA== +regjsgen@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" + integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== -regjsparser@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.3.0.tgz#3c326da7fcfd69fa0d332575a41c8c0cdf588c96" - integrity sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA== +regjsparser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" + integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== dependencies: jsesc "~0.5.0" @@ -10436,7 +11349,7 @@ request-promise@^4.2.2: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@2, request@^2.74.0, request@^2.81.0, request@^2.87.0: +request@2, request@^2.74.0, request@^2.87.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -10504,14 +11417,6 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -10524,11 +11429,6 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= - resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -10549,7 +11449,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.3.2, resolve@^1.5.0, resolve@^1.6.0, resolve@^1.8.1: +resolve@1.x, resolve@^1.1.6, resolve@^1.3.2: version "1.8.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== @@ -10594,7 +11494,7 @@ retry@^0.10.0, retry@~0.10.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== @@ -10719,11 +11619,6 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -scryptsy@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.0.0.tgz#262c36f0231cfa7654e2363fa394cd2dec66f378" - integrity sha1-Jiw28CMc+nZU4jY/o5TNLexm83g= - secp256k1@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.5.2.tgz#f95f952057310722184fe9c914e6b71281f2f2ae" @@ -10760,7 +11655,7 @@ semver-utils@^1.1.1: resolved "https://registry.yarnpkg.com/semver-utils/-/semver-utils-1.1.4.tgz#cf0405e669a57488913909fc1c3f29bf2a4871e2" integrity sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA== -"semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: +"semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== @@ -10799,10 +11694,10 @@ send@0.16.2: range-parser "~1.2.0" statuses "~1.4.0" -sequelize@^4.41.2: - version "4.41.2" - resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.41.2.tgz#bb9ba30d72e9eeb883c9861cd0e2cac672010883" - integrity sha512-8vPf2R0o9iEmtzkqNzwFdblO+0Mu+RNxOdLeYGGqWGlp3cushLpQucAeSGPQgf2hQVZP5yOCM1ouZKTQ5FTlvA== +sequelize@^4.42.0: + version "4.42.0" + resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.42.0.tgz#439467ba7bfe7d5afcc56d62b3e091860fbf18f3" + integrity sha512-qxAYnX4rcv7PbNtEidb56REpxNJCdbN0qyk1jb3+e6sE7OrmS0nYMU+MFVbNTJtZfSpOEEL1TX0TkMw+wzZBxg== dependencies: bluebird "^3.5.0" cls-bluebird "^2.1.0" @@ -10910,6 +11805,15 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shelljs@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" + integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -10952,6 +11856,11 @@ sisteransi@^0.1.1: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" integrity sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g== +sisteransi@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" + integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ== + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -10967,13 +11876,6 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" - integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== - dependencies: - is-fullwidth-code-point "^2.0.0" - sliced@0.0.x: version "0.0.5" resolved "https://registry.yarnpkg.com/sliced/-/sliced-0.0.5.tgz#5edc044ca4eb6f7816d50ba2fc63e25d8fe4707f" @@ -11050,13 +11952,14 @@ snyk-config@2.2.0: lodash "^4.17.5" nconf "^0.10.0" -snyk-docker-plugin@1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-1.13.1.tgz#4d5ad62fe76b03e36b2c414b9576e67daef21f73" - integrity sha512-rhVPwMryfGanLXeDoDzjQabGq8VlEPSkvDvraiOhm/F9o5E4zam6vDlVQXsYVRb4ydVKPLgux2ejWyFiG6shFA== +snyk-docker-plugin@1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/snyk-docker-plugin/-/snyk-docker-plugin-1.14.0.tgz#4f79a102908450bf32ff6db8391a72897d3eb8f1" + integrity sha512-bmhKHGwm0CIVOJD98uIuqGACbsuqjlwnj/Ybek+nlZowLqHyjhOUYQISPzjIwWbF1wpYf4Av6MzbCx9HsNPFIg== dependencies: debug "^3" dockerfile-ast "0.0.12" + semver "^5.6.0" tslib "^1" snyk-go-plugin@1.6.1: @@ -11068,10 +11971,10 @@ snyk-go-plugin@1.6.1: tmp "0.0.33" toml "^2.3.2" -snyk-gradle-plugin@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-2.1.1.tgz#661591014508fdd1cbe5b91f4f8e6af50f68a9ac" - integrity sha512-aFeVC5y3XkJ5BxknHhtYo76as3xJbzSQlXACGZrQZGQ/w/UhNdM8VI1QB6Eq4uEzexleB/hcJwYxNmhI2CNCeA== +snyk-gradle-plugin@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/snyk-gradle-plugin/-/snyk-gradle-plugin-2.1.2.tgz#d680ba37204edbff726485abda408ba8d13b67a6" + integrity sha512-GswT6b8Pvq4XtUXYFPBqQoxoeRG/WVRKhy2HcgfJbd3MStKyO4fwn14InlEAP7PQDLqGZ5hhvWwsNCT/d69lRA== dependencies: clone-deep "^0.3.0" @@ -11083,10 +11986,10 @@ snyk-module@1.9.1, snyk-module@^1.6.0, snyk-module@^1.9.1: debug "^3.1.0" hosted-git-info "^2.7.1" -snyk-mvn-plugin@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.0.0.tgz#875dcfe0d77b50396321552f2469ee69ca8d1416" - integrity sha512-9jAhZhv+7YcqtoQYCYlgMoxK+dWBKlk+wkX27Ebg3vNddNop9q5jZitRXTjsXwfSUZHRt+Ptw1f8vei9kjzZVg== +snyk-mvn-plugin@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/snyk-mvn-plugin/-/snyk-mvn-plugin-2.0.1.tgz#c52d5320e34ff3ab6d50d8b9549a4b32243ed6ae" + integrity sha512-TBrdcFXHdYuRYFCvpyUeFC+mCi6SOV3vdxgHrP7JRNnJwO8PYaKCObLJyhpRWa8IaHv/8CjJTmnEbWIh7BPHAA== snyk-nodejs-lockfile-parser@1.9.0: version "1.9.0" @@ -11134,10 +12037,10 @@ snyk-policy@1.13.1: snyk-try-require "^1.3.1" then-fs "^2.0.0" -snyk-python-plugin@1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/snyk-python-plugin/-/snyk-python-plugin-1.9.0.tgz#2f444f9377880181c1fdbed6ab2890687fe10c99" - integrity sha512-zlyOHoCpmyVym9AwkboeepzEGrY3gHsM7eWP/nJ85TgCnQO5H5orKm3RL57PNbWRY+BnDmoQQ+udQgjym2+3sg== +snyk-python-plugin@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/snyk-python-plugin/-/snyk-python-plugin-1.9.1.tgz#f8f7897f7068c434560cf43200d306fe91cf806d" + integrity sha512-4R040DBK77NSfSy3rCndmrv85YlLrKZU1ct59oZSoGb1PYdCi8kXRuq50UpSgasp6YR0yJxT22T38hNOAjTtVw== dependencies: tmp "0.0.33" @@ -11170,10 +12073,10 @@ snyk-resolve@1.0.1, snyk-resolve@^1.0.0, snyk-resolve@^1.0.1: debug "^3.1.0" then-fs "^2.0.0" -snyk-sbt-plugin@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/snyk-sbt-plugin/-/snyk-sbt-plugin-2.0.0.tgz#d7fa18bee77ecb045ecc7feb8915f83b75186582" - integrity sha512-bOUqsQ1Lysnwfnvf4QQIBfC0M0ZVuhlshTKd7pNwgAJ41YEPJNrPEpzOePl/HfKtwilEEwHh5YHvjYGegEKx0A== +snyk-sbt-plugin@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/snyk-sbt-plugin/-/snyk-sbt-plugin-2.0.1.tgz#f302dc5f265b3f8b8ee5b968c7d0ef36a65d6f65" + integrity sha512-AsGGMP0W3mlKygXUI5jjt54qWFttZEXT1A40+u21p8rZPXLZprwnd+QH9pZDd04d9W9aofGvON8NJeOn9KS39Q== snyk-tree@^1.0.0: version "1.0.0" @@ -11192,10 +12095,10 @@ snyk-try-require@1.3.1, snyk-try-require@^1.1.1, snyk-try-require@^1.3.1: lru-cache "^4.0.0" then-fs "^2.0.0" -snyk@^1.116.0: - version "1.116.2" - resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.116.2.tgz#d82fa2090bc25f892708a3623fc0e1c2383f53cc" - integrity sha512-zkW+IjSEDJ5f4leXck7a7aF36pJcIKRk3o2or78cnabq1mxQzgY8+ooECPDBnwvqySIwUKA8jOjnGRujaNCMpg== +snyk@^1.118.0: + version "1.118.0" + resolved "https://registry.yarnpkg.com/snyk/-/snyk-1.118.0.tgz#837df3d493a7bb03a1900fc027a575c6fa6ce301" + integrity sha512-5EtVdyDqbBF25Y9hu/jmKSnFVO8UK1vDuGejur1kJ2UE4ANxxuTQh+kTIPvx0FNoEJys/jpRfF7/QRxaxesK0g== dependencies: "@snyk/dep-graph" "1.1.2" "@snyk/gemfile" "1.1.0" @@ -11215,19 +12118,19 @@ snyk@^1.116.0: recursive-readdir "^2.2.2" semver "^5.5.0" snyk-config "2.2.0" - snyk-docker-plugin "1.13.1" + snyk-docker-plugin "1.14.0" snyk-go-plugin "1.6.1" - snyk-gradle-plugin "2.1.1" + snyk-gradle-plugin "2.1.2" snyk-module "1.9.1" - snyk-mvn-plugin "2.0.0" + snyk-mvn-plugin "2.0.1" snyk-nodejs-lockfile-parser "1.9.0" snyk-nuget-plugin "1.6.5" snyk-php-plugin "1.5.1" snyk-policy "1.13.1" - snyk-python-plugin "1.9.0" + snyk-python-plugin "1.9.1" snyk-resolve "1.0.1" snyk-resolve-deps "4.0.2" - snyk-sbt-plugin "2.0.0" + snyk-sbt-plugin "2.0.1" snyk-tree "^1.0.0" snyk-try-require "1.3.1" source-map-support "^0.5.9" @@ -11277,12 +12180,12 @@ somever@2.x.x: bounce "1.x.x" hoek "6.x.x" -sonic-boom@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-0.6.3.tgz#a02f9fc8d5ec42166e28c39760cc833552056595" - integrity sha512-TMhj6kDJk9LLzCTTL8+HPCfFn4MwkE4P6k2Up89Rz949+DSRw90V62upRKC99rJEOmu4E9ljH5Otu2JSRmx+bg== +sonic-boom@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-0.7.1.tgz#d6aae30428802dbd43b1167643efe1ef708bb534" + integrity sha512-zveqTNcDTI35ae0LgK/SwHkFeMfe6FJKyeACgPzuOe3f+9CyaoXJHTnDgekXt8PKIoMCGF/m57/9RNjjkqmt2A== dependencies: - flatstr "^1.0.8" + flatstr "^1.0.9" sort-keys@^2.0.0: version "2.0.0" @@ -11348,9 +12251,9 @@ spawn-please@^0.3.0: integrity sha1-2zOOxM/2Orxp8dDgjO6euL69nRE= spdx-correct@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.2.tgz#19bb409e91b47b1ad54159243f7312a858db3c2e" - integrity sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ== + version "3.1.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4" + integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -11393,9 +12296,9 @@ split2@^2.0.0: through2 "^2.0.2" split2@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/split2/-/split2-3.0.0.tgz#55057cd560687a7ef6464471597404577ff1735d" - integrity sha512-Cp7G+nUfKJyHCrAI8kze3Q00PFGEG1pMgrAlTFlDbn+GW24evSZHJuMl+iUJx1w/NTRDeBiTgvwnf6YOt94FMw== + version "3.1.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-3.1.0.tgz#064bbfac70cdb66f77827870d42f159a8b442201" + integrity sha512-ePE1otNQVMnBRyqf3INbZvZwBPGsdBDThgrOWZ6z8zXGNVQNVCSEoOO9aBMTzDN1mXoNSZJ2kHSFH7AA5SPWww== dependencies: readable-stream "^3.0.0" @@ -11459,7 +12362,14 @@ ssri@^6.0.0, ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -stack-trace@0.0.10, stack-trace@0.0.x, stack-trace@~0.0.9: +stack-generator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.3.tgz#bb74385c67ffc4ccf3c4dee5831832d4e509c8a0" + integrity sha512-kdzGoqrnqsMxOEuXsXyQTmvWXZmG0f3Ql2GDx5NtmZs59sT2Bt9Vdyq0XdtxUi58q/+nxtbF9KOQ9HkV1QznGg== + dependencies: + stackframe "^1.0.4" + +stack-trace@0.0.10, stack-trace@0.0.x: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= @@ -11469,6 +12379,11 @@ stack-utils@^1.0.1: resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== +stackframe@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.4.tgz#357b24a992f9427cba6b545d96a14ed2cbca187b" + integrity sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw== + staged-git-files@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-1.1.2.tgz#4326d33886dc9ecfa29a6193bf511ba90a46454b" @@ -11494,7 +12409,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.4.0 < 2": +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -11576,10 +12491,19 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== +string-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.0.0.tgz#5a1690a57cc78211fffd9bf24bbe24d090604eb1" + integrity sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.0.0" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== dependencies: safe-buffer "~5.1.0" @@ -11588,6 +12512,13 @@ string_decoder@~0.10.x: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + stringify-object@^3.2.2: version "3.3.0" resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" @@ -11597,6 +12528,11 @@ stringify-object@^3.2.2: is-obj "^1.0.1" is-regexp "^1.0.0" +stringify-package@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.0.tgz#e02828089333d7d45cd8c287c30aa9a13375081b" + integrity sha512-JIQqiWmLiEozOC0b0BtxZ/AOUtdUZHCBPgqIZ2kSJJqGwgb9neo44XdTHUC4HZSGqi03hOeB7W/E8rAlKnGe9g== + stringstream@~0.0.4: version "0.0.6" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" @@ -11616,6 +12552,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" + integrity sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow== + dependencies: + ansi-regex "^4.0.0" + strip-bom@3.0.0, strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -11645,7 +12588,7 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= -strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: +strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -11701,13 +12644,21 @@ supports-color@^3.1.2: dependencies: has-flag "^1.0.0" -supports-color@^5.3.0, supports-color@^5.5.0: +supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" +supports-hyperlinks@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz#71daedf36cc1060ac5100c351bb3da48c29c0ef7" + integrity sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw== + dependencies: + has-flag "^2.0.0" + supports-color "^5.0.0" + symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" @@ -11718,20 +12669,10 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= -table@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/table/-/table-5.1.0.tgz#69a54644f6f01ad1628f8178715b408dc6bf11f7" - integrity sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg== - dependencies: - ajv "^6.5.3" - lodash "^4.17.10" - slice-ansi "1.0.0" - string-width "^2.1.1" - tapable@^1.0.0, tapable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.0.tgz#0d076a172e3d9ba088fd2272b2668fb8d194b78c" - integrity sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.1.tgz#4d297923c5a72a42360de2ab52dadfaaec00018e" + integrity sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA== tar@^2.0.0, tar@~2.2.1: version "2.2.1" @@ -11822,9 +12763,9 @@ terser-webpack-plugin@^1.1.0: worker-farm "^1.5.2" terser@^3.8.1: - version "3.10.12" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.10.12.tgz#06d40765e40b33fd97977c0896c75b2b5d42142d" - integrity sha512-3ODPC1eVt25EVNb04s/PkHxOmzKBQUF6bwwuR6h2DbEF8/j265Y1UkwNtOk9am/pRxfJ5HPapOlUlO6c16mKQQ== + version "3.11.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.11.0.tgz#60782893e1f4d6788acc696351f40636d0e37af0" + integrity sha512-5iLMdhEPIq3zFWskpmbzmKwMQixKmTYwY3Ox9pjtSklBLnHiuQ0GKJLhL1HSYtyffHM3/lDIFBnb82m9D7ewwQ== dependencies: commander "~2.17.1" source-map "~0.6.1" @@ -11851,7 +12792,7 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== -text-table@^0.2.0, text-table@~0.2.0: +text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= @@ -11904,16 +12845,16 @@ timers-browserify@^2.0.4: setimmediate "^1.0.4" timezone-support@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/timezone-support/-/timezone-support-1.8.0.tgz#b0498940df10c2c950c3f1fc65a291757aeb6d90" - integrity sha512-eGoH5aDwLOqgTzCRzpBJMuGgqEMn7SJz7wbHf8T8ADwxH9t24HT8aLIw+WksPIFe7Ou561cx9GEsBa6vuSEf7g== + version "1.8.1" + resolved "https://registry.yarnpkg.com/timezone-support/-/timezone-support-1.8.1.tgz#0a8c04d0614be6fccdd2280aaad5072fa5d31e5f" + integrity sha512-+pKzxoUe4PZXaQcswceJlA+69oRyyu1uivnYKdpsC7eGzZiuvTLbU4WYPqTKslEsoSvjN8k/u/6qNfGikBB/wA== dependencies: commander "2.19.0" -tiny-glob@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.3.tgz#9dba7fe22b1e4e2e7bb59815b10152bdf3d085b4" - integrity sha512-pkCxlClaL6MznxrgW4D2VDk5w6hz3SEOtXcJ9CPhxLjhpsvTd1g3qkMAqxoJNjKsCQlJj6w485ij5d5/Fb9jUQ== +tiny-glob@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.6.tgz#9e056e169d9788fe8a734dfa1ff02e9b92ed7eda" + integrity sha512-A7ewMqPu1B5PWwC3m7KVgAu96Ch5LA0w4SnEN/LbDREj/gAD0nPWboRbn8YoP9ISZXqeNAlMvKSKoEuhcfK3Pw== dependencies: globalyzer "^0.1.0" globrex "^0.1.1" @@ -11986,6 +12927,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + toml@^2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/toml/-/toml-2.3.3.tgz#8d683d729577cb286231dfc7a8affe58d31728fb" @@ -12010,13 +12956,13 @@ toposort-class@^1.0.1: resolved "https://registry.yarnpkg.com/toposort-class/-/toposort-class-1.0.1.tgz#7ffd1f78c8be28c3ba45cd4e1a3f5ee193bd9988" integrity sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg= -tough-cookie@>=2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== +tough-cookie@>=2.3.3, tough-cookie@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== dependencies: - psl "^1.1.24" - punycode "^1.4.1" + psl "^1.1.28" + punycode "^2.1.1" tough-cookie@~2.3.0: version "2.3.4" @@ -12025,6 +12971,14 @@ tough-cookie@~2.3.0: dependencies: punycode "^1.4.1" +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + tr46@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" @@ -12032,6 +12986,11 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== + trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -12057,11 +13016,67 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -tslib@^1, tslib@^1.9.0, tslib@^1.9.3: +ts-jest@^23.10.5: + version "23.10.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.10.5.tgz#cdb550df4466a30489bf70ba867615799f388dd5" + integrity sha512-MRCs9qnGoyKgFc8adDEntAOP64fWK1vZKnOYU1o2HxaqjdJvGqmkLCPCnVq1/If4zkUmEjKPnCiUisTrlX2p2A== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + json5 "2.x" + make-error "1.x" + mkdirp "0.x" + resolve "1.x" + semver "^5.5" + yargs-parser "10.x" + +ts-loader@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.3.1.tgz#70614c8ec4354a9c8b89c9f97b2becb7a98a3980" + integrity sha512-fDDgpBH3SR8xlt2MasLdz3Yy611PQ/UY/KGyo7TgXhTRU/6sS8uGG0nJYnU1OdFBNKcoYbId1UTNaAOUn+i41g== + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + loader-utils "^1.0.2" + micromatch "^3.1.4" + semver "^5.0.1" + +tslib@^1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslint-config-prettier@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.17.0.tgz#946ed6117f98f3659a65848279156d87628c33dc" + integrity sha512-NKWNkThwqE4Snn4Cm6SZB7lV5RMDDFsBwz6fWUkTxOKGjMx8ycOHnjIbhn7dZd5XmssW3CwqUjlANR6EhP9YQw== + +tslint@^5.12.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.12.0.tgz#47f2dba291ed3d580752d109866fb640768fca36" + integrity sha512-CKEcH1MHUBhoV43SA/Jmy1l24HJJgI0eyLbBNSRyFlsQvb9v6Zdq+Nz2vEOH00nC5SUx4SneJ59PZUS/ARcokQ== + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" @@ -12100,21 +13115,59 @@ type-is@~1.6.16: mime-types "~2.1.18" typechecker@^4.0.1, typechecker@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-4.6.0.tgz#d245d9c2df21147d5e2a942fff170b68ece73c87" - integrity sha512-83OrXpyP3LNr7aRbLkt2nkjE/d7q8su8/uRvrKxCpswqVCVGOgyaKpaz8/MTjQqBYe4eLNuJ44pNakFZKqyPMA== + version "4.7.0" + resolved "https://registry.yarnpkg.com/typechecker/-/typechecker-4.7.0.tgz#5249f427358f45b7250c4924fd4d01ed9ba435e9" + integrity sha512-4LHc1KMNJ6NDGO+dSM/yNfZQRtp8NN7psYrPHUblD62Dvkwsp3VShsbM78kOgpcmMkRTgvwdKOTjctS+uMllgQ== dependencies: - editions "^2.0.2" + editions "^2.1.0" typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= +typedoc-default-themes@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz#6dc2433e78ed8bea8e887a3acde2f31785bd6227" + integrity sha1-bcJDPnjti+qOiHo6zeLzF4W9Yic= + +typedoc@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.13.0.tgz#9efdf352bd54873955cd161bd4b75f24a8c59dde" + integrity sha512-jQWtvPcV+0fiLZAXFEe70v5gqjDO6pJYJz4mlTtmGJeW2KRoIU/BEfktma6Uj8Xii7UakuZjbxFewl3UYOkU/w== + dependencies: + "@types/fs-extra" "^5.0.3" + "@types/handlebars" "^4.0.38" + "@types/highlight.js" "^9.12.3" + "@types/lodash" "^4.14.110" + "@types/marked" "^0.4.0" + "@types/minimatch" "3.0.3" + "@types/shelljs" "^0.8.0" + fs-extra "^7.0.0" + handlebars "^4.0.6" + highlight.js "^9.0.0" + lodash "^4.17.10" + marked "^0.4.0" + minimatch "^3.0.0" + progress "^2.0.0" + shelljs "^0.8.2" + typedoc-default-themes "^0.5.0" + typescript "3.1.x" + typeforce@^1.11.5: - version "1.16.0" - resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.16.0.tgz#060f871420f4ed90d411e0606bebc62a0889ad55" - integrity sha512-V60F7OHPH7vPlgIU73vYyeebKxWjQqCTlge+MvKlVn09PIhCOi/ZotowYdgREHB5S1dyHOr906ui6NheYXjlVQ== + version "1.18.0" + resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" + integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== + +typescript@3.1.x: + version "3.1.6" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.1.6.tgz#b6543a83cfc8c2befb3f4c8fba6896f5b0c9be68" + integrity sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA== + +typescript@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5" + integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg== uglify-js@^3.1.4: version "3.4.9" @@ -12248,7 +13301,7 @@ upath@^1.0.5: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== -update-notifier@^2.2.0, update-notifier@^2.5.0: +update-notifier@^2.1.0, update-notifier@^2.2.0, update-notifier@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.5.0.tgz#d0744593e13f161e406acb1d9408b72cad08aff6" integrity sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw== @@ -12424,10 +13477,10 @@ vise@3.x.x: dependencies: hoek "6.x.x" -vision@^5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/vision/-/vision-5.4.3.tgz#648663667f22dc6da17a00c049c2c1a97366d72f" - integrity sha512-4uVrdAKSjNF3WVf1Mjq//SfMclUgpHJtk0EBESyLFcZmrqJjUQ08DyCsRYqw31qEFK3bVm8UO9BlfodRwuyETg== +vision@^5.4.4: + version "5.4.4" + resolved "https://registry.yarnpkg.com/vision/-/vision-5.4.4.tgz#981b2d811a6061cc14cf2d5d05ad3bbc3ee59572" + integrity sha512-jFeH7pU/ODYmTOpY5jutMKU/fDr+P621WYEnWgqwDikxutBWJ+koxlgGnkZQoKY6JlYdY4Awo+rPN3DNdTeDKg== dependencies: boom "7.x.x" hoek "6.x.x" @@ -12505,10 +13558,10 @@ webpack-cli@^3.1.2: v8-compile-cache "^2.0.2" yargs "^12.0.2" -webpack-merge@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.4.tgz#0fde38eabf2d5fd85251c24a5a8c48f8a3f4eb7b" - integrity sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ== +webpack-merge@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.5.tgz#2be31e846c20767d1bef56bdca64c328a681190a" + integrity sha512-sVcM+MMJv6DO0C0GLLltx8mUlGMKXE0zBsuMqZ9jz2X9gsekALw6Rs0cAfTWc97VuWS6NpVUa78959zANnMMLQ== dependencies: lodash "^4.17.5" @@ -12525,10 +13578,10 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.26.1: - version "4.26.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.26.1.tgz#ff3a9283d363c07b3494dfa702d08f4f2ef6cb39" - integrity sha512-i2oOvEvuvLLSuSCkdVrknaxAhtUZ9g+nLSoHCWV0gDzqGX2DXaCrMmMUpbRsTSSLrUqAI56PoEiyMUZIZ1msug== +webpack@^4.27.1: + version "4.27.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.27.1.tgz#5f2e2db446d2266376fa15d7d2277a1a9c2e12bb" + integrity sha512-WArHiLvHrlfyRM8i7f+2SFbr/XbQ0bXqTkPF8JpHOzub5482Y3wx7rEO8stuLGOKOgZJcqcisLhD7LrM/+fVMw== dependencies: "@webassemblyjs/ast" "1.7.11" "@webassemblyjs/helper-module-context" "1.7.11" @@ -12611,7 +13664,7 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -widest-line@^2.0.0: +widest-line@^2.0.0, widest-line@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== @@ -12682,9 +13735,9 @@ winston@^3.1.0: winston-transport "^4.2.0" wkx@^0.4.1: - version "0.4.5" - resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.5.tgz#a85e15a6e69d1bfaec2f3c523be3dfa40ab861d0" - integrity sha512-01dloEcJZAJabLO5XdcRgqdKpmnxS0zIT02LhkdWOZX2Zs2tPM6hlZ4XG9tWaWur1Qd1OO4kJxUbe2+5BofvnA== + version "0.4.6" + resolved "https://registry.yarnpkg.com/wkx/-/wkx-0.4.6.tgz#228ab592e6457382ea6fb79fc825058d07fce523" + integrity sha512-LHxXlzRCYQXA9ZHgs8r7Gafh0gVOE8o3QmudM1PIkOdkXXjW7Thcl+gb2P2dRuKgW8cqkitCRZkkjtmWzpHi7A== dependencies: "@types/node" "*" @@ -12721,6 +13774,15 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" +wrap-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131" + integrity sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg== + dependencies: + ansi-styles "^3.2.0" + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrappy@1, wrappy@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -12772,13 +13834,6 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= - dependencies: - mkdirp "^0.5.1" - ws@^5.2.0: version "5.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" @@ -12826,10 +13881,10 @@ xregexp@2.0.0: resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= -xstate@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.2.1.tgz#d3a8aa304a22117a741b94401b8632cd75ec902f" - integrity sha512-dQEHU/78tUWQd43EMm+17xdM9xVqivSYvNh2++5CY0yST8FcBSl0cyfwrL6tF2MZBqpbuuNLWYc+Ib1bG/jlxQ== +xstate@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.2.2.tgz#66fe7a61dd9f40f6460a6dced7a92bfe3b402241" + integrity sha512-EDYvtdUyGX6bbjQ7vs+GQyc6OjXg+5ovLdm9zKMTV0uRpR1WbDywFPRZj4LWzUvkdFJJr3kHx3r5pkpzrWLg0Q== xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" @@ -12846,11 +13901,23 @@ y18n@^3.2.0, y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + yallist@^3.0.0, yallist@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== +yargs-parser@10.x, yargs-parser@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" @@ -12915,10 +13982,10 @@ yargs@^3.19.0: window-size "^0.1.4" y18n "^3.2.0" -zen-observable-ts@^0.8.11: - version "0.8.11" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.11.tgz#d54a27cd17dc4b4bb6bd008e5c096af7fcb068a9" - integrity sha512-8bs7rgGV4kz5iTb9isudkuQjtWwPnQ8lXq6/T76vrepYZVMsDEv6BXaEA+DHdJSK3KVLduagi9jSpSAJ5NgKHw== +zen-observable-ts@^0.8.13: + version "0.8.13" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.13.tgz#ae1fd77c84ef95510188b1f8bca579d7a5448fc2" + integrity sha512-WDb8SM0tHCb6c0l1k60qXWlm1ok3zN9U4VkLdnBKQwIYwUoB9psH7LIFgR+JVCCMmBxUgOjskIid8/N02k/2Bg== dependencies: zen-observable "^0.8.0"